diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-05-20 09:47:09 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-06-07 11:15:42 +0000 |
commit | 189d4fd8fad9e3c776873be51938cd31a42b6177 (patch) | |
tree | 6497caeff5e383937996768766ab3bb2081a40b2 /chromium/components/autofill | |
parent | 8bc75099d364490b22f43a7ce366b366c08f4164 (diff) | |
download | qtwebengine-chromium-189d4fd8fad9e3c776873be51938cd31a42b6177.tar.gz |
BASELINE: Update Chromium to 90.0.4430.221
Change-Id: Iff4d9d18d2fcf1a576f3b1f453010f744a232920
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/autofill')
328 files changed, 14492 insertions, 8604 deletions
diff --git a/chromium/components/autofill/DIR_METADATA b/chromium/components/autofill/DIR_METADATA new file mode 100644 index 00000000000..3994ddc84af --- /dev/null +++ b/chromium/components/autofill/DIR_METADATA @@ -0,0 +1,4 @@ +monorail { + component: "UI>Browser>Autofill" +} +team_email: "chrome-autofill@google.com" diff --git a/chromium/components/autofill/OWNERS b/chromium/components/autofill/OWNERS index ed9d92879d4..d2870efd3f4 100644 --- a/chromium/components/autofill/OWNERS +++ b/chromium/components/autofill/OWNERS @@ -10,6 +10,3 @@ mahmadi@chromium.org rogerm@chromium.org sebsg@chromium.org tmartino@chromium.org - -# COMPONENT: UI>Browser>Autofill -# TEAM: chrome-autofill@google.com diff --git a/chromium/components/autofill/android/BUILD.gn b/chromium/components/autofill/android/BUILD.gn index 9f15164b1a8..2234539df90 100644 --- a/chromium/components/autofill/android/BUILD.gn +++ b/chromium/components/autofill/android/BUILD.gn @@ -36,14 +36,15 @@ java_cpp_enum("autofill_core_browser_java_enums") { ] } -android_library("autofill_java") { +android_library("full_autofill_java") { deps = [ ":autofill_java_resources", + ":payments_autofill_java", "//base:base_java", "//content/public/android:content_java", - "//third_party/android_deps:androidx_annotation_annotation_java", - "//third_party/android_deps:androidx_appcompat_appcompat_resources_java", - "//third_party/android_deps:androidx_core_core_java", + "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_appcompat_appcompat_resources_java", + "//third_party/androidx:androidx_core_core_java", "//ui/android:ui_no_recycler_view_java", ] sources = [ @@ -52,9 +53,24 @@ android_library("autofill_java") { "java/src/org/chromium/components/autofill/AutofillDropdownFooter.java", "java/src/org/chromium/components/autofill/AutofillPopup.java", "java/src/org/chromium/components/autofill/AutofillSuggestion.java", - "java/src/org/chromium/components/autofill/Completable.java", - "java/src/org/chromium/components/autofill/EditableOption.java", ] srcjar_deps = [ ":autofill_core_browser_java_enums" ] resources_package = "org.chromium.components.autofill" } + +# A library containing the minimal deps for payments, so that ui_java_resources +# doesn't have to be pulled in. +android_library("payments_autofill_java") { + sources = [ + "java/src/org/chromium/components/autofill/Completable.java", + "java/src/org/chromium/components/autofill/EditableOption.java", + ] + deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ] +} + +java_group("autofill_java") { + deps = [ + ":full_autofill_java", + ":payments_autofill_java", + ] +} diff --git a/chromium/components/autofill/android/provider/BUILD.gn b/chromium/components/autofill/android/provider/BUILD.gn index bff1710270d..1050f6ad624 100644 --- a/chromium/components/autofill/android/provider/BUILD.gn +++ b/chromium/components/autofill/android/provider/BUILD.gn @@ -5,7 +5,17 @@ import("//build/config/android/rules.gni") import("//build/config/locales.gni") +android_aidl("autofill_aidl") { + import_include = [ "java/src" ] + sources = [ + "java/src/org/chromium/components/autofill_public/IAutofillHintsService.aidl", + "java/src/org/chromium/components/autofill_public/IViewTypeCallback.aidl", + "java/src/org/chromium/components/autofill_public/ViewType.aidl", + ] +} + android_library("java") { + srcjar_deps = [ ":autofill_aidl" ] deps = [ "//base:base_java", "//base:jni_java", @@ -13,17 +23,19 @@ android_library("java") { "//components/autofill/core/common/mojom:mojo_types_java", "//components/version_info/android:version_constants_java", "//content/public/android:content_java", - "//third_party/android_deps:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_annotation_annotation_java", "//ui/android:ui_no_recycler_view_java", ] annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] sources = [ "java/src/org/chromium/components/autofill/AutofillActionModeCallback.java", + "java/src/org/chromium/components/autofill/AutofillHintsService.java", "java/src/org/chromium/components/autofill/AutofillManagerWrapper.java", "java/src/org/chromium/components/autofill/AutofillProvider.java", "java/src/org/chromium/components/autofill/AutofillProviderUMA.java", "java/src/org/chromium/components/autofill/FormData.java", "java/src/org/chromium/components/autofill/FormFieldData.java", + "java/src/org/chromium/components/autofill_public/ViewType.java", ] } diff --git a/chromium/components/autofill/android/provider/DEPS b/chromium/components/autofill/android/provider/DEPS new file mode 100644 index 00000000000..1bc851ebbbd --- /dev/null +++ b/chromium/components/autofill/android/provider/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+components/autofill/content", +] diff --git a/chromium/components/autofill/android/provider/autofill_provider_android.cc b/chromium/components/autofill/android/provider/autofill_provider_android.cc index 157142cde47..652a4fe4ce0 100644 --- a/chromium/components/autofill/android/provider/autofill_provider_android.cc +++ b/chromium/components/autofill/android/provider/autofill_provider_android.cc @@ -9,11 +9,13 @@ #include "base/android/jni_android.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" +#include "base/feature_list.h" #include "components/autofill/android/provider/form_data_android.h" #include "components/autofill/android/provider/jni_headers/AutofillProvider_jni.h" #include "components/autofill/core/browser/autofill_driver.h" #include "components/autofill/core/browser/autofill_handler_proxy.h" #include "components/autofill/core/common/autofill_constants.h" +#include "components/autofill/core/common/autofill_features.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" #include "ui/android/window_android.h" @@ -34,6 +36,12 @@ namespace autofill { using mojom::SubmissionSource; +static jboolean JNI_AutofillProvider_IsQueryServerFieldTypesEnabled( + JNIEnv* env) { + return base::FeatureList::IsEnabled( + features::kAndroidAutofillQueryServerFieldTypes); +} + AutofillProviderAndroid::AutofillProviderAndroid( const JavaRef<jobject>& jcaller, content::WebContents* web_contents) @@ -151,7 +159,7 @@ void AutofillProviderAndroid::MaybeStartNewSession( Java_AutofillProvider_startAutofillSession( env, obj, form_obj, index, transformed_bounding.x(), transformed_bounding.y(), transformed_bounding.width(), - transformed_bounding.height()); + transformed_bounding.height(), handler->has_server_prediction()); } void AutofillProviderAndroid::OnAutofillAvailable(JNIEnv* env, @@ -341,8 +349,7 @@ void AutofillProviderAndroid::OnDidFillAutofillFormData( } void AutofillProviderAndroid::OnFormsSeen(AutofillHandlerProxy* handler, - const std::vector<FormData>& forms, - const base::TimeTicks timestamp) {} + const std::vector<FormData>& forms) {} void AutofillProviderAndroid::OnHidePopup(AutofillHandlerProxy* handler) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -356,6 +363,45 @@ void AutofillProviderAndroid::OnHidePopup(AutofillHandlerProxy* handler) { } } +void AutofillProviderAndroid::OnServerPredictionsAvailable( + AutofillHandlerProxy* handler) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (handler != handler_.get() || !form_.get()) + return; + + if (auto* form_structure = handler_->FindCachedFormByRendererId( + form_->form().unique_renderer_id)) { + form_->UpdateFieldTypes(*form_structure); + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); + if (obj.is_null()) + return; + + Java_AutofillProvider_onQueryDone(env, obj, /*success=*/true); + } +} + +void AutofillProviderAndroid::OnServerQueryRequestError( + AutofillHandlerProxy* handler, + FormSignature form_signature) { + if (!IsCurrentlyLinkedHandler(handler) || !form_.get()) + return; + + if (auto* form_structure = handler_->FindCachedFormByRendererId( + form_->form().unique_renderer_id)) { + if (form_structure->form_signature() != form_signature) + return; + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); + if (obj.is_null()) + return; + + Java_AutofillProvider_onQueryDone(env, obj, /*success=*/false); + } +} + void AutofillProviderAndroid::Reset(AutofillHandlerProxy* handler) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (handler == handler_.get()) { diff --git a/chromium/components/autofill/android/provider/autofill_provider_android.h b/chromium/components/autofill/android/provider/autofill_provider_android.h index 618517ca585..bd21af383a0 100644 --- a/chromium/components/autofill/android/provider/autofill_provider_android.h +++ b/chromium/components/autofill/android/provider/autofill_provider_android.h @@ -66,9 +66,11 @@ class AutofillProviderAndroid : public AutofillProvider { const FormData& form, base::TimeTicks timestamp) override; void OnFormsSeen(AutofillHandlerProxy* handler, - const std::vector<FormData>& forms, - const base::TimeTicks timestamp) override; + const std::vector<FormData>& forms) override; void OnHidePopup(AutofillHandlerProxy* handler) override; + void OnServerPredictionsAvailable(AutofillHandlerProxy* handler) override; + void OnServerQueryRequestError(AutofillHandlerProxy* handler, + FormSignature form_signature) override; void Reset(AutofillHandlerProxy* handler) override; diff --git a/chromium/components/autofill/android/provider/form_data_android.cc b/chromium/components/autofill/android/provider/form_data_android.cc index 7f24f8967c5..1d74c49fd32 100644 --- a/chromium/components/autofill/android/provider/form_data_android.cc +++ b/chromium/components/autofill/android/provider/form_data_android.cc @@ -40,7 +40,7 @@ ScopedJavaLocalRef<jobject> FormDataAndroid::GetJavaPeer( new FormFieldDataAndroid(&form_.fields[i]))); } if (form_structure) - ApplyHeuristicFieldType(*form_structure); + UpdateFieldTypes(*form_structure); ScopedJavaLocalRef<jstring> jname = ConvertUTF16ToJavaString(env, form_.name); ScopedJavaLocalRef<jstring> jhost = @@ -96,14 +96,20 @@ bool FormDataAndroid::SimilarFormAs(const FormData& form) { return form_.SimilarFormAs(form); } -void FormDataAndroid::ApplyHeuristicFieldType( - const FormStructure& form_structure) { +void FormDataAndroid::UpdateFieldTypes(const FormStructure& form_structure) { DCHECK(form_structure.field_count() == fields_.size()); auto form_field_data_android = fields_.begin(); for (const auto& autofill_field : form_structure) { DCHECK(form_field_data_android->get()->SimilarFieldAs(*autofill_field)); - form_field_data_android->get()->set_heuristic_type( - AutofillType(autofill_field->heuristic_type())); + std::vector<AutofillType> server_predictions; + for (const auto& prediction : autofill_field->server_predictions()) { + server_predictions.emplace_back( + static_cast<ServerFieldType>(prediction.type())); + } + form_field_data_android->get()->UpdateAutofillTypes( + AutofillType(autofill_field->heuristic_type()), + AutofillType(autofill_field->server_type()), + autofill_field->ComputedType(), server_predictions); if (++form_field_data_android == fields_.end()) break; } diff --git a/chromium/components/autofill/android/provider/form_data_android.h b/chromium/components/autofill/android/provider/form_data_android.h index 96002f62c94..e7c39495dc3 100644 --- a/chromium/components/autofill/android/provider/form_data_android.h +++ b/chromium/components/autofill/android/provider/form_data_android.h @@ -52,7 +52,8 @@ class FormDataAndroid { // |value|. void OnFormFieldDidChange(size_t index, const base::string16& value); - void ApplyHeuristicFieldType(const FormStructure& form); + // Updates the field types from the |form|. + void UpdateFieldTypes(const FormStructure& form); const FormData& form() { return form_; } diff --git a/chromium/components/autofill/android/provider/form_field_data_android.cc b/chromium/components/autofill/android/provider/form_field_data_android.cc index 943ff3cf116..b40701a8e56 100644 --- a/chromium/components/autofill/android/provider/form_field_data_android.cc +++ b/chromium/components/autofill/android/provider/form_field_data_android.cc @@ -21,9 +21,28 @@ using base::android::ToJavaArrayOfStrings; namespace autofill { +namespace { +base::android::ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfPredictionString( + JNIEnv* env, + const std::vector<AutofillType>& server_predictions) { + if (!server_predictions.empty()) { + std::vector<std::string> server_prediction_array; + server_prediction_array.reserve(server_predictions.size()); + for (const auto& p : server_predictions) { + server_prediction_array.emplace_back(p.ToString()); + } + return ToJavaArrayOfStrings(env, server_prediction_array); + } + return nullptr; +} + +} // namespace + FormFieldDataAndroid::FormFieldDataAndroid(FormFieldData* field) : heuristic_type_(AutofillType(UNKNOWN_TYPE)), field_ptr_(field) {} +FormFieldDataAndroid::~FormFieldDataAndroid() = default; + ScopedJavaLocalRef<jobject> FormFieldDataAndroid::GetJavaPeer() { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); @@ -47,9 +66,17 @@ ScopedJavaLocalRef<jobject> FormFieldDataAndroid::GetJavaPeer() { ScopedJavaLocalRef<jobjectArray> joption_contents = ToJavaArrayOfStrings(env, field_ptr_->option_contents); ScopedJavaLocalRef<jstring> jheuristic_type; - if (!heuristic_type_.IsUnknown()) + if (!heuristic_type_.IsUnknown()) { jheuristic_type = ConvertUTF8ToJavaString(env, heuristic_type_.ToString()); + } + ScopedJavaLocalRef<jstring> jserver_type = + ConvertUTF8ToJavaString(env, server_type_.ToString()); + ScopedJavaLocalRef<jstring> jcomputed_type = + ConvertUTF8ToJavaString(env, computed_type_.ToString()); + ScopedJavaLocalRef<jobjectArray> jserver_predictions = + ToJavaArrayOfPredictionString(env, server_predictions_); + ScopedJavaLocalRef<jobjectArray> jdatalist_values = ToJavaArrayOfStrings(env, field_ptr_->datalist_values); ScopedJavaLocalRef<jobjectArray> jdatalist_labels = @@ -60,7 +87,8 @@ ScopedJavaLocalRef<jobject> FormFieldDataAndroid::GetJavaPeer() { field_ptr_->should_autocomplete, jplaceholder, jtype, jid, joption_values, joption_contents, IsCheckable(field_ptr_->check_status), IsChecked(field_ptr_->check_status), field_ptr_->max_length, - jheuristic_type, field_ptr_->bounds.x(), field_ptr_->bounds.y(), + jheuristic_type, jserver_type, jcomputed_type, jserver_predictions, + field_ptr_->bounds.x(), field_ptr_->bounds.y(), field_ptr_->bounds.right(), field_ptr_->bounds.bottom(), jdatalist_values, jdatalist_labels, field_ptr_->IsVisible()); java_ref_ = JavaObjectWeakGlobalRef(env, obj); @@ -103,4 +131,32 @@ bool FormFieldDataAndroid::SimilarFieldAs(const FormFieldData& field) const { return field_ptr_->SimilarFieldAs(field); } +void FormFieldDataAndroid::UpdateAutofillTypes( + const AutofillType& heuristic_type, + const AutofillType& server_type, + const AutofillType& computed_type, + const std::vector<AutofillType>& server_predictions) { + heuristic_type_ = heuristic_type; + server_type_ = server_type; + computed_type_ = computed_type; + server_predictions_ = server_predictions; + + // Java peer isn't available when this object is instantiated, update to + // Java peer if the prediction arrives later. + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); + if (obj.is_null()) + return; + + ScopedJavaLocalRef<jstring> jserver_type = + ConvertUTF8ToJavaString(env, server_type_.ToString()); + ScopedJavaLocalRef<jstring> jcomputed_type = + ConvertUTF8ToJavaString(env, computed_type_.ToString()); + ScopedJavaLocalRef<jobjectArray> jserver_predictions = + ToJavaArrayOfPredictionString(env, server_predictions_); + + Java_FormFieldData_updateFieldTypes(env, obj, jserver_type, jcomputed_type, + jserver_predictions); +} + } // namespace autofill diff --git a/chromium/components/autofill/android/provider/form_field_data_android.h b/chromium/components/autofill/android/provider/form_field_data_android.h index a12ad979bfe..ff0879b48b9 100644 --- a/chromium/components/autofill/android/provider/form_field_data_android.h +++ b/chromium/components/autofill/android/provider/form_field_data_android.h @@ -16,20 +16,24 @@ namespace autofill { // autofill::FormFieldData available in Java. class FormFieldDataAndroid { public: - FormFieldDataAndroid(FormFieldData* field); - virtual ~FormFieldDataAndroid() {} + explicit FormFieldDataAndroid(FormFieldData* field); + virtual ~FormFieldDataAndroid(); base::android::ScopedJavaLocalRef<jobject> GetJavaPeer(); void GetValue(); void OnFormFieldDidChange(const base::string16& value); bool SimilarFieldAs(const FormFieldData& field) const; - - void set_heuristic_type(const AutofillType& heuristic_type) { - heuristic_type_ = heuristic_type; - } + void UpdateAutofillTypes(const AutofillType& heuristic_type, + const AutofillType& server_type, + const AutofillType& computed_type, + const std::vector<AutofillType>& server_predictions); private: AutofillType heuristic_type_; + AutofillType server_type_; + AutofillType computed_type_; + std::vector<AutofillType> server_predictions_; + // Not owned. FormFieldData* field_ptr_; JavaObjectWeakGlobalRef java_ref_; diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillHintsService.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillHintsService.java new file mode 100644 index 00000000000..14fd7b0e73e --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillHintsService.java @@ -0,0 +1,74 @@ +// Copyright 2020 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. + +package org.chromium.components.autofill; + +import android.os.IBinder; + +import org.chromium.base.Log; +import org.chromium.components.autofill_public.IAutofillHintsService; +import org.chromium.components.autofill_public.IViewTypeCallback; +import org.chromium.components.autofill_public.ViewType; + +import java.util.List; + +/** + * This class is used to talk to autofill service about the view type. + */ +public class AutofillHintsService { + private static final String TAG = "AutofillHintsService"; + + public AutofillHintsService() { + mBinder = new IAutofillHintsService.Stub() { + @Override + public void registerViewTypeCallback(IViewTypeCallback callback) { + mCallback = callback; + if (mUnsentViewTypes != null) { + invokeOnViewTypeAvailable(); + } else if (mQueryFailed != null) { + invokeOnQueryFailed(); + } + } + }; + } + + public IBinder getBinder() { + return mBinder; + } + + public void onViewTypeAvailable(List<ViewType> viewTypes) { + if (mUnsentViewTypes != null) return; + mUnsentViewTypes = viewTypes; + if (mCallback == null) return; + invokeOnViewTypeAvailable(); + } + + public void onQueryFailed() { + if (mQueryFailed != null) return; + mQueryFailed = Boolean.TRUE; + if (mCallback == null) return; + invokeOnQueryFailed(); + } + + private void invokeOnViewTypeAvailable() { + try { + mCallback.onViewTypeAvailable(mUnsentViewTypes); + } catch (Exception e) { + Log.e(TAG, "onViewTypeAvailable ", e); + } + } + + private void invokeOnQueryFailed() { + try { + mCallback.onQueryFailed(); + } catch (Exception e) { + Log.e(TAG, "onQueryFailed ", e); + } + } + + private IAutofillHintsService.Stub mBinder; + private IViewTypeCallback mCallback; + private List<ViewType> mUnsentViewTypes; + private Boolean mQueryFailed; +} diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java index e92fdb19c61..237cf244e98 100644 --- a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillManagerWrapper.java @@ -5,6 +5,7 @@ package org.chromium.components.autofill; import android.annotation.TargetApi; +import android.content.ComponentName; import android.content.Context; import android.graphics.Rect; import android.os.Build; @@ -29,7 +30,8 @@ public class AutofillManagerWrapper { // NOTE: As a result of the above, the tag below still references the name of this class from // when it was originally developed specifically for Android WebView. public static final String TAG = "AwAutofillManager"; - + private static final String AWG_COMPONENT_NAME = + "com.google.android.gms/com.google.android.gms.autofill.service.AutofillService"; /** * The observer of suggestion window. */ @@ -58,17 +60,42 @@ public class AutofillManagerWrapper { private boolean mDestroyed; private boolean mDisabled; private ArrayList<WeakReference<InputUIObserver>> mInputUIObservers; + // Indicates if AwG is the current Android autofill service. + private final boolean mIsAwGCurrentAutofillService; public AutofillManagerWrapper(Context context) { updateLogStat(); if (isLoggable()) log("constructor"); mAutofillManager = context.getSystemService(AutofillManager.class); mDisabled = mAutofillManager == null || !mAutofillManager.isEnabled(); + if (mDisabled) { + mIsAwGCurrentAutofillService = false; if (isLoggable()) log("disabled"); return; } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + ComponentName componentName = null; + try { + componentName = mAutofillManager.getAutofillServiceComponentName(); + } catch (Exception e) { + // Can't catch com.android.internal.util.SyncResultReceiver.TimeoutException, + // because + // - The exception isn't Android API. + // - Different version of Android handle it differently. + // Uses Exception to catch various cases. (refer to crbug.com/1186406) + Log.e(TAG, "getAutofillServiceComponentName", e); + } + if (componentName != null) { + mIsAwGCurrentAutofillService = + AWG_COMPONENT_NAME.equals(componentName.flattenToString()); + } else { + mIsAwGCurrentAutofillService = false; + } + } else { + mIsAwGCurrentAutofillService = false; + } mMonitor = new AutofillInputUIMonitor(this); mAutofillManager.registerCallback(mMonitor); } @@ -142,6 +169,14 @@ public class AutofillManagerWrapper { return mDisabled; } + /** + * Only work for Android P and beyond. Always return false for Android O. + * @return if the Autofill with Google is the current autofill service. + */ + public boolean isAwGCurrentAutofillService() { + return mIsAwGCurrentAutofillService; + } + private boolean checkAndWarnIfDestroyed() { if (mDestroyed) { Log.w(TAG, "Application attempted to call on a destroyed AutofillManagerWrapper", @@ -165,9 +200,13 @@ public class AutofillManagerWrapper { } } - public void notifyNewSessionStarted() { + public void notifyNewSessionStarted(boolean hasServerPrediction) { updateLogStat(); - if (isLoggable()) log("Session starts"); + if (isLoggable()) log("Session starts, has server prediction = " + hasServerPrediction); + } + + public void onQueryDone(boolean success) { + if (isLoggable()) log("Query " + (success ? "succeed" : "failed")); } /** diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProvider.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProvider.java index 226f0dc55a7..726a17f0784 100644 --- a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProvider.java +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProvider.java @@ -28,6 +28,7 @@ import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; import org.chromium.base.annotations.VerifiesOnO; import org.chromium.base.metrics.ScopedSysTraceEvent; +import org.chromium.components.autofill_public.ViewType; import org.chromium.components.version_info.VersionConstants; import org.chromium.content_public.browser.RenderCoordinates; import org.chromium.content_public.browser.WebContents; @@ -37,6 +38,8 @@ import org.chromium.ui.base.ViewAndroidDelegate; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.display.DisplayAndroid; +import java.util.ArrayList; + /** * This class works with Android autofill service to fill web form, it doesn't use chrome's * autofill service or suggestion UI. All methods are supposed to be called in UI thread. @@ -58,6 +61,11 @@ import org.chromium.ui.display.DisplayAndroid; @JNINamespace("autofill") public class AutofillProvider { private static final String TAG = "AutofillProvider"; + + // This member is initialize at first use. Not access it directly, always through + // isQueryServerFieldTypesEnabled(). + private static Boolean sIsQueryServerFieldTypesEnabled; + private static class FocusField { public final short fieldIndex; public final Rect absBound; @@ -81,11 +89,19 @@ public class AutofillProvider { public final int sessionId; private FormData mFormData; private FocusField mFocusField; - - public AutofillRequest(FormData formData, FocusField focus) { + private AutofillHintsService mAutofillHintsService; + + /** + * @param formData the form of the AutofillRequest. + * @param focus the current focused field. + * @param hasServerPrediction whether the server type of formData is valid. + */ + public AutofillRequest(FormData formData, FocusField focus, boolean hasServerPrediction) { sessionId = getNextClientId(); mFormData = formData; mFocusField = focus; + // Don't need to create binder object if server prediction is already available. + if (!hasServerPrediction) mAutofillHintsService = new AutofillHintsService(); } public void fillViewStructure(ViewStructure structure) { @@ -99,6 +115,7 @@ public class AutofillProvider { ViewStructure child = structure.newChild(index++); int virtualId = toVirtualId(sessionId, fieldIndex++); child.setAutofillId(structure.getAutofillId(), virtualId); + field.setAutofillId(child.getAutofillId()); if (field.mAutocompleteAttr != null && !field.mAutocompleteAttr.isEmpty()) { child.setAutofillHints(field.mAutocompleteAttr.split(" +")); } @@ -118,6 +135,16 @@ public class AutofillProvider { .addAttribute("ua-autofill-hints", field.mHeuristicType) .addAttribute("id", field.mId); + if (isQueryServerFieldTypesEnabled()) { + builder.addAttribute("crowdsourcing-autofill-hints", field.getServerType()); + builder.addAttribute("computed-autofill-hints", field.getComputedType()); + // Compose multiple predictions to a string separated by ','. + String[] predictions = field.getServerPredictions(); + if (predictions != null && predictions.length > 0) { + builder.addAttribute("crowdsourcing-predictions-autofill-hints", + String.join(",", predictions)); + } + } switch (field.getControlType()) { case FormFieldData.ControlType.LIST: child.setAutofillType(View.AUTOFILL_TYPE_LIST); @@ -249,6 +276,24 @@ public class AutofillProvider { private static int toVirtualId(int clientId, short index) { return (clientId << 16) | index; } + + public AutofillHintsService getAutofillHintsService() { + return mAutofillHintsService; + } + + public void onQueryDone(boolean success) { + if (mAutofillHintsService == null) return; + if (success) { + ArrayList<ViewType> viewTypes = new ArrayList<ViewType>(); + for (FormFieldData field : mFormData.mFields) { + viewTypes.add(new ViewType(field.getAutofillId(), field.getServerType(), + field.getComputedType(), field.getServerPredictions())); + } + mAutofillHintsService.onViewTypeAvailable(viewTypes); + } else { + mAutofillHintsService.onQueryFailed(); + } + } } private final String mProviderName; @@ -279,7 +324,7 @@ public class AutofillProvider { assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; mAutofillManager = manager; mContainerView = containerView; - mAutofillUMA = new AutofillProviderUMA(context); + mAutofillUMA = new AutofillProviderUMA(context, manager.isAwGCurrentAutofillService()); mInputUIObserver = new AutofillManagerWrapper.InputUIObserver() { @Override public void onInputUIShown() { @@ -321,6 +366,13 @@ public class AutofillProvider { bundle.putCharSequence("VIRTUAL_STRUCTURE_PROVIDER_NAME", mProviderName); bundle.putCharSequence( "VIRTUAL_STRUCTURE_PROVIDER_VERSION", VersionConstants.PRODUCT_VERSION); + + if (isQueryServerFieldTypesEnabled()) { + AutofillHintsService autofillHintsService = mRequest.getAutofillHintsService(); + if (autofillHintsService != null) { + bundle.putBinder("AUTOFILL_HINTS_SERVICE", autofillHintsService.getBinder()); + } + } } mRequest.fillViewStructure(structure); if (AutofillManagerWrapper.isLoggable()) { @@ -373,10 +425,11 @@ public class AutofillProvider { * @param y the boundary of focus field. * @param width the boundary of focus field. * @param height the boundary of focus field. + * @param hasServerPrediction whether the server prediction arrived. */ @CalledByNative - public void startAutofillSession( - FormData formData, int focus, float x, float y, float width, float height) { + public void startAutofillSession(FormData formData, int focus, float x, float y, float width, + float height, boolean hasServerPrediction) { // Check focusField inside short value? // Autofill Manager might have session that wasn't started by AutofillProvider, // we just always cancel existing session here. @@ -387,13 +440,17 @@ public class AutofillProvider { Rect absBound = transformToWindowBounds(new RectF(x, y, x + width, y + height)); if (mRequest != null) notifyViewExitBeforeDestroyRequest(); transformFormFieldToContainViewCoordinates(formData); - mRequest = new AutofillRequest(formData, new FocusField((short) focus, absBound)); + mRequest = new AutofillRequest( + formData, new FocusField((short) focus, absBound), hasServerPrediction); int virtualId = mRequest.getVirtualId((short) focus); notifyVirtualViewEntered(mContainerView, virtualId, absBound); mAutofillUMA.onSessionStarted(mAutofillManager.isDisabled()); + if (hasServerPrediction) { + mAutofillUMA.onServerTypeAvailable(formData, /*afterSessionStarted=*/false); + } mAutofillTriggeredTimeMillis = System.currentTimeMillis(); - mAutofillManager.notifyNewSessionStarted(); + mAutofillManager.notifyNewSessionStarted(hasServerPrediction); } /** @@ -704,6 +761,22 @@ public class AutofillProvider { forceNotifyFormValues(); } + @CalledByNative + private void onQueryDone(boolean success) { + mRequest.onQueryDone(success); + mAutofillUMA.onServerTypeAvailable( + success ? mRequest.mFormData : null, /*afterSessionStarted*/ true); + mAutofillManager.onQueryDone(success); + } + + public static boolean isQueryServerFieldTypesEnabled() { + if (sIsQueryServerFieldTypesEnabled == null) { + sIsQueryServerFieldTypesEnabled = + AutofillProviderJni.get().isQueryServerFieldTypesEnabled(); + } + return sIsQueryServerFieldTypesEnabled; + } + private void forceNotifyFormValues() { if (mRequest == null) return; for (int i = 0; i < mRequest.getFieldCount(); ++i) { @@ -795,5 +868,7 @@ public class AutofillProvider { void setAnchorViewRect(long nativeAutofillProviderAndroid, AutofillProvider caller, View anchorView, float x, float y, float width, float height); + + boolean isQueryServerFieldTypesEnabled(); } } diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProviderUMA.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProviderUMA.java index 394cd849c95..3f4ea071c87 100644 --- a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProviderUMA.java +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/AutofillProviderUMA.java @@ -5,6 +5,7 @@ package org.chromium.components.autofill; import android.content.Context; +import android.os.Build; import org.chromium.autofill.mojom.SubmissionSource; import org.chromium.base.ContextUtils; @@ -26,6 +27,10 @@ public class AutofillProviderUMA { public static final String UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT = "Autofill.WebView.CreatedByActivityContext"; + // Records whether the current autofill service is AwG. + public static final String UMA_AUTOFILL_AWG_IS_CURRENT_SERVICE = + "Autofill.WebView.AwGIsCurrentService"; + // Records what happened in an autofill session. public static final String UMA_AUTOFILL_AUTOFILL_SESSION = "Autofill.WebView.AutofillSession"; // The possible value of UMA_AUTOFILL_AUTOFILL_SESSION. @@ -45,6 +50,25 @@ public class AutofillProviderUMA { public static final int USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED = 13; public static final int AUTOFILL_SESSION_HISTOGRAM_COUNT = 14; + // The possible values for the server prediction availability. + public static final String UMA_AUTOFILL_SERVER_PREDICTION_AVAILABILITY = + "Autofill.WebView.ServerPredicton.PredictionAvailability"; + public static final int SERVER_PREDICTION_NOT_AVAILABLE = 0; + public static final int SERVER_PREDICTION_AVAILABLE_ON_SESSION_STARTS = 1; + public static final int SERVER_PREDICTION_AVAILABLE_AFTER_SESSION_STARTS = 2; + public static final int SERVER_PREDICTION_AVAILABLE_COUNT = 3; + + // The possible values for the AwG suggestion availability. + public static final String UMA_AUTOFILL_AWG_SUGGSTION_AVAILABILITY = + "Autofill.WebView.ServerPrediction.AwGSuggestionAvailability"; + public static final int AWG_NO_SUGGESTION = 0; + public static final int AWG_HAS_SUGGESTION_NO_AUTOFILL = 1; + public static final int AWG_HAS_SUGGESTION_AUTOFILLED = 2; + public static final int AWG_SUGGSTION_AVAILABLE_COUNT = 3; + + public static final String UMA_AUTOFILL_VALID_SERVER_PREDICTION = + "Autofill.WebView.ServerPredicton.HasValidServerPrediction"; + // Records whether user changed autofilled field if user ever changed the form. The action isn't // recorded if user didn't change form at all. public static final String UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD = @@ -120,6 +144,31 @@ public class AutofillProviderUMA { if (mSuggestionTimeMillis != null) { recordTimesHistogram(UMA_AUTOFILL_SUGGESTION_TIME, mSuggestionTimeMillis); } + if (!mServerPredictionAvailable && AutofillProvider.isQueryServerFieldTypesEnabled()) { + RecordHistogram.recordEnumeratedHistogram( + UMA_AUTOFILL_SERVER_PREDICTION_AVAILABILITY, + SERVER_PREDICTION_NOT_AVAILABLE, SERVER_PREDICTION_AVAILABLE_COUNT); + } + } + + public void onServerTypeAvailable(FormData formData, boolean afterSessionStarted) { + if (!AutofillProvider.isQueryServerFieldTypesEnabled()) return; + mServerPredictionAvailable = true; + RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_SERVER_PREDICTION_AVAILABILITY, + afterSessionStarted ? SERVER_PREDICTION_AVAILABLE_AFTER_SESSION_STARTS + : SERVER_PREDICTION_AVAILABLE_ON_SESSION_STARTS, + SERVER_PREDICTION_AVAILABLE_COUNT); + if (formData != null) { + boolean hasValidServerData = false; + for (FormFieldData fieldData : formData.mFields) { + if (!fieldData.getServerType().equals("NO_SERVER_DATA")) { + hasValidServerData = true; + break; + } + } + RecordHistogram.recordBooleanHistogram( + UMA_AUTOFILL_VALID_SERVER_PREDICTION, hasValidServerData); + } } private int toUMAAutofillSessionValue() { @@ -174,19 +223,62 @@ public class AutofillProviderUMA { private int mState; private Boolean mUserChangedAutofilledField; + + // Indicates whether the server prediction arrives. + private boolean mServerPredictionAvailable; + } + + /** + * The class to record Autofill.WebView.ServerPrediction.AwGSuggestion, is only instantiated + * when the Android platform AutofillServcie is AwG, This will give us more actual result in + * A/B experiment while only AwG supports the server prediction. + */ + private static class ServerPredictionRecorder { + private boolean mHasSuggestions; + private boolean mAutofilled; + private boolean mRecorded; + + public void onSuggestionDisplayed() { + mHasSuggestions = true; + } + + public void onAutofill() { + mAutofilled = true; + } + + public void recordHistograms() { + if (mRecorded) return; + mRecorded = true; + int sample = AWG_NO_SUGGESTION; + if (mHasSuggestions) { + sample = mAutofilled ? AWG_HAS_SUGGESTION_AUTOFILLED + : AWG_HAS_SUGGESTION_NO_AUTOFILL; + } + RecordHistogram.recordEnumeratedHistogram( + UMA_AUTOFILL_AWG_SUGGSTION_AVAILABILITY, sample, AWG_SUGGSTION_AVAILABLE_COUNT); + } } private SessionRecorder mRecorder; private Boolean mAutofillDisabled; - public AutofillProviderUMA(Context context) { + private final boolean mIsAwGCurrentAutofillService; + private ServerPredictionRecorder mServerPredictionRecorder; + + public AutofillProviderUMA(Context context, boolean isAwGCurrentAutofillService) { RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT, ContextUtils.activityFromContext(context) != null); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + RecordHistogram.recordBooleanHistogram( + UMA_AUTOFILL_AWG_IS_CURRENT_SERVICE, isAwGCurrentAutofillService); + } + mIsAwGCurrentAutofillService = isAwGCurrentAutofillService; } public void onFormSubmitted(int submissionSource) { if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_SUBMITTED); recordSession(); + if (mServerPredictionRecorder != null) mServerPredictionRecorder.recordHistograms(); // We record this no matter autofill service is disabled or not. RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_SUBMISSION_SOURCE, toUMASubmissionSource(submissionSource), SUBMISSION_SOURCE_HISTOGRAM_COUNT); @@ -201,6 +293,9 @@ public class AutofillProviderUMA { if (mRecorder != null) recordSession(); mRecorder = new SessionRecorder(); + if (mIsAwGCurrentAutofillService) { + mServerPredictionRecorder = new ServerPredictionRecorder(); + } } public void onVirtualStructureProvided() { @@ -212,10 +307,12 @@ public class AutofillProviderUMA { mRecorder.record(SessionRecorder.EVENT_SUGGESTION_DISPLAYED); mRecorder.setSuggestionTimeMillis(suggestionTimeMillis); } + if (mServerPredictionRecorder != null) mServerPredictionRecorder.onSuggestionDisplayed(); } public void onAutofill() { if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_AUTOFILLED); + if (mServerPredictionRecorder != null) mServerPredictionRecorder.onAutofill(); } public void onUserChangeFieldValue(boolean isPreviouslyAutofilled) { @@ -227,6 +324,17 @@ public class AutofillProviderUMA { } } + /** + * Invoked when the server query was done or has arrived when the autofill sension starts. + * + * @param formData the form of the current session, is null if the query failed. + * @param afterSessionStarted true if the server type predication arrive after the session + * starts. + */ + public void onServerTypeAvailable(FormData formData, boolean afterSessionStarted) { + mRecorder.onServerTypeAvailable(formData, afterSessionStarted); + } + private void recordSession() { if (mAutofillDisabled != null && !mAutofillDisabled.booleanValue() && mRecorder != null) { mRecorder.recordHistogram(); diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormFieldData.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormFieldData.java index 9175bd42752..77f65ef7324 100644 --- a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormFieldData.java +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill/FormFieldData.java @@ -5,6 +5,7 @@ package org.chromium.components.autofill; import android.graphics.RectF; +import android.view.autofill.AutofillId; import androidx.annotation.IntDef; import androidx.annotation.VisibleForTesting; @@ -63,10 +64,18 @@ public class FormFieldData { // Indicates whether this fields was autofilled, but changed by user. private boolean mPreviouslyAutofilled; + // Provides the field type along with mHeuristicType, but could be changed + // after the object instantiated. + private String mServerType; + private String mComputedType; + private String[] mServerPredictions; + private AutofillId mAutofillId; + private FormFieldData(String name, String label, String value, String autocompleteAttr, boolean shouldAutocomplete, String placeholder, String type, String id, String[] optionValues, String[] optionContents, boolean isCheckField, boolean isChecked, - int maxLength, String heuristicType, float left, float top, float right, float bottom, + int maxLength, String heuristicType, String serverType, String computedType, + String[] serverPredictions, float left, float top, float right, float bottom, String[] datalistValues, String[] datalistLabels, boolean visible) { mName = name; mLabel = label; @@ -92,6 +101,9 @@ public class FormFieldData { } mMaxLength = maxLength; mHeuristicType = heuristicType; + mServerType = serverType; + mServerPredictions = serverPredictions; + mComputedType = computedType; mBounds = new RectF(left, top, right, bottom); mVisible = visible; } @@ -137,6 +149,26 @@ public class FormFieldData { } @CalledByNative + private void updateFieldTypes( + String serverType, String computedType, String[] serverPredictions) { + mServerType = serverType; + mComputedType = computedType; + mServerPredictions = serverPredictions; + } + + public String getServerType() { + return mServerType; + } + + public String getComputedType() { + return mComputedType; + } + + public String[] getServerPredictions() { + return mServerPredictions; + } + + @CalledByNative public boolean isChecked() { return mIsChecked; } @@ -150,17 +182,25 @@ public class FormFieldData { mAutofilled = autofilled; } + public void setAutofillId(AutofillId id) { + mAutofillId = id; + } + + public AutofillId getAutofillId() { + return mAutofillId; + } + @CalledByNative @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public static FormFieldData createFormFieldData(String name, String label, String value, String autocompleteAttr, boolean shouldAutocomplete, String placeholder, String type, String id, String[] optionValues, String[] optionContents, boolean isCheckField, - boolean isChecked, int maxLength, String heuristicType, float left, float top, - float right, float bottom, String[] datalistValues, String[] datalistLabels, - boolean visible) { + boolean isChecked, int maxLength, String heuristicType, String serverType, + String computedType, String[] serverPredictions, float left, float top, float right, + float bottom, String[] datalistValues, String[] datalistLabels, boolean visible) { return new FormFieldData(name, label, value, autocompleteAttr, shouldAutocomplete, placeholder, type, id, optionValues, optionContents, isCheckField, isChecked, - maxLength, heuristicType, left, top, right, bottom, datalistValues, datalistLabels, - visible); + maxLength, heuristicType, serverType, computedType, serverPredictions, left, top, + right, bottom, datalistValues, datalistLabels, visible); } } diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/IAutofillHintsService.aidl b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/IAutofillHintsService.aidl new file mode 100644 index 00000000000..dd5dd191b39 --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/IAutofillHintsService.aidl @@ -0,0 +1,22 @@ +// Copyright 2020 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. + +package org.chromium.components.autofill_public; + +import android.os.Bundle; + +import org.chromium.components.autofill_public.IViewTypeCallback; + +/** + * Interface to provide the autofill hints that are unable to be supported + * by Android framework. + * + * The autofill service could get the binder from ViewStructure. + * Bundle bundle = viewStructure.getExtras(); + * IBinder binder = bundle.getBinder("AUTOFILL_HINTS_SERVICE"); + */ +interface IAutofillHintsService { + // Register the IViewTypeCallback to get the server prediction type. + void registerViewTypeCallback(IViewTypeCallback callback); +} diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/IViewTypeCallback.aidl b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/IViewTypeCallback.aidl new file mode 100644 index 00000000000..70fa902c55d --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/IViewTypeCallback.aidl @@ -0,0 +1,22 @@ +// Copyright 2020 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. + +package org.chromium.components.autofill_public; + +import android.os.Bundle; + +import org.chromium.components.autofill_public.ViewType; + +/** + * The interface for AutofillHintsService to provide the type of view. + */ +interface IViewTypeCallback { + // Invoked when the query succeeds, though the server might not have the + // prediction of the views. + void onViewTypeAvailable(in List<ViewType> viewTypes); + + // Invoked when the query fails, mostly because of the connection or server + // error. + void onQueryFailed(); +} diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/OWNERS b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/OWNERS new file mode 100644 index 00000000000..8f094e0099e --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/OWNERS @@ -0,0 +1,2 @@ +per-file *.aidl=set noparent +per-file *.aidl=file://ipc/SECURITY_OWNERS diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/README.md b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/README.md new file mode 100644 index 00000000000..7e77e096932 --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/README.md @@ -0,0 +1,53 @@ +# How to integrate the AutofillHintsService + +1. Add all files of this directory to your project. +2. Get the binder in your autofill service as below + + ```java + public void processNode(AssistStructure.ViewNode node) { + Bundle bundle = node.getExtras(); + if (bundle != null) { + IBinder binder = bundle.getBinder("AUTOFILL_HINTS_SERVICE"); + if (binder != null) { + callViewTypeService(binder); + } else { + Log.e("MyAutofillService", "binder is null."); + } + } else { + Log.e("MyAutofillService", "bundle is null."); + } + } + ``` + +3. Register the ViewTypeCallback + + ```java + private void callViewTypeService(IBinder binder) { + IViewTypeService viewTypeService = IViewTypeService.Stub.asInterface(binder); + if (viewTypeService != null) { + try { + if (mViewTypeCallback == null) mViewTypeCallback = new ViewTypeCallback(); + viewTypeService.registerViewTypeCallback(mViewTypeCallback.getBinder()); + Log.d("MyAutofillService", " registerViewTypeCallback "); + } catch (Exception e) { + Log.e("MyAutofillService", " registerViewTypeCallback exception", e); + } + } else { + Log.e("MyAutofillService", "viewTypeService is null."); + } + } + ``` + +4. A list of ViewType will be returned from ViewTypeCallback when they are available. + + ```java + public void onViewTypeAvailable(List<ViewType> viewTypeList) { + for(ViewType viewType : viewTypeList) { + if (viewType.getServerPredictions() ! = null) { + // Uses server predictions if they are available. + } else { + // otherwise, uses viewType.mServerType. + } + } + } + ``` diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/ViewType.aidl b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/ViewType.aidl new file mode 100644 index 00000000000..6a7398d38a4 --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/ViewType.aidl @@ -0,0 +1,7 @@ +// Copyright 2020 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. + +package org.chromium.components.autofill_public; + +parcelable ViewType; diff --git a/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/ViewType.java b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/ViewType.java new file mode 100644 index 00000000000..fe0d9e18e28 --- /dev/null +++ b/chromium/components/autofill/android/provider/java/src/org/chromium/components/autofill_public/ViewType.java @@ -0,0 +1,88 @@ +// Copyright 2020 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. + +package org.chromium.components.autofill_public; + +import android.annotation.TargetApi; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.autofill.AutofillId; + +import org.chromium.base.annotations.VerifiesOnO; + +/** + * This class is used to send the server and computed view type to the autofill service. + * The valid types are listed in the two FieldTypeToStringPiece() functions in + * components/autofill/core/browser/field_types.cc. Note that the list of possibly returned strings + * can and will change in the future. + */ +@TargetApi(Build.VERSION_CODES.O) +@VerifiesOnO +public class ViewType implements Parcelable { + /** + * The AutofillId of the view that types are for. + */ + public final AutofillId mAutofillId; + + /** + * The type from Chrome autofill server. + */ + public final String mServerType; + + /** + * The type computed overall type. The valid types are the same as for mServerType. + */ + public final String mComputedType; + + private String[] mServerPredictions; + + public static final Parcelable.Creator<ViewType> CREATOR = new Parcelable.Creator<ViewType>() { + @Override + public ViewType createFromParcel(Parcel in) { + return new ViewType(in); + } + + @Override + public ViewType[] newArray(int size) { + return new ViewType[size]; + } + }; + + public ViewType( + AutofillId id, String serverType, String computedType, String[] serverPredictions) { + mAutofillId = id; + mServerType = serverType; + mComputedType = computedType; + mServerPredictions = serverPredictions; + } + + private ViewType(Parcel in) { + mAutofillId = AutofillId.CREATOR.createFromParcel(in); + mServerType = in.readString(); + mComputedType = in.readString(); + in.readStringArray(mServerPredictions); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + mAutofillId.writeToParcel(parcel, flags); + parcel.writeString(mServerType); + parcel.writeString(mComputedType); + parcel.writeStringArray(mServerPredictions); + } + + /** + * @return the server predictions, they are in the order of the confidence. The mServerType + * shall be used if the server predictions aren't available. + */ + public String[] getServerPredictions() { + return mServerPredictions; + } +} diff --git a/chromium/components/autofill/android/provider/junit/src/org/chromium/components/autofill/AutofillProviderTest.java b/chromium/components/autofill/android/provider/junit/src/org/chromium/components/autofill/AutofillProviderTest.java index 5d63ae0630b..87c73025e49 100644 --- a/chromium/components/autofill/android/provider/junit/src/org/chromium/components/autofill/AutofillProviderTest.java +++ b/chromium/components/autofill/android/provider/junit/src/org/chromium/components/autofill/AutofillProviderTest.java @@ -83,11 +83,11 @@ public class AutofillProviderTest { public void testTransformFormFieldToContainViewCoordinates() { ArrayList<FormFieldData> fields = new ArrayList<FormFieldData>(1); fields.add(FormFieldData.createFormFieldData(null, null, null, null, false, null, null, - null, null, null, false, false, 0, null, 10 /* left */, 20 /* top */, - 300 /* right */, 60 /*bottom*/, null, null, true)); + null, null, null, false, false, 0, null, null, null, null, 10 /* left */, + 20 /* top */, 300 /* right */, 60 /*bottom*/, null, null, true)); fields.add(FormFieldData.createFormFieldData(null, null, null, null, false, null, null, - null, null, null, false, false, 0, null, 20 /* left */, 100 /* top */, - 400 /* right */, 200 /*bottom*/, null, null, true)); + null, null, null, false, false, 0, null, null, null, null, 20 /* left */, + 100 /* top */, 400 /* right */, 200 /*bottom*/, null, null, true)); FormData formData = new FormData(null, null, fields); mAutofillProvider.transformFormFieldToContainViewCoordinates(formData); RectF result = formData.mFields.get(0).getBoundsInContainerViewCoordinates(); diff --git a/chromium/components/autofill/android/provider/test_support/BUILD.gn b/chromium/components/autofill/android/provider/test_support/BUILD.gn new file mode 100644 index 00000000000..6ed8af92b68 --- /dev/null +++ b/chromium/components/autofill/android/provider/test_support/BUILD.gn @@ -0,0 +1,41 @@ +# Copyright 2020 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. + +import("//build/config/android/config.gni") +import("//build/config/android/rules.gni") + +testonly = true + +android_library("component_autofill_provider_java_test_support") { + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] + sources = [ + "java/src/org/chromium/components/autofill/AutofillHintsServiceTestHelper.java", + "java/src/org/chromium/components/autofill/AutofillProviderTestHelper.java", + ] + deps = [ + "//base:base_java", + "//base:base_java_test_support", + "//base:jni_java", + "//components/autofill/android/provider:autofill_aidl", + "//components/autofill/android/provider:java", + "//content/public/android:content_java", + "//third_party/androidx:androidx_annotation_annotation_java", + ] +} +generate_jni("jni_headers") { + sources = [ + "java/src/org/chromium/components/autofill/AutofillProviderTestHelper.java", + ] +} + +source_set("component_autofill_provider_native_test_support") { + sources = [ "autofill_provider_test_helper.cc" ] + deps = [ + ":jni_headers", + "//base", + "//components/autofill/content/browser", + "//components/autofill/core/browser:test_support", + "//content/public/browser", + ] +} diff --git a/chromium/components/autofill/android/provider/test_support/autofill_provider_test_helper.cc b/chromium/components/autofill/android/provider/test_support/autofill_provider_test_helper.cc new file mode 100644 index 00000000000..1d244099f3e --- /dev/null +++ b/chromium/components/autofill/android/provider/test_support/autofill_provider_test_helper.cc @@ -0,0 +1,173 @@ +// Copyright 2020 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 "components/autofill/android/provider/test_support/jni_headers/AutofillProviderTestHelper_jni.h" + +#include "base/android/jni_array.h" +#include "base/base64.h" +#include "base/strings/string16.h" +#include "components/autofill/content/browser/content_autofill_driver.h" +#include "components/autofill/content/browser/content_autofill_driver_factory.h" +#include "components/autofill/core/browser/autofill_provider.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/field_types.h" +#include "content/public/browser/web_contents.h" + +namespace autofill { + +namespace { +AutofillHandler* GetAutofillHandler(content::WebContents* web_contents, + content::RenderFrameHost* rfh) { + // Avoid using ContentAutofillDriver::GetForRenderFrameHost(), it will create + // a new ContentAutofillDriver. + if (ContentAutofillDriverFactory* factory = + ContentAutofillDriverFactory::FromWebContents(web_contents)) { + if (ContentAutofillDriver* driver = + static_cast<ContentAutofillDriver*>(factory->DriverForKey(rfh))) { + return driver->autofill_handler(); + } + } + return nullptr; +} + +AutofillHandler* ToMainFrameAutofillHandler( + const base::android::JavaParamRef<jobject>& jweb_contents) { + content::WebContents* web_contents = + content::WebContents::FromJavaWebContents(jweb_contents); + CHECK(web_contents); + AutofillHandler* autofill_handler = + GetAutofillHandler(web_contents, web_contents->GetMainFrame()); + CHECK(autofill_handler); + return autofill_handler; +} + +} // namespace + +static void JNI_AutofillProviderTestHelper_DisableDownloadServerForTesting( + JNIEnv* env_md_ctx_st) { + AutofillProvider::set_is_download_manager_disabled_for_testing(); +} + +static jboolean +JNI_AutofillProviderTestHelper_SimulateMainFrameAutofillServerResponseForTesting( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& jweb_contents, + const base::android::JavaParamRef<jobjectArray>& jfield_ids, + const base::android::JavaParamRef<jintArray>& jfield_types) { + std::vector<base::string16> field_ids; + base::android::AppendJavaStringArrayToStringVector(env, jfield_ids, + &field_ids); + std::vector<int> field_types; + base::android::JavaIntArrayToIntVector(env, jfield_types, &field_types); + + AutofillHandler* autofill_handler = ToMainFrameAutofillHandler(jweb_contents); + const std::map<FormRendererId, std::unique_ptr<FormStructure>>& + form_structures = autofill_handler->form_structures(); + CHECK(!form_structures.empty()); + + // Make API response with suggestions. + AutofillQueryResponse response; + AutofillQueryResponse::FormSuggestion* form_suggestion; + + form_suggestion = response.add_form_suggestions(); + size_t found_fields_count = 0; + std::vector<FormSignature> signatures; + for (auto& j : form_structures) { + FormData formData = j.second->ToFormData(); + for (size_t i = 0; i < field_ids.size(); ++i) { + for (auto form_field_data : formData.fields) { + if (form_field_data.id_attribute == field_ids[i]) { + autofill::test::AddFieldSuggestionToForm( + form_field_data, + static_cast<autofill::ServerFieldType>(field_types[i]), + form_suggestion); + found_fields_count++; + break; + } + } + } + if (found_fields_count > 0) { + signatures = autofill::test::GetEncodedSignatures(*(j.second)); + break; + } + } + CHECK(found_fields_count == field_ids.size()); + + std::string response_string; + CHECK(response.SerializeToString(&response_string)); + std::string encoded_response_string; + base::Base64Encode(response_string, &encoded_response_string); + autofill_handler->OnLoadedServerPredictionsForTest(encoded_response_string, + signatures); + return true; +} + +static jboolean +JNI_AutofillProviderTestHelper_SimulateMainFramePredictionsAutofillServerResponseForTesting( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& jweb_contents, + const base::android::JavaParamRef<jobjectArray>& jfield_ids, + const base::android::JavaParamRef<jobjectArray>& jfield_types) { + std::vector<base::string16> field_ids; + base::android::AppendJavaStringArrayToStringVector(env, jfield_ids, + &field_ids); + std::vector<std::vector<int>> field_types; + base::android::JavaArrayOfIntArrayToIntVector(env, jfield_types, + &field_types); + + AutofillHandler* autofill_handler = ToMainFrameAutofillHandler(jweb_contents); + const std::map<FormRendererId, std::unique_ptr<FormStructure>>& + form_structures = autofill_handler->form_structures(); + CHECK(!form_structures.empty()); + + // Make API response with suggestions. + AutofillQueryResponse response; + AutofillQueryResponse::FormSuggestion* form_suggestion; + + form_suggestion = response.add_form_suggestions(); + size_t found_fields_count = 0; + std::vector<FormSignature> signatures; + for (auto& j : form_structures) { + FormData formData = j.second->ToFormData(); + for (size_t i = 0; i < field_ids.size(); ++i) { + for (auto form_field_data : formData.fields) { + if (form_field_data.id_attribute == field_ids[i]) { + autofill::test::AddFieldPredictionsToForm( + form_field_data, field_types[i], form_suggestion); + found_fields_count++; + break; + } + } + } + if (found_fields_count > 0) { + signatures = autofill::test::GetEncodedSignatures(*(j.second)); + CHECK(found_fields_count == field_ids.size()); + } + } + + std::string response_string; + CHECK(response.SerializeToString(&response_string)); + std::string encoded_response_string; + base::Base64Encode(response_string, &encoded_response_string); + autofill_handler->OnLoadedServerPredictionsForTest(encoded_response_string, + signatures); + return true; +} + +static void +JNI_AutofillProviderTestHelper_SimulateMainFrameAutofillQueryFailedForTesting( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& jweb_contents) { + AutofillHandler* autofill_handler = ToMainFrameAutofillHandler(jweb_contents); + const std::map<FormRendererId, std::unique_ptr<FormStructure>>& + form_structures = autofill_handler->form_structures(); + // Always use first form. + CHECK(form_structures.size()); + autofill_handler->OnServerRequestErrorForTest( + *(autofill::test::GetEncodedSignatures(*(form_structures.begin()->second)) + .begin()), + AutofillDownloadManager::RequestType::REQUEST_QUERY, 400); +} + +} // namespace autofill diff --git a/chromium/components/autofill/android/provider/test_support/java/src/org/chromium/components/autofill/AutofillHintsServiceTestHelper.java b/chromium/components/autofill/android/provider/test_support/java/src/org/chromium/components/autofill/AutofillHintsServiceTestHelper.java new file mode 100644 index 00000000000..742e98d7471 --- /dev/null +++ b/chromium/components/autofill/android/provider/test_support/java/src/org/chromium/components/autofill/AutofillHintsServiceTestHelper.java @@ -0,0 +1,57 @@ +// Copyright 2020 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. + +package org.chromium.components.autofill; + +import android.os.IBinder; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.components.autofill_public.IAutofillHintsService; +import org.chromium.components.autofill_public.IViewTypeCallback; +import org.chromium.components.autofill_public.ViewType; + +import java.util.List; + +/** + * This class implements and registers IViewTypeCallback for testing. + */ +public class AutofillHintsServiceTestHelper { + public void registerViewTypeService(IBinder binder) throws Exception { + IAutofillHintsService.Stub.asInterface(binder).registerViewTypeCallback(getBinder()); + } + + private IViewTypeCallback.Stub mBinder = new IViewTypeCallback.Stub() { + @Override + public void onViewTypeAvailable(List<ViewType> viewTypeList) { + mViewTypeList = viewTypeList; + mCallbackHelper.notifyCalled(); + } + + @Override + public void onQueryFailed() { + mQueryFailed = true; + mCallbackHelper.notifyCalled(); + } + }; + + private List<ViewType> mViewTypeList; + private boolean mQueryFailed; + private CallbackHelper mCallbackHelper = new CallbackHelper(); + + public IViewTypeCallback getBinder() { + return mBinder; + } + + public List<ViewType> getViewTypes() { + return mViewTypeList; + } + + public boolean isQueryFailed() { + return mQueryFailed; + } + + public void waitForCallbackInvoked() throws Exception { + mCallbackHelper.waitForCallback(0); + } +} diff --git a/chromium/components/autofill/android/provider/test_support/java/src/org/chromium/components/autofill/AutofillProviderTestHelper.java b/chromium/components/autofill/android/provider/test_support/java/src/org/chromium/components/autofill/AutofillProviderTestHelper.java new file mode 100644 index 00000000000..5db6332bce7 --- /dev/null +++ b/chromium/components/autofill/android/provider/test_support/java/src/org/chromium/components/autofill/AutofillProviderTestHelper.java @@ -0,0 +1,64 @@ +// Copyright 2012 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. + +package org.chromium.components.autofill; + +import android.annotation.TargetApi; +import android.os.Build; + +import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; +import org.chromium.base.annotations.VerifiesOnO; +import org.chromium.content_public.browser.WebContents; + +/** + * The help class for Autofill Provider test to access the native code. + */ +@VerifiesOnO +@TargetApi(Build.VERSION_CODES.O) +@JNINamespace("autofill") +public class AutofillProviderTestHelper { + /** + * Disable the download server for testing to avoid the server response affect the integration + * tests. Must be called before WebContents is created. + */ + public static void disableDownloadServerForTesting() { + AutofillProviderTestHelperJni.get().disableDownloadServerForTesting(); + } + + /** + * Simulate the primary server type only. + */ + public static boolean simulateMainFrameAutofillServerResponseForTesting( + WebContents webContents, String[] fieldIds, int[] fieldTypes) { + return AutofillProviderTestHelperJni.get() + .simulateMainFrameAutofillServerResponseForTesting( + webContents, fieldIds, fieldTypes); + } + + /** + * Simulate the server predictions, the first prediction will be set as primary server type. + */ + public static boolean simulateMainFramePredictionsAutofillServerResponseForTesting( + WebContents webContents, String[] fieldIds, int[][] fieldTypes) { + return AutofillProviderTestHelperJni.get() + .simulateMainFramePredictionsAutofillServerResponseForTesting( + webContents, fieldIds, fieldTypes); + } + + public static void simulateMainFrameAutofillQueryFailedForTesting(WebContents webContents) { + AutofillProviderTestHelperJni.get().simulateMainFrameAutofillQueryFailedForTesting( + webContents); + } + + @NativeMethods + interface Natives { + void disableDownloadServerForTesting(); + boolean simulateMainFrameAutofillServerResponseForTesting( + WebContents webContents, String[] fieldIds, int[] fieldTypes); + boolean simulateMainFramePredictionsAutofillServerResponseForTesting( + WebContents webContents, String[] fieldIds, int[][] fieldTypes); + void simulateMainFrameAutofillQueryFailedForTesting(WebContents webContents); + } +} diff --git a/chromium/components/autofill/content/browser/BUILD.gn b/chromium/components/autofill/content/browser/BUILD.gn index d54493f4c21..2cb8cb1f316 100644 --- a/chromium/components/autofill/content/browser/BUILD.gn +++ b/chromium/components/autofill/content/browser/BUILD.gn @@ -31,9 +31,11 @@ static_library("browser") { "//base:i18n", "//components/os_crypt", "//components/prefs", + "//components/profile_metrics", "//components/resources", "//components/strings", "//components/user_prefs", + "//components/version_info", "//content/public/browser", "//content/public/common", "//gpu/config", @@ -77,6 +79,7 @@ source_set("unit_tests") { "//components/autofill/core/browser", "//components/autofill/core/browser:test_support", "//components/autofill/core/common", + "//components/version_info", "//content/public/browser", "//content/public/common", "//content/test:test_support", diff --git a/chromium/components/autofill/content/browser/DEPS b/chromium/components/autofill/content/browser/DEPS index c98729d3c65..9f9c1a2c0e0 100644 --- a/chromium/components/autofill/content/browser/DEPS +++ b/chromium/components/autofill/content/browser/DEPS @@ -1,12 +1,13 @@ include_rules = [ "+content/public/browser", "+components/keyed_service/content", + "+components/version_info", + "+components/profile_metrics", "+crypto/random.h", "+gpu/config/gpu_info.h", "+services/device/public", "+services/service_manager/public/mojom", "+third_party/blink/public/common", - "+third_party/blink/public/platform/web_rect.h", ] specific_include_rules = { diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc index 3c37af33dac..0bf21286ba2 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc @@ -4,6 +4,7 @@ #include "components/autofill/content/browser/content_autofill_driver.h" +#include <tuple> #include <utility> #include <vector> @@ -12,12 +13,14 @@ #include "build/build_config.h" #include "components/autofill/content/browser/content_autofill_driver_factory.h" #include "components/autofill/core/browser/autofill_client.h" -#include "components/autofill/core/browser/autofill_external_delegate.h" #include "components/autofill/core/browser/autofill_handler_proxy.h" #include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/payments/payments_service_url.h" #include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_payments_features.h" +#include "components/profile_metrics/browser_profile_type.h" +#include "components/version_info/channel.h" #include "content/public/browser/back_forward_cache.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/navigation_controller.h" @@ -36,13 +39,31 @@ #include "ui/gfx/geometry/size_f.h" #include "url/origin.h" +namespace { + +bool ShouldEnableHeavyFormDataScraping(const version_info::Channel channel) { + switch (channel) { + case version_info::Channel::CANARY: + case version_info::Channel::DEV: + return true; + case version_info::Channel::STABLE: + case version_info::Channel::BETA: + case version_info::Channel::UNKNOWN: + return false; + } + NOTREACHED(); + return false; +} + +} // namespace + namespace autofill { ContentAutofillDriver::ContentAutofillDriver( content::RenderFrameHost* render_frame_host, AutofillClient* client, const std::string& app_locale, - AutofillManager::AutofillDownloadManagerState enable_download_manager, + AutofillHandler::AutofillDownloadManagerState enable_download_manager, AutofillProvider* provider) : render_frame_host_(render_frame_host), autofill_manager_(nullptr), @@ -51,11 +72,14 @@ ContentAutofillDriver::ContentAutofillDriver( // AutofillManager isn't used if provider is valid, Autofill provider is // currently used by Android WebView only. if (provider) { - SetAutofillProvider(provider); + SetAutofillProvider(provider, client, enable_download_manager); } else { SetAutofillManager(std::make_unique<AutofillManager>( this, client, app_locale, enable_download_manager)); } + if (client && ShouldEnableHeavyFormDataScraping(client->GetChannel())) { + GetAutofillAgent()->EnableHeavyFormDataScraping(); + } } ContentAutofillDriver::~ContentAutofillDriver() = default; @@ -75,6 +99,17 @@ void ContentAutofillDriver::BindPendingReceiver( } bool ContentAutofillDriver::IsIncognito() const { + // TODO(https://crbug.com/1125474): Enable Autofill for Ephemeral Guest + // profiles. + // TODO(https://crbug.com/1125474): Consider renaming this function to + // |IsOffTheRecord| after deprecation of off-the-record or ephemeral Guest + // profiles. + if (autofill_manager_ && + autofill_manager_->client()->GetProfileType() == + profile_metrics::BrowserProfileType::kEphemeralGuest) { + return true; + } + return render_frame_host_->GetSiteInstance() ->GetBrowserContext() ->IsOffTheRecord(); @@ -136,8 +171,10 @@ void ContentAutofillDriver::SendFormDataToRenderer( void ContentAutofillDriver::PropagateAutofillPredictions( const std::vector<FormStructure*>& forms) { - autofill_manager_->client()->PropagateAutofillPredictions(render_frame_host_, - forms); + AutofillHandler* handler = + autofill_manager_ ? autofill_manager_ : autofill_handler_.get(); + DCHECK(handler); + handler->PropagateAutofillPredictions(render_frame_host_, forms); } void ContentAutofillDriver::HandleParsedForms( @@ -216,12 +253,11 @@ gfx::RectF ContentAutofillDriver::TransformBoundingBoxToViewportCoordinates( } net::IsolationInfo ContentAutofillDriver::IsolationInfo() { - return render_frame_host_->GetIsolationInfoForSubresources(); + return render_frame_host_->GetPendingIsolationInfoForSubresources(); } -void ContentAutofillDriver::FormsSeen(const std::vector<FormData>& forms, - base::TimeTicks timestamp) { - autofill_handler_->OnFormsSeen(forms, timestamp); +void ContentAutofillDriver::FormsSeen(const std::vector<FormData>& forms) { + autofill_handler_->OnFormsSeen(forms); } void ContentAutofillDriver::SetFormToBeProbablySubmitted( @@ -326,6 +362,14 @@ void ContentAutofillDriver::DidNavigateFrame( return; } + ShowOfferNotificationIfApplicable(navigation_handle); + + // When IsServedFromBackForwardCache, the form data is not parsed + // again. So, we should keep and use the autofill handler's + // form_structures from BFCache for form submit. + if (navigation_handle->IsServedFromBackForwardCache()) + return; + submitted_forms_.clear(); autofill_handler_->Reset(); } @@ -334,9 +378,6 @@ void ContentAutofillDriver::SetAutofillManager( std::unique_ptr<AutofillManager> manager) { autofill_handler_ = std::move(manager); autofill_manager_ = static_cast<AutofillManager*>(autofill_handler_.get()); - autofill_external_delegate_ = - std::make_unique<AutofillExternalDelegate>(autofill_manager_, this); - autofill_manager_->SetExternalDelegate(autofill_external_delegate_.get()); } ContentAutofillDriver::ContentAutofillDriver() @@ -381,9 +422,12 @@ void ContentAutofillDriver::RemoveHandler( view->GetRenderWidgetHost()->RemoveKeyPressEventCallback(handler); } -void ContentAutofillDriver::SetAutofillProvider(AutofillProvider* provider) { - autofill_handler_ = - std::make_unique<AutofillHandlerProxy>(this, log_manager_, provider); +void ContentAutofillDriver::SetAutofillProvider( + AutofillProvider* provider, + AutofillClient* client, + AutofillHandler::AutofillDownloadManagerState enable_download_manager) { + autofill_handler_ = std::make_unique<AutofillHandlerProxy>( + this, client, provider, enable_download_manager); GetAutofillAgent()->SetUserGestureRequired(false); GetAutofillAgent()->SetSecureContextRequired(true); GetAutofillAgent()->SetFocusRequiresScroll(false); @@ -428,10 +472,51 @@ void ContentAutofillDriver::ReportAutofillWebOTPMetrics( } void ContentAutofillDriver::SetAutofillProviderForTesting( - AutofillProvider* provider) { - SetAutofillProvider(provider); + AutofillProvider* provider, + AutofillClient* client) { + SetAutofillProvider(provider, client, + AutofillHandler::AutofillDownloadManagerState:: + DISABLE_AUTOFILL_DOWNLOAD_MANAGER); // AutofillManager isn't used if provider is valid. autofill_manager_ = nullptr; } +void ContentAutofillDriver::ShowOfferNotificationIfApplicable( + content::NavigationHandle* navigation_handle) { + if (!navigation_handle->IsInMainFrame()) + return; + + // TODO(crbug.com/1093057): Android webview does not have |autofill_manager_|, + // so flow is not enabled in Android Webview. + if (!base::FeatureList::IsEnabled( + features::kAutofillEnableOfferNotification) || + !autofill_manager_) { + return; + } + + AutofillOfferManager* offer_manager = autofill_manager_->offer_manager(); + // This happens in the Incognito mode. + if (!offer_manager) + return; + + GURL url = autofill_manager_->client()->GetLastCommittedURL(); + if (!offer_manager->IsUrlEligible(url)) + return; + + // Try to show offer notification when the last committed URL has the domain + // that an offer is applicable for. + std::tuple<std::vector<GURL>, GURL, CreditCard*> result = + offer_manager->GetEligibleDomainsAndCardForOfferForUrl(url); + std::vector<GURL>& domains = std::get<0>(result); + GURL offer_details_url = std::get<1>(result); + CreditCard* card = std::get<2>(result); + // TODO(crbug.com/1093057): Update return condition once we introduce the + // promo offers. + if (domains.empty() || !card) + return; + + autofill_manager_->client()->ShowOfferNotificationIfApplicable( + domains, offer_details_url, card); +} + } // namespace autofill diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h index efa287202f4..f659b5d0013 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver.h @@ -16,7 +16,6 @@ #include "components/autofill/content/common/mojom/autofill_agent.mojom.h" #include "components/autofill/content/common/mojom/autofill_driver.mojom.h" #include "components/autofill/core/browser/autofill_driver.h" -#include "components/autofill/core/browser/autofill_external_delegate.h" #include "components/autofill/core/browser/autofill_manager.h" #include "mojo/public/cpp/bindings/associated_receiver.h" #include "mojo/public/cpp/bindings/associated_remote.h" @@ -63,7 +62,7 @@ class ContentAutofillDriver : public AutofillDriver, content::RenderFrameHost* render_frame_host, AutofillClient* client, const std::string& app_locale, - AutofillManager::AutofillDownloadManagerState enable_download_manager, + AutofillHandler::AutofillDownloadManagerState enable_download_manager, AutofillProvider* provider); ~ContentAutofillDriver() override; @@ -107,8 +106,7 @@ class ContentAutofillDriver : public AutofillDriver, // mojom::AutofillDriver: void SetFormToBeProbablySubmitted( const base::Optional<FormData>& form) override; - void FormsSeen(const std::vector<FormData>& forms, - base::TimeTicks timestamp) override; + void FormsSeen(const std::vector<FormData>& forms) override; void FormSubmitted(const FormData& form, bool known_success, mojom::SubmissionSource source) override; @@ -155,10 +153,10 @@ class ContentAutofillDriver : public AutofillDriver, const content::RenderWidgetHost::KeyPressEventCallback& handler); void RemoveKeyPressHandler(); - void SetAutofillProviderForTesting(AutofillProvider* provider); + void SetAutofillProviderForTesting(AutofillProvider* provider, + AutofillClient* client); - // Sets the manager to |manager| and sets |manager|'s external delegate - // to |autofill_external_delegate_|. Takes ownership of |manager|. + // Sets the manager to |manager|. Takes ownership of |manager|. void SetAutofillManager(std::unique_ptr<AutofillManager> manager); // Reports whether a document collects phone numbers, uses one time code, uses @@ -182,12 +180,20 @@ class ContentAutofillDriver : public AutofillDriver, void RemoveHandler( const content::RenderWidgetHost::KeyPressEventCallback& handler) override; - void SetAutofillProvider(AutofillProvider* provider); + void SetAutofillProvider( + AutofillProvider* provider, + AutofillClient* client, + AutofillHandler::AutofillDownloadManagerState enable_download_manager); // Returns whether navigator.credentials.get({otp: {transport:"sms"}}) has // been used. bool DocumentUsedWebOTP() const; + // Show a bubble or infobar indicating that the current page has an eligible + // offer or reward, if the bubble/infobar is not currently being visible. + void ShowOfferNotificationIfApplicable( + content::NavigationHandle* navigation_handle); + // Weak ref to the RenderFrameHost the driver is associated with. Should // always be non-NULL and valid for lifetime of |this|. content::RenderFrameHost* const render_frame_host_; @@ -213,10 +219,6 @@ class ContentAutofillDriver : public AutofillDriver, // Pointer to an implementation of InternalAuthenticator. std::unique_ptr<InternalAuthenticator> authenticator_impl_; - // AutofillExternalDelegate instance that this object instantiates in the - // case where the Autofill native UI is enabled. - std::unique_ptr<AutofillExternalDelegate> autofill_external_delegate_; - KeyPressHandlerManager key_press_handler_manager_; LogManager* const log_manager_; diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc index 3862c76d670..508edb416b1 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc @@ -21,6 +21,7 @@ #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/form_data_predictions.h" +#include "components/version_info/version_info.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/ssl_status.h" #include "content/public/browser/storage_partition.h" @@ -141,6 +142,7 @@ class FakeAutofillAgent : public mojom::AutofillAgent { // Mocked mojom::AutofillAgent methods: MOCK_METHOD0(FirstUserGestureObservedInTab, void()); + MOCK_METHOD0(EnableHeavyFormDataScraping, void()); private: void CallDone() { @@ -311,10 +313,11 @@ class ContentAutofillDriverTest : public content::RenderViewHostTestHarness { content::RenderViewHostTestHarness::TearDown(); } - void Navigate(bool same_document) { + void Navigate(bool same_document, bool from_bfcache = false) { content::MockNavigationHandle navigation_handle(GURL(), main_rfh()); navigation_handle.set_has_committed(true); navigation_handle.set_is_same_document(same_document); + navigation_handle.set_is_served_from_bfcache(from_bfcache); driver_->DidNavigateFrame(&navigation_handle); } @@ -335,6 +338,11 @@ TEST_F(ContentAutofillDriverTest, NavigatedMainFrameSameDocument) { Navigate(/*same_document=*/true); } +TEST_F(ContentAutofillDriverTest, NavigatedMainFrameFromBackForwardCache) { + EXPECT_CALL(*driver_->mock_autofill_manager(), Reset()).Times(0); + Navigate(/*same_document=*/false, /*from_bfcache=*/true); +} + TEST_F(ContentAutofillDriverTest, FormDataSentToRenderer_FillForm) { int input_page_id = 42; FormData input_form_data; @@ -456,4 +464,30 @@ TEST_F(ContentAutofillDriverTest, PreviewFieldWithValue) { EXPECT_EQ(input_value, output_value); } +TEST_F(ContentAutofillDriverTest, EnableHeavyFormDataScraping) { + struct TestCase { + version_info::Channel channel; + bool heavy_scraping_enabled; + } kTestCases[] = {{version_info::Channel::CANARY, true}, + {version_info::Channel::DEV, true}, + {version_info::Channel::UNKNOWN, false}, + {version_info::Channel::BETA, false}, + {version_info::Channel::STABLE, false}}; + + for (auto test_case : kTestCases) { + SCOPED_TRACE(testing::Message() + << "channel: " + << version_info::GetChannelString(test_case.channel)); + test_autofill_client_->set_channel_for_testing(test_case.channel); + EXPECT_CALL(fake_agent_, EnableHeavyFormDataScraping()) + .Times(test_case.heavy_scraping_enabled ? 1 : 0); + + std::unique_ptr<ContentAutofillDriver> driver(new TestContentAutofillDriver( + web_contents()->GetMainFrame(), test_autofill_client_.get())); + + base::RunLoop().RunUntilIdle(); + testing::Mock::VerifyAndClearExpectations(&fake_agent_); + } +} + } // namespace autofill diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.cc b/chromium/components/autofill/content/browser/risk/fingerprint.cc index 994561d81ae..2607d63a434 100644 --- a/chromium/components/autofill/content/browser/risk/fingerprint.cc +++ b/chromium/components/autofill/content/browser/risk/fingerprint.cc @@ -45,7 +45,6 @@ #include "services/device/public/mojom/geolocation.mojom.h" #include "services/device/public/mojom/geolocation_context.mojom.h" #include "services/device/public/mojom/geoposition.mojom.h" -#include "third_party/blink/public/platform/web_rect.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/geometry/rect.h" @@ -328,7 +327,7 @@ void FingerprintDataLoader::OnGpuInfoUpdate() { return; DCHECK(gpu_observation_.IsObservingSource(gpu_data_manager_)); - gpu_observation_.RemoveObservation(); + gpu_observation_.Reset(); MaybeFillFingerprint(); } diff --git a/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc b/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc index 885c1d4206d..6ef24aa48ff 100644 --- a/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc +++ b/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc @@ -21,7 +21,6 @@ #include "services/device/public/mojom/geoposition.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/platform/web_rect.h" #include "ui/gfx/geometry/rect.h" using testing::ElementsAre; diff --git a/chromium/components/autofill/content/common/mojom/autofill_agent.mojom b/chromium/components/autofill/content/common/mojom/autofill_agent.mojom index 28e6bd73674..28177255d0b 100644 --- a/chromium/components/autofill/content/common/mojom/autofill_agent.mojom +++ b/chromium/components/autofill/content/common/mojom/autofill_agent.mojom @@ -78,6 +78,10 @@ interface AutofillAgent { // Set whether or not an assistant action is currently running an action. SetAssistantActionState(bool running); + + // Allows heavy scraping of form data (e.g., button titles for + // unowned forms). + EnableHeavyFormDataScraping(); }; // There is one instance of this interface per render frame in the render diff --git a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom index 106f0d69d37..a87410e220b 100644 --- a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom +++ b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom @@ -22,7 +22,7 @@ interface AutofillDriver { // Notification that forms have been seen that are candidates for // filling/submitting by the AutofillManager. - FormsSeen(array<FormData> forms, mojo_base.mojom.TimeTicks timestamp); + FormsSeen(array<FormData> forms); // Notification that a form has been submitted. The |known_success| indicates // whether the submission succeeded or not. Currently, we assume submission diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc index 95ff56c0a45..52e5547d61e 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/autofill_agent.cc @@ -437,13 +437,12 @@ void AutofillAgent::TriggerRefillIfNeeded(const FormData& form) { if (FindFormAndFieldForFormControlElement(element_, field_data_manager_.get(), &updated_form, &field) && (!element_.IsAutofilled() || !form.DynamicallySameFormAs(updated_form))) { - base::TimeTicks forms_seen_timestamp = AutofillTickClock::NowTicks(); WebLocalFrame* frame = render_frame()->GetWebFrame(); std::vector<FormData> forms; forms.push_back(updated_form); // Always communicate to browser process for topmost frame. if (!forms.empty() || !frame->Parent()) { - GetAutofillDriver()->FormsSeen(forms, forms_seen_timestamp); + GetAutofillDriver()->FormsSeen(forms); } } } @@ -481,8 +480,10 @@ void AutofillAgent::PreviewForm(int32_t id, const FormData& form) { if (id != autofill_query_id_) return; + ClearPreviewedForm(); + query_node_autofill_state_ = element_.GetAutofillState(); - form_util::PreviewForm(form, element_); + previewed_elements_ = form_util::PreviewForm(form, element_); GetAutofillDriver()->DidPreviewAutofillFormData(); } @@ -513,8 +514,9 @@ void AutofillAgent::ClearPreviewedForm() { if (password_autofill_agent_->DidClearAutofillSelection(element_)) return; - form_util::ClearPreviewedFormWithElement(element_, - query_node_autofill_state_); + form_util::ClearPreviewedElements(previewed_elements_, element_, + query_node_autofill_state_); + previewed_elements_ = {}; } void AutofillAgent::FillFieldWithValue(const base::string16& value) { @@ -759,6 +761,10 @@ void AutofillAgent::SetAssistantActionState(bool running) { } } +void AutofillAgent::EnableHeavyFormDataScraping() { + is_heavy_form_data_scraping_enabled_ = true; +} + void AutofillAgent::QueryAutofillSuggestions( const WebFormControlElement& element, bool autoselect_first_suggestion) { @@ -818,25 +824,23 @@ void AutofillAgent::DoFillFieldWithValue(const base::string16& value, void AutofillAgent::DoPreviewFieldWithValue(const base::string16& value, WebInputElement* node) { + ClearPreviewedForm(); query_node_autofill_state_ = element_.GetAutofillState(); node->SetSuggestedValue(blink::WebString::FromUTF16(value)); node->SetAutofillState(WebAutofillState::kPreviewed); form_util::PreviewSuggestion(node->SuggestedValue().Utf16(), node->Value().Utf16(), node); + previewed_elements_.push_back(*node); } void AutofillAgent::ProcessForms() { - // Record timestamp of when the forms are first seen. This is used to - // measure the overhead of the Autofill feature. - base::TimeTicks forms_seen_timestamp = AutofillTickClock::NowTicks(); - WebLocalFrame* frame = render_frame()->GetWebFrame(); std::vector<FormData> forms = form_cache_.ExtractNewForms(field_data_manager_.get()); // Always communicate to browser process for topmost frame. if (!forms.empty() || !frame->Parent()) { - GetAutofillDriver()->FormsSeen(forms, forms_seen_timestamp); + GetAutofillDriver()->FormsSeen(forms); } } @@ -1019,15 +1023,17 @@ void AutofillAgent::OnProvisionallySaveForm( UpdateLastInteractedForm(element.Form()); } else { // Remove invisible elements + WebLocalFrame* frame = render_frame()->GetWebFrame(); for (auto it = formless_elements_user_edited_.begin(); it != formless_elements_user_edited_.end();) { - if (form_util::IsWebElementVisible(*it)) { + if (form_util::IsFormControlVisible(frame, *it)) { it = formless_elements_user_edited_.erase(it); } else { ++it; } } - formless_elements_user_edited_.insert(element); + formless_elements_user_edited_.insert( + FieldRendererId(element.UniqueRendererFormControlId())); provisionally_saved_form_ = base::make_optional<FormData>(); if (!CollectFormlessElements(&provisionally_saved_form_.value())) { provisionally_saved_form_.reset(); @@ -1127,6 +1133,7 @@ base::Optional<FormData> AutofillAgent::GetSubmittedForm() const { } } else if (formless_elements_user_edited_.size() != 0 && !form_util::IsSomeControlElementVisible( + render_frame()->GetWebFrame(), formless_elements_user_edited_)) { // we check if all the elements the user has interacted with are gone, // to decide if submission has occurred, and use the diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h index a6d4cbeb9ec..dae44e9b0dd 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.h +++ b/chromium/components/autofill/content/renderer/autofill_agent.h @@ -100,6 +100,7 @@ class AutofillAgent : public content::RenderFrameObserver, const std::vector<std::string>& selectors, GetElementFormAndFieldDataCallback callback) override; void SetAssistantActionState(bool running) override; + void EnableHeavyFormDataScraping() override; void FormControlElementClicked(const blink::WebFormControlElement& element, bool was_focused); @@ -124,6 +125,10 @@ class AutofillAgent : public content::RenderFrameObserver, FormTracker* form_tracker_for_testing() { return &form_tracker_; } + bool is_heavy_form_data_scraping_enabled() { + return is_heavy_form_data_scraping_enabled_; + } + void SelectWasUpdated(const blink::WebFormControlElement& element); protected: @@ -297,6 +302,9 @@ class AutofillAgent : public content::RenderFrameObserver, // The element corresponding to the last request sent for form field Autofill. blink::WebFormControlElement element_; + // The elements that currently are being previewed. + std::vector<blink::WebFormControlElement> previewed_elements_; + // The form element currently requesting an interactive autocomplete. blink::WebFormElement in_flight_request_form_; @@ -305,7 +313,7 @@ class AutofillAgent : public content::RenderFrameObserver, // When dealing with forms that don't use a <form> tag, we keep track of the // elements the user has modified so we can determine when submission occurs. - std::set<blink::WebFormControlElement> formless_elements_user_edited_; + std::set<FieldRendererId> formless_elements_user_edited_; // The form user interacted, it is used if last_interacted_form_ or formless // form can't be converted to FormData at the time of form submission. @@ -367,6 +375,10 @@ class AutofillAgent : public content::RenderFrameObserver, // is. bool is_screen_reader_enabled_ = false; + // Whether agents should enable heavy scraping of form data (e.g., button + // titles for unowned forms). + bool is_heavy_form_data_scraping_enabled_ = false; + const scoped_refptr<FieldDataManager> field_data_manager_; base::WeakPtrFactory<AutofillAgent> weak_ptr_factory_{this}; diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc index f42cf808f28..d6da084c1bc 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc @@ -15,12 +15,12 @@ #include "base/check_op.h" #include "base/command_line.h" +#include "base/containers/contains.h" #include "base/i18n/case_conversion.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/notreached.h" -#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -99,15 +99,6 @@ enum FieldFilterMask { FILTER_NON_FOCUSABLE_ELEMENTS, }; -// Returns whether sending autofill field metadata to the server is enabled. -// TODO(crbug.com/938804): Remove this when button titles are crowdsourced in -// all channels. -bool IsAutofillFieldMetadataEnabled() { - static base::NoDestructor<std::string> kGroupName( - base::FieldTrialList::FindFullName("AutofillFieldMetadata")); - return base::StartsWith(*kGroupName, "Enabled", base::CompareCase::SENSITIVE); -} - void TruncateString(base::string16* str, size_t max_length) { if (str->length() > max_length) str->resize(max_length); @@ -999,7 +990,7 @@ typedef void (*Callback)(const FormFieldData&, bool, /* is_initiating_element */ blink::WebFormControlElement*); -void ForEachMatchingFormFieldCommon( +std::vector<WebFormControlElement> ForEachMatchingFormFieldCommon( std::vector<WebFormControlElement>* control_elements, const WebElement& initiating_element, const FormData& data, @@ -1009,6 +1000,9 @@ void ForEachMatchingFormFieldCommon( const Callback& callback) { DCHECK(control_elements); + std::vector<WebFormControlElement> matching_fields; + matching_fields.reserve(control_elements->size()); + const bool num_elements_matches_num_fields = control_elements->size() == data.fields.size(); UMA_HISTOGRAM_BOOLEAN("Autofill.NumElementsMatchesNumFields", @@ -1023,7 +1017,7 @@ void ForEachMatchingFormFieldCommon( // restrictions are applied. // // TODO(crbug/847221): Add a UKM to capture these events. - return; + return matching_fields; } // The intended behaviour is: @@ -1072,6 +1066,7 @@ void ForEachMatchingFormFieldCommon( if (!is_preview && element->Focused()) initially_focused_element = element; + matching_fields.push_back(*element); callback(data.fields[i], is_initiating_element, element); continue; } @@ -1115,7 +1110,7 @@ void ForEachMatchingFormFieldCommon( // If there is no other field to be autofilled, sending the blur event and // then the focus event for the initiating element does not make sense. if (autofillable_elements_index.empty()) - return; + return matching_fields; // A blur event is emitted for the focused element if it is the initiating // element before all other elements are autofilled. @@ -1123,50 +1118,58 @@ void ForEachMatchingFormFieldCommon( initially_focused_element->DispatchBlurEvent(); // Autofill the non-initiating elements. - for (const auto& index : autofillable_elements_index) + for (const auto& index : autofillable_elements_index) { + matching_fields.push_back((*control_elements)[index]); callback(data.fields[index], false, &(*control_elements)[index]); + } // A focus event is emitted for the initiating element after autofilling is // completed. It is not intended to work for the preview filling. if (initially_focused_element) initially_focused_element->DispatchFocusEvent(); + + return matching_fields; } // For each autofillable field in |data| that matches a field in the |form|, // the |callback| is invoked with the corresponding |form| field data. -void ForEachMatchingFormField(const WebFormElement& form_element, - const WebElement& initiating_element, - const FormData& data, - FieldFilterMask filters, - bool force_override, - bool is_preview, - const Callback& callback) { +std::vector<WebFormControlElement> ForEachMatchingFormField( + const WebFormElement& form_element, + const WebElement& initiating_element, + const FormData& data, + FieldFilterMask filters, + bool force_override, + bool is_preview, + const Callback& callback) { std::vector<WebFormControlElement> control_elements = ExtractAutofillableElementsInForm(form_element); - ForEachMatchingFormFieldCommon(&control_elements, initiating_element, data, - filters, force_override, is_preview, callback); + return ForEachMatchingFormFieldCommon(&control_elements, initiating_element, + data, filters, force_override, + is_preview, callback); } // For each autofillable field in |data| that matches a field in the set of // unowned autofillable form fields, the |callback| is invoked with the // corresponding |data| field. -void ForEachMatchingUnownedFormField(const WebElement& initiating_element, - const FormData& data, - FieldFilterMask filters, - bool force_override, - bool is_preview, - const Callback& callback) { +std::vector<WebFormControlElement> ForEachMatchingUnownedFormField( + const WebElement& initiating_element, + const FormData& data, + FieldFilterMask filters, + bool force_override, + bool is_preview, + const Callback& callback) { if (initiating_element.IsNull()) - return; + return {}; std::vector<WebFormControlElement> control_elements = GetUnownedAutofillableFormFieldElements( initiating_element.GetDocument().All(), nullptr); if (!IsElementInControlElementSet(initiating_element, control_elements)) - return; + return {}; - ForEachMatchingFormFieldCommon(&control_elements, initiating_element, data, - filters, force_override, is_preview, callback); + return ForEachMatchingFormFieldCommon(&control_elements, initiating_element, + data, filters, force_override, + is_preview, callback); } // Sets the |field|'s value to the value in |data|, and specifies the section @@ -1589,6 +1592,32 @@ bool IsSomeControlElementVisible( return false; } +bool IsSomeControlElementVisible( + blink::WebLocalFrame* frame, + const std::set<FieldRendererId>& control_elements) { + // This is basically a set intersection of |control_elements| and the form + // controls on the website. We don't call IsFormControlVisible() on each + // element in |control_elements| as that would be O(N * M). Iterating over + // all form controls on the website and checking their existence in + // control_lements makes this O(N log M), where N is the number of form + // controls on the website and M the number of elements in |control_elements|. + WebDocument doc = frame->GetDocument(); + if (doc.IsNull()) + return false; + WebElementCollection elements = doc.All(); + + for (WebElement element = elements.FirstItem(); !element.IsNull(); + element = elements.NextItem()) { + if (!element.IsFormControlElement() || !IsWebElementVisible(element)) + continue; + WebFormControlElement control = element.To<WebFormControlElement>(); + FieldRendererId field_renderer_id(control.UniqueRendererFormControlId()); + if (control_elements.find(field_renderer_id) != control_elements.end()) + return true; + } + return false; +} + bool AreFormContentsVisible(const WebFormElement& form) { return IsSomeControlElementVisible(form.GetFormControlElements()); } @@ -1663,10 +1692,8 @@ bool IsWebElementVisible(const blink::WebElement& element) { base::string16 GetFormIdentifier(const WebFormElement& form) { base::string16 identifier = form.GetName().Utf16(); - static base::NoDestructor<WebString> kId("id"); if (identifier.empty()) - identifier = form.GetAttribute(*kId).Utf16(); - + identifier = form.GetIdAttribute().Utf16(); return identifier; } @@ -1716,7 +1743,6 @@ void WebFormControlElementToFormField( DCHECK(field); DCHECK(!element.IsNull()); static base::NoDestructor<WebString> kAutocomplete("autocomplete"); - static base::NoDestructor<WebString> kId("id"); static base::NoDestructor<WebString> kName("name"); static base::NoDestructor<WebString> kRole("role"); static base::NoDestructor<WebString> kPlaceholder("placeholder"); @@ -1725,7 +1751,7 @@ void WebFormControlElementToFormField( // Save both id and name attributes, if present. If there is only one of them, // it will be saved to |name|. See HTMLFormControlElement::nameForAutofill. field->name = element.NameForAutofill().Utf16(); - field->id_attribute = element.GetAttribute(*kId).Utf16(); + field->id_attribute = element.GetIdAttribute().Utf16(); field->name_attribute = element.GetAttribute(*kName).Utf16(); field->unique_renderer_id = FieldRendererId(element.UniqueRendererFormControlId()); @@ -1837,15 +1863,15 @@ void WebFormControlElementToFormField( if (field_data_manager && field->properties_mask & (FieldPropertiesFlags::kUserTyped | FieldPropertiesFlags::kAutofilled)) { - const base::string16 typed_value = field_data_manager->GetUserTypedValue( + const base::string16 user_input = field_data_manager->GetUserInput( FieldRendererId(element.UniqueRendererFormControlId())); // The typed value is preserved for all passwords. It is also preserved for // potential usernames, as long as the |value| is not deemed acceptable. if (field->form_control_type == "password" || - !ScriptModifiedUsernameAcceptable(value, typed_value, + !ScriptModifiedUsernameAcceptable(value, user_input, field_data_manager)) { - field->typed_value = typed_value; + field->user_input = user_input; } } } @@ -2059,85 +2085,66 @@ bool FindFormAndFieldForFormControlElement( element, field_data_manager, form_util::EXTRACT_NONE, form, field); } -void FillForm(const FormData& form, const WebFormControlElement& element) { +std::vector<WebFormControlElement> FillForm( + const FormData& form, + const WebFormControlElement& element) { WebFormElement form_element = element.Form(); if (form_element.IsNull()) { - ForEachMatchingUnownedFormField(element, form, + return ForEachMatchingUnownedFormField(element, form, + FILTER_ALL_NON_EDITABLE_ELEMENTS, + false, /* dont force override */ + false, /* not a preview filling */ + &FillFormField); + } else { + return ForEachMatchingFormField(form_element, element, form, FILTER_ALL_NON_EDITABLE_ELEMENTS, false, /* dont force override */ false, /* not a preview filling */ &FillFormField); - return; } - - ForEachMatchingFormField(form_element, element, form, - FILTER_ALL_NON_EDITABLE_ELEMENTS, - false, /* dont force override */ - false, /* not a preview filling */ - &FillFormField); } -void PreviewForm(const FormData& form, const WebFormControlElement& element) { +std::vector<WebFormControlElement> PreviewForm( + const FormData& form, + const WebFormControlElement& element) { WebFormElement form_element = element.Form(); if (form_element.IsNull()) { - ForEachMatchingUnownedFormField(element, form, + return ForEachMatchingUnownedFormField(element, form, + FILTER_ALL_NON_EDITABLE_ELEMENTS, + false, /* dont force override */ + true, /* preview filling */ + &PreviewFormField); + } else { + return ForEachMatchingFormField(form_element, element, form, FILTER_ALL_NON_EDITABLE_ELEMENTS, false, /* dont force override */ true, /* preview filling */ &PreviewFormField); - return; } - - ForEachMatchingFormField(form_element, element, form, - FILTER_ALL_NON_EDITABLE_ELEMENTS, - false, /* dont force override */ - true, /* preview filling */ - &PreviewFormField); } -bool ClearPreviewedFormWithElement(const WebFormControlElement& element, - blink::WebAutofillState old_autofill_state) { - WebFormElement form_element = element.Form(); - std::vector<WebFormControlElement> control_elements; - if (form_element.IsNull()) { - control_elements = GetUnownedAutofillableFormFieldElements( - element.GetDocument().All(), nullptr); - if (!IsElementInControlElementSet(element, control_elements)) - return false; - } else { - control_elements = ExtractAutofillableElementsInForm(form_element); - } - - for (size_t i = 0; i < control_elements.size(); ++i) { - // There might be unrelated elements in this form which have already been - // auto-filled. For example, the user might have already filled the address - // part of a form and now be dealing with the credit card section. We only - // want to reset the auto-filled status for fields that were previewed. - WebFormControlElement control_element = control_elements[i]; - - // Only text input, textarea and select elements can be previewed. - WebInputElement* input_element = ToWebInputElement(&control_element); - if (!IsTextInput(input_element) && !IsMonthInput(input_element) && - !IsTextAreaElement(control_element) && - !IsSelectElement(control_element)) +void ClearPreviewedElements( + std::vector<blink::WebFormControlElement>& previewed_elements, + const WebFormControlElement& initiating_element, + blink::WebAutofillState old_autofill_state) { + for (WebFormControlElement& control_element : previewed_elements) { + if (control_element.IsNull()) continue; // Only clear previewed fields. if (control_element.GetAutofillState() != WebAutofillState::kPreviewed) continue; - if ((IsTextInput(input_element) || IsMonthInput(input_element) || - IsTextAreaElement(control_element) || - IsSelectElement(control_element)) && - control_element.SuggestedValue().IsEmpty()) + if (control_element.SuggestedValue().IsEmpty()) continue; // Clear the suggested value. For the initiating node, also restore the // original value. + WebInputElement* input_element = ToWebInputElement(&control_element); if (IsTextInput(input_element) || IsMonthInput(input_element) || IsTextAreaElement(control_element)) { control_element.SetSuggestedValue(WebString()); - bool is_initiating_node = (element == control_element); + bool is_initiating_node = (initiating_element == control_element); if (is_initiating_node) { // Clearing the suggested value in the focused node (above) can cause // selection to be lost. We force selection range to restore the text @@ -2145,17 +2152,14 @@ bool ClearPreviewedFormWithElement(const WebFormControlElement& element, int length = control_element.Value().length(); control_element.SetSelectionRange(length, length); control_element.SetAutofillState(old_autofill_state); - } else { control_element.SetAutofillState(WebAutofillState::kNotFilled); } - } else if (IsSelectElement(control_element)) { + } else { control_element.SetSuggestedValue(WebString()); control_element.SetAutofillState(WebAutofillState::kNotFilled); } } - - return true; } bool IsWebpageEmpty(const blink::WebLocalFrame* frame) { @@ -2211,9 +2215,10 @@ base::string16 FindChildText(const WebNode& node) { ButtonTitleList GetButtonTitles(const WebFormElement& web_form, const WebDocument& document, ButtonTitlesCache* button_titles_cache) { - DCHECK(button_titles_cache); - if (!IsAutofillFieldMetadataEnabled() && web_form.IsNull()) + if (!button_titles_cache) { + // Button titles scraping is disabled for this form. return ButtonTitleList(); + } // True if the cache has no entry for |web_form|. bool cache_miss = true; diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h index 1a33e640255..a8b0a8b46f3 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.h +++ b/chromium/components/autofill/content/renderer/form_autofill_util.h @@ -96,6 +96,11 @@ bool IsFormControlVisible(blink::WebLocalFrame* frame, bool IsSomeControlElementVisible( const blink::WebVector<blink::WebFormControlElement>& control_elements); +// Returns true if at least one element from |control_elements| is visible. +bool IsSomeControlElementVisible( + blink::WebLocalFrame* frame, + const std::set<FieldRendererId>& control_elements); + // Returns true if some control elements of |form| are visible. bool AreFormContentsVisible(const blink::WebFormElement& form); @@ -244,21 +249,24 @@ bool FindFormAndFieldForFormControlElement( FormFieldData* field); // Fills the form represented by |form|. |element| is the input element that -// initiated the auto-fill process. -void FillForm(const FormData& form, - const blink::WebFormControlElement& element); - -// Previews the form represented by |form|. |element| is the input element that -// initiated the preview process. -void PreviewForm(const FormData& form, - const blink::WebFormControlElement& element); - -// Clears the placeholder values and the auto-filled background for any fields -// in the form containing |node| that have been previewed. Resets the -// autofilled state of |node| to |was_autofilled|. Returns false if the form is -// not found. -bool ClearPreviewedFormWithElement(const blink::WebFormControlElement& element, - blink::WebAutofillState old_autofill_state); +// initiated the auto-fill process. Returns the filled fields. +std::vector<blink::WebFormControlElement> FillForm( + const FormData& form, + const blink::WebFormControlElement& element); + +// Previews the form represented by |form|. |element| is the input element that +// initiated the preview process. Returns the previewed fields. +std::vector<blink::WebFormControlElement> PreviewForm( + const FormData& form, + const blink::WebFormControlElement& element); + +// Clears the suggested values in |control_elements|. The state of +// |initiating_element| is set to |old_autofill_state|; all other fields are set +// to kNotFilled. +void ClearPreviewedElements( + std::vector<blink::WebFormControlElement>& control_elements, + const blink::WebFormControlElement& initiating_element, + blink::WebAutofillState old_autofill_state); // Checks if the webpage is empty. // This kind of webpage is considered as empty: diff --git a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc index 34575e77b47..17593a6df51 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc @@ -360,12 +360,6 @@ TEST_F(FormAutofillUtilsTest, GetButtonTitles_TooLongTitle) { } TEST_F(FormAutofillUtilsTest, GetButtonTitles_Formless) { - // Button titles computation and crowdsourcing for <form>less forms are - // enabled only if |AutofillFieldMetadata| (Dev and Canary) is enabled. - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.Init(); - base::FieldTrialList::CreateFieldTrial("AutofillFieldMetadata", "Enabled"); - constexpr char kNoFormHtml[] = "<div class='reg-form'>" " <input type='button' value='\n Show\t password '>" @@ -400,13 +394,10 @@ TEST_F(FormAutofillUtilsTest, GetButtonTitles_Formless) { VerifyButtonTitleCache(form_target, expected, cache); } -TEST_F(FormAutofillUtilsTest, GetButtonTitles_Formless_DisabledByDefault) { - // Button titles computation and crowdsourcing for <form>less forms should be - // disabled if |AutofillFieldMetadata| is disabled. - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.Init(); - base::FieldTrialList::CreateFieldTrial("AutofillFieldMetadata", "Disabled"); - +TEST_F(FormAutofillUtilsTest, GetButtonTitles_DisabledIfNoCache) { + // Button titles scraping for unowned forms can be time-consuming and disabled + // in Beta and Stable. To disable button titles computation, |buttons_cache| + // should be null. constexpr char kNoFormHtml[] = "<div class='reg-form'>" " <input type='button' value='\n Show\t password '>" @@ -425,13 +416,11 @@ TEST_F(FormAutofillUtilsTest, GetButtonTitles_Formless_DisabledByDefault) { ASSERT_NE(nullptr, web_frame); WebFormElement form_target; ASSERT_FALSE(web_frame->GetDocument().Body().IsNull()); - ButtonTitlesCache cache; autofill::ButtonTitleList actual = - GetButtonTitles(form_target, web_frame->GetDocument(), &cache); + GetButtonTitles(form_target, web_frame->GetDocument(), nullptr); EXPECT_TRUE(actual.empty()); - EXPECT_TRUE(cache.empty()); } TEST_F(FormAutofillUtilsTest, IsEnabled) { diff --git a/chromium/components/autofill/content/renderer/form_cache.cc b/chromium/components/autofill/content/renderer/form_cache.cc index a4aca1d571a..ab2c36aa4a9 100644 --- a/chromium/components/autofill/content/renderer/form_cache.cc +++ b/chromium/components/autofill/content/renderer/form_cache.cc @@ -10,6 +10,7 @@ #include <utility> #include "base/check_op.h" +#include "base/containers/contains.h" #include "base/feature_list.h" #include "base/macros.h" #include "base/stl_util.h" diff --git a/chromium/components/autofill/content/renderer/html_based_username_detector.cc b/chromium/components/autofill/content/renderer/html_based_username_detector.cc index df035aa498c..5824571b4b4 100644 --- a/chromium/components/autofill/content/renderer/html_based_username_detector.cc +++ b/chromium/components/autofill/content/renderer/html_based_username_detector.cc @@ -9,6 +9,7 @@ #include <tuple> #include <utility> +#include "base/containers/contains.h" #include "base/containers/flat_set.h" #include "base/i18n/case_conversion.h" #include "base/macros.h" diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc index 83a970ccad2..97731d7f534 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc @@ -13,13 +13,13 @@ #include <vector> #include "base/bind.h" +#include "base/containers/contains.h" #include "base/feature_list.h" #include "base/i18n/case_conversion.h" #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/numerics/safe_conversions.h" -#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -1370,7 +1370,9 @@ PasswordAutofillAgent::GetFormDataFromUnownedInputElements() { return nullptr; return CreateFormDataFromUnownedInputElements( *web_frame, field_data_manager_.get(), &username_detector_cache_, - &button_titles_cache_); + autofill_agent_ && autofill_agent_->is_heavy_form_data_scraping_enabled() + ? &button_titles_cache_ + : nullptr); } void PasswordAutofillAgent::InformAboutFormClearing( diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h index 040f7aecada..98b5b758bc5 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.h +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h @@ -14,7 +14,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "base/util/type_safety/strong_alias.h" +#include "base/types/strong_alias.h" #include "build/build_config.h" #include "components/autofill/content/common/mojom/autofill_agent.mojom.h" #include "components/autofill/content/common/mojom/autofill_driver.mojom.h" @@ -27,7 +27,6 @@ #include "components/autofill/core/common/password_form_fill_data.h" #include "components/autofill/core/common/renderer_id.h" #include "content/public/renderer/render_frame_observer.h" -#include "content/public/renderer/render_view_observer.h" #include "mojo/public/cpp/bindings/associated_receiver.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" @@ -108,9 +107,9 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, public FormTracker::Observer, public mojom::PasswordAutofillAgent { public: - using UseFallbackData = util::StrongAlias<class UseFallbackDataTag, bool>; - using ShowAll = util::StrongAlias<class ShowAllTag, bool>; - using GenerationShowing = util::StrongAlias<class GenerationShowingTag, bool>; + using UseFallbackData = base::StrongAlias<class UseFallbackDataTag, bool>; + using ShowAll = base::StrongAlias<class ShowAllTag, bool>; + using GenerationShowing = base::StrongAlias<class GenerationShowingTag, bool>; PasswordAutofillAgent(content::RenderFrame* render_frame, blink::AssociatedInterfaceRegistry* registry); @@ -253,7 +252,7 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, } private: - using OnPasswordField = util::StrongAlias<class OnPasswordFieldTag, bool>; + using OnPasswordField = base::StrongAlias<class OnPasswordFieldTag, bool>; // Enumeration representing possible Touch To Fill states. This is used to // make sure that Touch To Fill will only be shown in response to the first @@ -319,16 +318,17 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, DISALLOW_COPY_AND_ASSIGN(FocusStateNotifier); }; - // This class keeps track of autofilled password input elements and makes sure - // the autofilled password value is not accessible to JavaScript code until - // the user interacts with the page. + // This class keeps track of autofilled username and password input elements + // and ensures that the autofilled values are not accessible to JavaScript + // code until the user interacts with the page. This restriction improves + // privacy (crbug.com/798492) and reduces attack surface (crbug.com/777272). class PasswordValueGatekeeper { public: PasswordValueGatekeeper(); ~PasswordValueGatekeeper(); - // Call this for every autofilled password field, so that the gatekeeper - // protects the value accordingly. + // Call this for every autofilled username and password field, so that + // the gatekeeper protects the value accordingly. void RegisterElement(blink::WebInputElement* element); // Call this to notify the gatekeeper that the user interacted with the diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc index dea5a37779b..cfc0cb471d0 100644 --- a/chromium/components/autofill/content/renderer/password_generation_agent.cc +++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc @@ -12,7 +12,7 @@ #include "base/bind.h" #include "base/check_op.h" #include "base/command_line.h" -#include "base/stl_util.h" +#include "base/containers/contains.h" #include "base/threading/thread_task_runner_handle.h" #include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/content/renderer/password_autofill_agent.h" diff --git a/chromium/components/autofill/content/renderer/prefilled_values_detector.cc b/chromium/components/autofill/content/renderer/prefilled_values_detector.cc index 8ef1d3abaef..43e427c34f2 100644 --- a/chromium/components/autofill/content/renderer/prefilled_values_detector.cc +++ b/chromium/components/autofill/content/renderer/prefilled_values_detector.cc @@ -4,87 +4,88 @@ #include "components/autofill/content/renderer/prefilled_values_detector.h" -#include "base/no_destructor.h" +#include "base/containers/fixed_flat_set.h" #include "base/strings/string_util.h" namespace autofill { -const base::flat_set<std::string, std::less<>>& KnownUsernamePlaceholders() { - // Explicitly create a |StringFlatSet| when constructing - // kPrefilledUsernameValues to work around GCC bug 84849, which causes the - // initializer list not to be properly forwarded to base::flat_set's - // constructor. - using StringFlatSet = base::flat_set<std::string, std::less<>>; - static base::NoDestructor<StringFlatSet> kPrefilledUsernameValues( - StringFlatSet({"___.___.___-__", - "+1", - "3~15个字符,中文字符7个以内", - "benutzername", - "client id", - "codice titolare", - "digite seu cpf ou e-mail", - "ds logon username", - "email", - "email address", - "email masih kosong", - "email/手機號碼", - "e-mail/username", - "e-mail address", - "enter username", - "enter user name", - "identifiant", - "kullanıcı adı", - "kunden-id", - "login", - "nick", - "nom d'usuari", - "nom utilisateur", - "rut", - "siret", - "this is usually your email address", - "tu dni", - "uid/用戶名/email", - "uporabnik...", - "user/codice", - "user id", - "user name", - "username", - "username or email", - "username or email:", - "username/email", - "usuario", - "your email address", - "ååååmmddxxxx", - "아이디 or @이하 모두 입력", - "Имя", - "Имя (логин)", - "Логин", - "Логин...", - "Логин (e-mail)", - "שם משתמש", - "כתובת דוא''ל", - "اسم العضو", - "اسم المستخدم", - "الاسم", - "نام کاربری", - "メールアドレス", - "อีเมล", - "用户名", - "用户名/email", - "邮箱/手机", - "帳號", - "請輸入身份證字號", - "请用微博帐号登录", - "请输入手机号或邮箱", - "请输入邮箱或手机号", - "邮箱/手机/展位号"})); - return *kPrefilledUsernameValues; +namespace { + +constexpr auto kKnownUsernamePlaceholders = + base::MakeFixedFlatSet<base::StringPiece>({ + "___.___.___-__", + "+1", + "3~15个字符,中文字符7个以内", + "benutzername", + "client id", + "codice titolare", + "digite seu cpf ou e-mail", + "ds logon username", + "email", + "email address", + "email masih kosong", + "email/手機號碼", + "e-mail/username", + "e-mail address", + "enter username", + "enter user name", + "identifiant", + "kullanıcı adı", + "kunden-id", + "login", + "nick", + "nom d'usuari", + "nom utilisateur", + "rut", + "siret", + "this is usually your email address", + "tu dni", + "uid/用戶名/email", + "uporabnik...", + "user/codice", + "user id", + "user name", + "username", + "username or email", + "username or email:", + "username/email", + "usuario", + "your email address", + "ååååmmddxxxx", + "아이디 or @이하 모두 입력", + "Имя", + "Имя (логин)", + "Логин", + "Логин...", + "Логин (e-mail)", + "שם משתמש", + "כתובת דוא''ל", + "اسم العضو", + "اسم المستخدم", + "الاسم", + "نام کاربری", + "メールアドレス", + "อีเมล", + "用户名", + "用户名/email", + "邮箱/手机", + "帳號", + "請輸入身份證字號", + "请用微博帐号登录", + "请输入手机号或邮箱", + "请输入邮箱或手机号", + "邮箱/手机/展位号", + }); + +} // namespace + +base::span<const base::StringPiece> KnownUsernamePlaceholders() { + return base::make_span(kKnownUsernamePlaceholders.begin(), + kKnownUsernamePlaceholders.end()); } bool PossiblePrefilledUsernameValue(const std::string& username_value, const std::string& possible_email_domain) { - const auto& placeholders = KnownUsernamePlaceholders(); - std::string normalized_username_value = base::ToLowerASCII( base::TrimWhitespaceASCII(username_value, base::TRIM_ALL)); @@ -101,7 +102,7 @@ bool PossiblePrefilledUsernameValue(const std::string& username_value, return true; } - return placeholders.find(normalized_username_value) != placeholders.end(); + return kKnownUsernamePlaceholders.contains(normalized_username_value); } } // namespace autofill diff --git a/chromium/components/autofill/content/renderer/prefilled_values_detector.h b/chromium/components/autofill/content/renderer/prefilled_values_detector.h index 8c4c732dc26..f8071de7e4d 100644 --- a/chromium/components/autofill/content/renderer/prefilled_values_detector.h +++ b/chromium/components/autofill/content/renderer/prefilled_values_detector.h @@ -7,14 +7,15 @@ #include <string> -#include "base/containers/flat_set.h" +#include "base/containers/span.h" +#include "base/strings/string_piece.h" namespace autofill { -// Returns a set of known username placeholders, all guaranteed to be lower +// Returns a list of known username placeholders, all guaranteed to be lower // case. // This is only exposed for testing. -const base::flat_set<std::string, std::less<>>& KnownUsernamePlaceholders(); +base::span<const base::StringPiece> KnownUsernamePlaceholders(); // Checks if the prefilled value of the username element is one of the known // values possibly used as placeholders. The list of possible placeholder diff --git a/chromium/components/autofill/content/renderer/prefilled_values_detector_unittest.cc b/chromium/components/autofill/content/renderer/prefilled_values_detector_unittest.cc index 6c3250665e3..fa87d73d922 100644 --- a/chromium/components/autofill/content/renderer/prefilled_values_detector_unittest.cc +++ b/chromium/components/autofill/content/renderer/prefilled_values_detector_unittest.cc @@ -12,7 +12,7 @@ namespace autofill { // Ensure that all entries in KnownUsernamePlaceholders() are lowercase because // the lowercase string of the website is tested against this set. TEST(PossiblePrefilledUsernameValue, AllLowerCase) { - for (const auto& entry : KnownUsernamePlaceholders()) + for (auto entry : KnownUsernamePlaceholders()) EXPECT_EQ(entry, base::ToLowerASCII(entry)); } diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn index b0dcbce1521..d6ce1fbbc51 100644 --- a/chromium/components/autofill/core/browser/BUILD.gn +++ b/chromium/components/autofill/core/browser/BUILD.gn @@ -18,6 +18,17 @@ grit("autofill_address_rewriter_resources") { output_dir = "$root_gen_dir/components/autofill/core/browser" } +action("default_regex_patterns_cc") { + visibility = [ ":*" ] + sources = [ "pattern_provider/resources/regex_patterns.json" ] + script = "pattern_provider/transpile_default_regex_patterns.py" + args = [ + rebase_path("pattern_provider/resources/regex_patterns.json"), + rebase_path("$target_gen_dir/pattern_provider/default_regex_patterns.cc"), + ] + outputs = [ "$target_gen_dir/pattern_provider/default_regex_patterns.cc" ] +} + static_library("browser") { sources = [ "address_normalization_manager.cc", @@ -25,8 +36,8 @@ static_library("browser") { "address_normalizer.h", "address_normalizer_impl.cc", "address_normalizer_impl.h", - "address_profiles/address_profile_save_manager.cc", - "address_profiles/address_profile_save_manager.h", + "address_profile_save_manager.cc", + "address_profile_save_manager.h", "address_rewriter.cc", "address_rewriter.h", "autocomplete_history_manager.cc", @@ -151,6 +162,10 @@ static_library("browser") { "form_parsing/search_field.h", "form_parsing/travel_field.cc", "form_parsing/travel_field.h", + "form_processing/label_processing_util.cc", + "form_processing/label_processing_util.h", + "form_processing/name_processing_util.cc", + "form_processing/name_processing_util.h", "form_structure.cc", "form_structure.h", "form_types.cc", @@ -193,6 +208,7 @@ static_library("browser") { "metrics/form_event_logger_base.cc", "metrics/form_event_logger_base.h", "metrics/form_events.h", + "pattern_provider/default_regex_patterns.h", "pattern_provider/pattern_configuration_parser.cc", "pattern_provider/pattern_configuration_parser.h", "pattern_provider/pattern_provider.cc", @@ -234,6 +250,8 @@ static_library("browser") { "payments/webauthn_callback_types.h", "personal_data_manager.cc", "personal_data_manager.h", + "personal_data_manager_cleaner.cc", + "personal_data_manager_cleaner.h", "personal_data_manager_observer.h", "randomized_encoder.cc", "randomized_encoder.h", @@ -314,6 +332,8 @@ static_library("browser") { "webdata/system_encryptor.h", ] + sources += get_target_outputs(":default_regex_patterns_cc") + if (is_win) { sources += [ "autofill_ie_toolbar_import_win.cc", @@ -332,6 +352,8 @@ static_library("browser") { sources += [ "payments/autofill_credit_card_filling_infobar_delegate_mobile.cc", "payments/autofill_credit_card_filling_infobar_delegate_mobile.h", + "payments/autofill_offer_notification_infobar_delegate_mobile.cc", + "payments/autofill_offer_notification_infobar_delegate_mobile.h", "payments/autofill_save_card_infobar_delegate_mobile.cc", "payments/autofill_save_card_infobar_delegate_mobile.h", "payments/autofill_save_card_infobar_mobile.h", @@ -376,25 +398,29 @@ static_library("browser") { ] deps = [ ":autofill_address_rewriter_resources", + ":default_regex_patterns_cc", "//base", "//base:i18n", "//build:branding_buildflags", + "//build:chromeos_buildflags", "//components/google/core/common", "//components/history/core/browser", "//components/infobars/core", "//components/keyed_service/core", - "//components/language_usage_metrics", + "//components/language/core/browser", "//components/leveldb_proto", "//components/os_crypt", "//components/policy/core/browser", "//components/policy/core/common", "//components/pref_registry", "//components/prefs", + "//components/profile_metrics", "//components/signin/public/base", "//components/signin/public/identity_manager", "//components/strings", "//components/sync", "//components/translate/core/browser", + "//components/translate/core/common", "//components/variations/net", "//components/variations/service:service", "//components/version_info", @@ -461,8 +487,6 @@ static_library("test_support") { "logging/stub_log_manager.h", "mock_autocomplete_history_manager.cc", "mock_autocomplete_history_manager.h", - "pattern_provider/test_pattern_provider.cc", - "pattern_provider/test_pattern_provider.h", "payments/test_authentication_requester.cc", "payments/test_authentication_requester.h", "payments/test_credit_card_save_manager.cc", @@ -494,7 +518,6 @@ static_library("test_support") { "test_autofill_profile_validator.h", "test_autofill_profile_validator_delayed.cc", "test_autofill_profile_validator_delayed.h", - "test_autofill_provider.cc", "test_autofill_provider.h", "test_autofill_tick_clock.cc", "test_autofill_tick_clock.h", @@ -533,6 +556,7 @@ static_library("test_support") { "//components/translate/core/browser:test_support", "//components/ukm", "//components/ukm:test_support", + "//components/version_info:version_info", "//google_apis:test_support", "//services/network:test_support", "//services/network/public/cpp", @@ -593,7 +617,7 @@ source_set("unit_tests") { sources = [ "address_normalization_manager_unittest.cc", "address_normalizer_impl_unittest.cc", - "address_profiles/address_profile_save_manager_unittest.cc", + "address_profile_save_manager_unittest.cc", "address_rewriter_unittest.cc", "autocomplete_history_manager_unittest.cc", "autofill_address_policy_handler_unittest.cc", @@ -609,6 +633,7 @@ source_set("unit_tests") { "autofill_profile_sync_util_unittest.cc", "autofill_profile_validation_util_unittest.cc", "autofill_profile_validator_unittest.cc", + "autofill_provider_unittest.cc", "autofill_regexes_unittest.cc", "autofill_subject_unittest.cc", "autofill_type_unittest.cc", @@ -634,9 +659,13 @@ source_set("unit_tests") { "form_parsing/field_candidates_unittest.cc", "form_parsing/form_field_unittest.cc", "form_parsing/name_field_unittest.cc", + "form_parsing/parsing_test_utils.cc", + "form_parsing/parsing_test_utils.h", "form_parsing/phone_field_unittest.cc", "form_parsing/price_field_unittest.cc", "form_parsing/search_field_unittest.cc", + "form_processing/label_processing_util_unittest.cc", + "form_processing/name_processing_util_unittest.cc", "form_structure_unittest.cc", "geo/address_i18n_unittest.cc", "geo/alternative_state_name_map_unittest.cc", @@ -723,8 +752,8 @@ source_set("unit_tests") { ":unit_tests_bundle_data", "//base", "//base/test:test_support", + "//build:chromeos_buildflags", "//components/autofill/core/common", - "//components/infobars/core:feature_flags", "//components/leveldb_proto", "//components/os_crypt", "//components/os_crypt:test_support", @@ -738,6 +767,7 @@ source_set("unit_tests") { "//components/sync:test_support_model", "//components/sync/driver:test_support", "//components/translate/core/browser", + "//components/translate/core/common", "//components/ukm", "//components/ukm:test_support", "//components/unified_consent", diff --git a/chromium/components/autofill/core/browser/DEPS b/chromium/components/autofill/core/browser/DEPS index 99a308e09b9..69082786805 100644 --- a/chromium/components/autofill/core/browser/DEPS +++ b/chromium/components/autofill/core/browser/DEPS @@ -4,15 +4,17 @@ include_rules = [ "+components/history/core/browser", "+components/infobars/core", "+components/keyed_service/core", - "+components/language_usage_metrics", + "+components/language/core/browser", "+components/leveldb_proto", "+components/metrics", + "+components/profile_metrics", "+components/policy", "+components/security_state", "+components/security_interstitials/core/pref_names.h", "+components/signin/public", "+components/sync", "+components/translate/core/browser", + "+components/translate/core/common", "+components/variations", "+components/version_info", "+components/webdata/common", diff --git a/chromium/components/autofill/core/browser/address_profile_save_manager.cc b/chromium/components/autofill/core/browser/address_profile_save_manager.cc new file mode 100644 index 00000000000..4272c8e7eb1 --- /dev/null +++ b/chromium/components/autofill/core/browser/address_profile_save_manager.cc @@ -0,0 +1,54 @@ +// Copyright 2020 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 "components/autofill/core/browser/address_profile_save_manager.h" + +#include "components/autofill/core/browser/data_model/autofill_profile.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/common/autofill_features.h" + +namespace autofill { + +AddressProfileSaveManager::AddressProfileSaveManager( + AutofillClient* client, + PersonalDataManager* personal_data_manager) + : client_(client), personal_data_manager_(personal_data_manager) {} + +AddressProfileSaveManager::~AddressProfileSaveManager() = default; + +void AddressProfileSaveManager::SaveProfile(const AutofillProfile& profile) { + if (!personal_data_manager_) + return; + + if (base::FeatureList::IsEnabled( + features::kAutofillAddressProfileSavePrompt)) { + client_->ConfirmSaveAddressProfile( + profile, + base::BindOnce(&AddressProfileSaveManager::SaveProfilePromptCallback, + weak_ptr_factory_.GetWeakPtr())); + return; + } + SaveProfileInternal(profile); +} + +void AddressProfileSaveManager::SaveProfileInternal( + const AutofillProfile& profile) { + personal_data_manager_->SaveImportedProfile(profile); +} + +void AddressProfileSaveManager::SaveProfilePromptCallback( + AutofillClient::SaveAddressProfileOfferUserDecision user_decision, + AutofillProfile profile) { + switch (user_decision) { + case AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted: + case AutofillClient::SaveAddressProfileOfferUserDecision::kEdited: + personal_data_manager_->SaveImportedProfile(profile); + break; + case AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined: + case AutofillClient::SaveAddressProfileOfferUserDecision::kIgnored: + break; + } +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/address_profile_save_manager.h b/chromium/components/autofill/core/browser/address_profile_save_manager.h new file mode 100644 index 00000000000..0eec8bf5ae5 --- /dev/null +++ b/chromium/components/autofill/core/browser/address_profile_save_manager.h @@ -0,0 +1,50 @@ +// Copyright 2020 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_PROFILE_SAVE_MANAGER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_PROFILE_SAVE_MANAGER_H_ + +#include <string> + +#include "base/memory/weak_ptr.h" +#include "components/autofill/core/browser/autofill_client.h" + +namespace autofill { + +class AutofillProfile; +class PersonalDataManager; + +// Manages logic for saving address profiles to the database. Owned by +// FormDataImporter. +class AddressProfileSaveManager { + public: + // The parameters should outlive the AddressProfileSaveManager. + AddressProfileSaveManager(AutofillClient* client, + PersonalDataManager* personal_data_manager); + AddressProfileSaveManager(const AddressProfileSaveManager&) = delete; + AddressProfileSaveManager& operator=(const AddressProfileSaveManager&) = + delete; + virtual ~AddressProfileSaveManager(); + + // Saves `profile` using the `personal_data_manager_`. + void SaveProfile(const AutofillProfile& profile); + + private: + void SaveProfilePromptCallback( + AutofillClient::SaveAddressProfileOfferUserDecision user_decision, + AutofillProfile profile); + void SaveProfileInternal(const AutofillProfile& profile); + + AutofillClient* const client_; + + // The personal data manager, used to save and load personal data to/from the + // web database. + PersonalDataManager* const personal_data_manager_; + + base::WeakPtrFactory<AddressProfileSaveManager> weak_ptr_factory_{this}; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_PROFILE_SAVE_MANAGER_H_ diff --git a/chromium/components/autofill/core/browser/address_profile_save_manager_unittest.cc b/chromium/components/autofill/core/browser/address_profile_save_manager_unittest.cc new file mode 100644 index 00000000000..19b9037b57e --- /dev/null +++ b/chromium/components/autofill/core/browser/address_profile_save_manager_unittest.cc @@ -0,0 +1,49 @@ +// Copyright 2020 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 "components/autofill/core/browser/address_profile_save_manager.h" + +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/test_autofill_client.h" +#include "components/autofill/core/browser/test_personal_data_manager.h" +#include "components/autofill/core/common/autofill_features.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +namespace { +class MockPersonalDataManager : public TestPersonalDataManager { + public: + MockPersonalDataManager() = default; + ~MockPersonalDataManager() override = default; + MOCK_METHOD(std::string, + SaveImportedProfile, + (const AutofillProfile&), + (override)); +}; + +class AddressProfileSaveManagerTest : public testing::Test { + protected: + base::test::TaskEnvironment task_environment_; + TestAutofillClient autofill_client_; + MockPersonalDataManager mock_personal_data_manager_; +}; + +} // namespace + +TEST_F(AddressProfileSaveManagerTest, SaveProfileWhenNoSavePrompt) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndDisableFeature( + features::kAutofillAddressProfileSavePrompt); + + AddressProfileSaveManager save_manager(&autofill_client_, + &mock_personal_data_manager_); + AutofillProfile test_profile = test::GetFullProfile(); + EXPECT_CALL(mock_personal_data_manager_, SaveImportedProfile(test_profile)); + save_manager.SaveProfile(test_profile); +} +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager.cc b/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager.cc deleted file mode 100644 index 4081c68239a..00000000000 --- a/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2020 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 "components/autofill/core/browser/address_profiles/address_profile_save_manager.h" - -#include "components/autofill/core/browser/data_model/autofill_profile.h" -#include "components/autofill/core/browser/personal_data_manager.h" - -namespace autofill { - -AddressProfileSaveManager::AddressProfileSaveManager( - PersonalDataManager* personal_data_manager) - : personal_data_manager_(personal_data_manager) {} - -AddressProfileSaveManager::~AddressProfileSaveManager() = default; - -std::string AddressProfileSaveManager::SaveProfile( - const AutofillProfile& profile) { - return personal_data_manager_ - ? personal_data_manager_->SaveImportedProfile(profile) - : std::string(); -} - -} // namespace autofill diff --git a/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager.h b/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager.h deleted file mode 100644 index 69ac2d01511..00000000000 --- a/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 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. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_PROFILES_ADDRESS_PROFILE_SAVE_MANAGER_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_PROFILES_ADDRESS_PROFILE_SAVE_MANAGER_H_ - -#include <string> - -namespace autofill { - -class AutofillProfile; -class PersonalDataManager; - -// Manages logic for saving address profiles to the database. Owned by -// FormDataImporter. -class AddressProfileSaveManager { - public: - explicit AddressProfileSaveManager( - PersonalDataManager* personal_data_manager); - AddressProfileSaveManager(const AddressProfileSaveManager&) = delete; - AddressProfileSaveManager& operator=(const AddressProfileSaveManager&) = - delete; - virtual ~AddressProfileSaveManager(); - - // Saves `imported_profile` using the `personal_data_manager_`. Returns the - // guid of the new or updated profile, or the empty string if no profile was - // saved. - std::string SaveProfile(const AutofillProfile& imported_profile); - - private: - // The personal data manager, used to save and load personal data to/from the - // web database. - PersonalDataManager* const personal_data_manager_; -}; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_PROFILES_ADDRESS_PROFILE_SAVE_MANAGER_H_ diff --git a/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager_unittest.cc b/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager_unittest.cc deleted file mode 100644 index b73b0dc35e5..00000000000 --- a/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager_unittest.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2020 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 "components/autofill/core/browser/address_profiles/address_profile_save_manager.h" - -#include "components/autofill/core/browser/autofill_test_utils.h" -#include "components/autofill/core/browser/test_personal_data_manager.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace autofill { - -namespace { -class MockPersonalDataManager : public TestPersonalDataManager { - public: - MockPersonalDataManager() = default; - ~MockPersonalDataManager() override = default; - MOCK_METHOD(std::string, - SaveImportedProfile, - (const AutofillProfile&), - (override)); -}; - -} // namespace - -TEST(AddressProfileSaveManager, SaveProfile) { - MockPersonalDataManager pdm; - AddressProfileSaveManager save_manager(&pdm); - AutofillProfile test_profile = test::GetFullProfile(); - EXPECT_CALL(pdm, SaveImportedProfile(test_profile)); - save_manager.SaveProfile(test_profile); -} -} // namespace autofill diff --git a/chromium/components/autofill/core/browser/address_rewriter.cc b/chromium/components/autofill/core/browser/address_rewriter.cc index 030a5aba146..c2d8533e376 100644 --- a/chromium/components/autofill/core/browser/address_rewriter.cc +++ b/chromium/components/autofill/core/browser/address_rewriter.cc @@ -35,8 +35,8 @@ static bool ExtractRegionRulesData(const std::string& region, int resource_id = 0; std::string resource_key = GetMapKey(region); for (size_t i = 0; i < kAutofillAddressRewriterResourcesSize; ++i) { - if (kAutofillAddressRewriterResources[i].name == resource_key) { - resource_id = kAutofillAddressRewriterResources[i].value; + if (kAutofillAddressRewriterResources[i].path == resource_key) { + resource_id = kAutofillAddressRewriterResources[i].id; break; } } diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc index 364ee098871..6ffe717feb3 100644 --- a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc +++ b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc @@ -15,10 +15,12 @@ #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_metrics.h" +#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/browser/ui/suggestion.h" #include "components/autofill/core/browser/validation.h" #include "components/autofill/core/browser/webdata/autofill_entry.h" #include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/form_data.h" #include "components/prefs/pref_service.h" @@ -42,6 +44,22 @@ bool IsTextField(const FormFieldData& field) { field.form_control_type == "email"; } +// Returns true if the field has a meaningful name. +// An input field name 'field_2' bears no semantic meaning and there is a chance +// that a different website or different form uses the same field name for a +// totally different purpose. +bool IsMeaningfulFieldName(const base::string16& name) { + // If the corresponding feature is not enabled, every field name is considered + // as meaningful. + if (!base::FeatureList::IsEnabled( + features::kAutocompleteFilterForMeaningfulNames)) { + return true; + } + return !MatchesPattern( + name, + base::UTF8ToUTF16("^(((field|input)(_|-)?\\d+)|tan|otp|title|captcha)$")); +} + } // namespace void AutocompleteHistoryManager::UMARecorder::OnGetAutocompleteSuggestions( @@ -153,7 +171,8 @@ void AutocompleteHistoryManager::OnGetAutocompleteSuggestions( base::WeakPtr<SuggestionsHandler> handler) { CancelPendingQueries(handler.get()); - if (!is_autocomplete_enabled || form_control_type == "textarea" || + if (!IsMeaningfulFieldName(name) || !is_autocomplete_enabled || + form_control_type == "textarea" || IsInAutofillSuggestionsDisabledExperiment()) { SendSuggestions({}, QueryHandler(query_id, autoselect_first_suggestion, prefix, handler)); @@ -372,7 +391,8 @@ bool AutocompleteHistoryManager::IsFieldValueSaveable( } } - return is_value_valid && !field.name.empty() && IsTextField(field) && + return IsMeaningfulFieldName(field.name) && is_value_valid && + !field.name.empty() && IsTextField(field) && field.should_autocomplete && !IsValidCreditCardNumber(field.value) && !IsSSN(field.value) && (field.properties_mask & kUserTyped || field.is_focusable) && diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc index 29f9a67bee4..853dc159546 100644 --- a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc @@ -8,10 +8,12 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/run_loop.h" +#include "base/stl_util.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -47,8 +49,11 @@ namespace { class MockAutofillClient : public TestAutofillClient { public: MockAutofillClient() : prefs_(test::PrefServiceForTesting()) {} - ~MockAutofillClient() override {} - PrefService* GetPrefs() override { return prefs_.get(); } + ~MockAutofillClient() override = default; + PrefService* GetPrefs() override { + return const_cast<PrefService*>(base::as_const(*this).GetPrefs()); + } + const PrefService* GetPrefs() const override { return prefs_.get(); } private: std::unique_ptr<PrefService> prefs_; @@ -475,6 +480,79 @@ TEST_F(AutocompleteHistoryManagerTest, std::move(mocked_results)); } +// Tests that no suggestions are queried if the field name is filtered because +// it has a meaningless name. +TEST_F(AutocompleteHistoryManagerTest, + DoQuerySuggestionsForMeaninglessFieldNames_FilterName) { + base::test::ScopedFeatureList scoped_feature; + scoped_feature.InitAndEnableFeature( + features::kAutocompleteFilterForMeaningfulNames); + + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + int test_query_id = 2; + auto test_name = ASCIIToUTF16("input_123"); + auto test_prefix = ASCIIToUTF16(""); + + // Only expect a call when the name is not filtered out. + EXPECT_CALL(*web_data_service_, + GetFormValuesForElementName(test_name, test_prefix, _, + autocomplete_manager_.get())) + .Times(0); + + // Simulate request for suggestions. + autocomplete_manager_->OnGetAutocompleteSuggestions( + test_query_id, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_name, test_prefix, + "Some Type", suggestions_handler->GetWeakPtr()); + + // Setting up mock to verify that DB response does not trigger a call to the + // handler's OnSuggestionsReturned. + EXPECT_CALL(*suggestions_handler.get(), + OnSuggestionsReturned(test_query_id, + /*autoselect_first_suggestion=*/false, _)) + .Times(0); +} + +// Tests that the suggestions are queried if the field name is not filtered +// because the field's name is meaningful. +TEST_F(AutocompleteHistoryManagerTest, + DoQuerySuggestionsForMeaninglessFieldNames_PassName) { + base::test::ScopedFeatureList scoped_feature; + scoped_feature.InitAndEnableFeature( + features::kAutocompleteFilterForMeaningfulNames); + + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + int test_query_id = 2; + auto test_name = ASCIIToUTF16("addressline_1"); + auto test_prefix = ASCIIToUTF16(""); + int mocked_db_query_id = 100; + + std::vector<AutofillEntry> expected_values; + + std::unique_ptr<WDTypedResult> mocked_results = + GetMockedDbResults(expected_values); + + // Expect a call because the name is not filtered. + EXPECT_CALL(*web_data_service_, + GetFormValuesForElementName(test_name, test_prefix, _, + autocomplete_manager_.get())) + .WillOnce(Return(mocked_db_query_id)); + + // Simulate request for suggestions. + autocomplete_manager_->OnGetAutocompleteSuggestions( + test_query_id, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_name, test_prefix, + "Some Type", suggestions_handler->GetWeakPtr()); + + // Setting up mock to verify that DB response triggers a call to the handler's + EXPECT_CALL(*suggestions_handler.get(), + OnSuggestionsReturned(test_query_id, + /*autoselect_first_suggestion=*/false, _)); + + autocomplete_manager_->OnWebDataServiceRequestDone(mocked_db_query_id, + std::move(mocked_results)); +} + TEST_F(AutocompleteHistoryManagerTest, SuggestionsReturned_InvokeHandler_SingleValue) { int mocked_db_query_id = 100; diff --git a/chromium/components/autofill/core/browser/autofill_browser_util.cc b/chromium/components/autofill/core/browser/autofill_browser_util.cc index 7d5ab372f01..eafd2ccab31 100644 --- a/chromium/components/autofill/core/browser/autofill_browser_util.cc +++ b/chromium/components/autofill/core/browser/autofill_browser_util.cc @@ -14,12 +14,9 @@ bool IsInsecureFormAction(const GURL& action_url) { // to same-origin contexts, so they are not blocked. Some forms use // javascript URLs to handle submissions in JS, those don't count as mixed // content either. - // The data scheme is explicitly allowed in order to match blink's equivalent - // check, since IsUrlPotentiallyTrustworthy excludes it. if (action_url.SchemeIs(url::kJavaScriptScheme) || action_url.SchemeIs(url::kBlobScheme) || - action_url.SchemeIs(url::kFileSystemScheme) || - action_url.SchemeIs(url::kDataScheme)) { + action_url.SchemeIs(url::kFileSystemScheme)) { return false; } return !network::IsUrlPotentiallyTrustworthy(action_url); @@ -28,17 +25,18 @@ bool IsInsecureFormAction(const GURL& action_url) { namespace autofill { -bool IsFormOrClientNonSecure(AutofillClient* client, const FormData& form) { +bool IsFormOrClientNonSecure(const AutofillClient* client, + const FormData& form) { return !client->IsContextSecure() || (form.action.is_valid() && form.action.SchemeIs("http")); } -bool IsFormMixedContent(AutofillClient* client, const FormData& form) { +bool IsFormMixedContent(const AutofillClient* client, const FormData& form) { return client->IsContextSecure() && (form.action.is_valid() && IsInsecureFormAction(form.action)); } -bool ShouldAllowCreditCardFallbacks(AutofillClient* client, +bool ShouldAllowCreditCardFallbacks(const AutofillClient* client, const FormData& form) { // Skip the form check if there wasn't a form yet: if (form.unique_renderer_id.is_null()) diff --git a/chromium/components/autofill/core/browser/autofill_browser_util.h b/chromium/components/autofill/core/browser/autofill_browser_util.h index c03cbe8f5a1..e03b14408a3 100644 --- a/chromium/components/autofill/core/browser/autofill_browser_util.h +++ b/chromium/components/autofill/core/browser/autofill_browser_util.h @@ -14,15 +14,16 @@ namespace autofill { class AutofillClient; // Checks whether a given form is considered insecure (by origin or action). -bool IsFormOrClientNonSecure(AutofillClient* client, const FormData& form); +bool IsFormOrClientNonSecure(const AutofillClient* client, + const FormData& form); // Checks whether a given form is considered mixed content. A form is mixed // content if is displayed on a secure context, but submits to an insecure one. -bool IsFormMixedContent(AutofillClient* client, const FormData& form); +bool IsFormMixedContent(const AutofillClient* client, const FormData& form); // Returns true if context provided by the client and the given form are // considered "secure enough" to manually fill credit card data. -bool ShouldAllowCreditCardFallbacks(AutofillClient* client, +bool ShouldAllowCreditCardFallbacks(const AutofillClient* client, const FormData& form); } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_client.cc b/chromium/components/autofill/core/browser/autofill_client.cc index 54d977b408b..05229af8b02 100644 --- a/chromium/components/autofill/core/browser/autofill_client.cc +++ b/chromium/components/autofill/core/browser/autofill_client.cc @@ -4,6 +4,7 @@ #include "components/autofill/core/browser/autofill_client.h" +#include "base/stl_util.h" #include "components/autofill/core/browser/ui/suggestion.h" #include "components/version_info/channel.h" @@ -43,6 +44,12 @@ std::string AutofillClient::GetVariationConfigCountryCode() const { return std::string(); } +profile_metrics::BrowserProfileType AutofillClient::GetProfileType() const { + // This is an abstract interface and thus never instantiated directly, + // therefore it is safe to always return |kRegular| here. + return profile_metrics::BrowserProfileType::kRegular; +} + #if !defined(OS_IOS) std::unique_ptr<InternalAuthenticator> AutofillClient::CreateCreditCardInternalAuthenticator( @@ -51,6 +58,14 @@ AutofillClient::CreateCreditCardInternalAuthenticator( } #endif +void AutofillClient::ShowOfferNotificationIfApplicable( + const std::vector<GURL>& domains_to_display_bubble, + const GURL& offer_details_url, + const CreditCard* card) { + // This is overridden by platform subclasses. Currently only + // ChromeAutofillClient (Chrome Desktop and Clank) implement this. +} + LogManager* AutofillClient::GetLogManager() const { return nullptr; } diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h index d8bb5ce5779..1a8c8b2f652 100644 --- a/chromium/components/autofill/core/browser/autofill_client.h +++ b/chromium/components/autofill/core/browser/autofill_client.h @@ -15,13 +15,14 @@ #include "base/i18n/rtl.h" #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" -#include "base/util/type_safety/strong_alias.h" +#include "base/types/strong_alias.h" #include "base/values.h" #include "build/build_config.h" #include "components/autofill/core/browser/payments/legal_message_line.h" #include "components/autofill/core/browser/payments/risk_data_loader.h" #include "components/autofill/core/browser/ui/popup_item_ids.h" #include "components/autofill/core/browser/ui/popup_types.h" +#include "components/profile_metrics/browser_profile_type.h" #include "components/security_state/core/security_state.h" #include "components/translate/core/browser/language_state.h" #include "services/metrics/public/cpp/ukm_source_id.h" @@ -58,6 +59,7 @@ enum class Channel; namespace autofill { class AddressNormalizer; +class AutofillProfile; class AutocompleteHistoryManager; class AutofillOfferManager; class AutofillPopupDelegate; @@ -134,6 +136,13 @@ class AutofillClient : public RiskDataLoader { FIDO = 2, }; + enum class SaveAddressProfileOfferUserDecision { + kAccepted, + kEdited, + kDeclined, + kIgnored, + }; + // Used for explicitly requesting the user to enter/confirm cardholder name, // expiration date month and year. struct UserProvidedCardDetails { @@ -180,7 +189,7 @@ class AutofillClient : public RiskDataLoader { // Required arguments to create a dropdown showing autofill suggestions. struct PopupOpenArgs { using AutoselectFirstSuggestion = - ::util::StrongAlias<class AutoSelectFirstSuggestionTag, bool>; + ::base::StrongAlias<class AutoSelectFirstSuggestionTag, bool>; PopupOpenArgs(); PopupOpenArgs(const gfx::RectF& element_bounds, @@ -235,6 +244,10 @@ class AutofillClient : public RiskDataLoader { typedef base::RepeatingCallback<void(WebauthnDialogCallbackType)> WebauthnDialogCallback; + using AddressProfileSavePromptCallback = + base::OnceCallback<void(SaveAddressProfileOfferUserDecision, + autofill::AutofillProfile profile)>; + ~AutofillClient() override = default; // Returns the channel for the installation. In branded builds, this will be @@ -251,6 +264,7 @@ class AutofillClient : public RiskDataLoader { // Gets the preferences associated with the client. virtual PrefService* GetPrefs() = 0; + virtual const PrefService* GetPrefs() const = 0; // Gets the sync service associated with the client. virtual syncer::SyncService* GetSyncService() = 0; @@ -282,7 +296,7 @@ class AutofillClient : public RiskDataLoader { // Gets the virtual URL of the last committed page of this client's // associated WebContents. - virtual const GURL& GetLastCommittedURL() = 0; + virtual const GURL& GetLastCommittedURL() const = 0; // Gets the security level used for recording histograms for the current // context if possible, SECURITY_LEVEL_COUNT otherwise. @@ -291,10 +305,19 @@ class AutofillClient : public RiskDataLoader { // Returns the language state, if available. virtual const translate::LanguageState* GetLanguageState() = 0; + // Returns the translate driver, if available, which is used to observe the + // page language for language-dependent heuristics. + virtual translate::TranslateDriver* GetTranslateDriver() = 0; + // Retrieves the country code of the user from Chrome variation service. // If the variation service is not available, return an empty string. virtual std::string GetVariationConfigCountryCode() const; + // Returns the profile type of the session. + // TODO(https://crbug.com/1169142): Replace by getting profile type directly + // from BrowserContext. + virtual profile_metrics::BrowserProfileType GetProfileType() const; + #if !defined(OS_IOS) // Creates the appropriate implementation of InternalAuthenticator. May be // null for platforms that don't support this, in which case standard CVC @@ -434,6 +457,12 @@ class AutofillClient : public RiskDataLoader { virtual void ConfirmCreditCardFillAssist(const CreditCard& card, base::OnceClosure callback) = 0; + // Shows the offer-to-save address profile bubble. Runs |callback| once the + // user makes a decision with respect to the offer-to-save prompt. + virtual void ConfirmSaveAddressProfile( + const AutofillProfile& profile, + AddressProfileSavePromptCallback callback) = 0; + // Returns true if both the platform and the device support scanning credit // cards. Should be called before ScanCreditCard(). virtual bool HasCreditCardScanFeature() = 0; @@ -475,6 +504,21 @@ class AutofillClient : public RiskDataLoader { // Hide the Autofill popup if one is currently showing. virtual void HideAutofillPopup(PopupHidingReason reason) = 0; + // TODO(crbug.com/1093057): Rename all the "domain" in this flow to origin. + // The server is passing down full origin of the + // urls. "Domain" is no longer accurate. + // Will show a bubble or infobar indicating that the current web domain has an + // eligible offer or reward if no other notification bubble is currently + // visible. See bubble controller for details. The bubble is sticky over a set + // of domains given in |domains_to_display_bubble|. The bubble displays the + // information of the |card| if the offer is card-related. On mobile, the + // bubble also shows the |offer_details_url| as a link which has more + // information about the offer. + virtual void ShowOfferNotificationIfApplicable( + const std::vector<GURL>& domains_to_display_bubble, + const GURL& offer_details_url, + const CreditCard* card); + // Whether the Autocomplete feature of Autofill should be enabled. virtual bool IsAutocompleteEnabled() = 0; @@ -490,14 +534,14 @@ class AutofillClient : public RiskDataLoader { const base::string16& profile_full_name) = 0; // If the context is secure. - virtual bool IsContextSecure() = 0; + virtual bool IsContextSecure() const = 0; // Whether it is appropriate to show a signin promo for this user. virtual bool ShouldShowSigninPromo() = 0; // Whether server side cards are supported by the client. If false, only // local cards will be shown. - virtual bool AreServerCardsSupported() = 0; + virtual bool AreServerCardsSupported() const = 0; // Handles simple actions for the autofill popups. virtual void ExecuteCommand(int id) = 0; diff --git a/chromium/components/autofill/core/browser/autofill_data_util.cc b/chromium/components/autofill/core/browser/autofill_data_util.cc index d8b5520cfc4..ab3195e97b5 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util.cc +++ b/chromium/components/autofill/core/browser/autofill_data_util.cc @@ -81,24 +81,23 @@ const char* const family_name_prefixes[] = {"d'", "de", "del", "den", "der", // The common and non-ambiguous CJK surnames (last names) that have more than // one character. const char* common_cjk_multi_char_surnames[] = { - // Korean, taken from the list of surnames: - // https://ko.wikipedia.org/wiki/%ED%95%9C%EA%B5%AD%EC%9D%98_%EC%84%B1%EC%94%A8_%EB%AA%A9%EB%A1%9D - "남궁", "사공", "서문", "선우", "제갈", "황보", "독고", "망절", - - // Chinese, taken from the top 10 Chinese 2-character surnames: - // https://zh.wikipedia.org/wiki/%E8%A4%87%E5%A7%93#.E5.B8.B8.E8.A6.8B.E7.9A.84.E8.A4.87.E5.A7.93 - // Simplified Chinese (mostly mainland China) - "欧阳", "令狐", "皇甫", "上官", "司徒", "诸葛", "司马", "宇文", "呼延", "端木", - // Traditional Chinese (mostly Taiwan) - "張簡", "歐陽", "諸葛", "申屠", "尉遲", "司馬", "軒轅", "夏侯" -}; + // Korean, taken from the list of surnames: + // https://ko.wikipedia.org/wiki/%ED%95%9C%EA%B5%AD%EC%9D%98_%EC%84%B1%EC%94%A8_%EB%AA%A9%EB%A1%9D + "남궁", "사공", "서문", "선우", "제갈", "황보", "독고", "망절", + + // Chinese, taken from the top 10 Chinese 2-character surnames: + // https://zh.wikipedia.org/wiki/%E8%A4%87%E5%A7%93#.E5.B8.B8.E8.A6.8B.E7.9A.84.E8.A4.87.E5.A7.93 + // Simplified Chinese (mostly mainland China) + "欧阳", "令狐", "皇甫", "上官", "司徒", "诸葛", "司马", "宇文", "呼延", + "端木", + // Traditional Chinese (mostly Taiwan) + "張簡", "歐陽", "諸葛", "申屠", "尉遲", "司馬", "軒轅", "夏侯"}; // All Korean surnames that have more than one character, even the // rare/ambiguous ones. const char* korean_multi_char_surnames[] = { - "강전", "남궁", "독고", "동방", "망절", "사공", "서문", "선우", - "소봉", "어금", "장곡", "제갈", "황목", "황보" -}; + "강전", "남궁", "독고", "동방", "망절", "사공", "서문", + "선우", "소봉", "어금", "장곡", "제갈", "황목", "황보"}; // Returns true if |set| contains |element|, modulo a final period. bool ContainsString(const char* const set[], @@ -146,7 +145,8 @@ void StripSuffixes(std::vector<base::StringPiece16>* name_tokens) { // Find whether |name| starts with any of the strings from the array // |prefixes|. The returned value is the length of the prefix found, or 0 if // none is found. -size_t StartsWithAny(base::StringPiece16 name, const char** prefixes, +size_t StartsWithAny(base::StringPiece16 name, + const char** prefixes, size_t prefix_count) { base::string16 buffer; for (size_t i = 0; i < prefix_count; i++) { @@ -250,16 +250,16 @@ void AddGroupToBitmask(uint32_t* group_bitmask, ServerFieldType type) { const FieldTypeGroup group = AutofillType(AutofillType(type).GetStorableType()).group(); switch (group) { - case autofill::NAME: + case autofill::FieldTypeGroup::kName: *group_bitmask |= kName; break; - case autofill::ADDRESS_HOME: + case autofill::FieldTypeGroup::kAddressHome: *group_bitmask |= kAddress; break; - case autofill::EMAIL: + case autofill::FieldTypeGroup::kEmail: *group_bitmask |= kEmail; break; - case autofill::PHONE_HOME: + case autofill::FieldTypeGroup::kPhoneHome: *group_bitmask |= kPhone; break; default: @@ -383,12 +383,12 @@ bool IsCJKName(base::StringPiece16 name) { NameParts SplitName(base::StringPiece16 name) { static const base::char16 kWordSeparators[] = { - u' ', // ASCII space. - u',', // ASCII comma. - u'\u3000', // 'IDEOGRAPHIC SPACE' (U+3000). - u'\u30FB', // 'KATAKANA MIDDLE DOT' (U+30FB). - u'\u00B7', // 'MIDDLE DOT' (U+00B7). - u'\0' // End of string. + u' ', // ASCII space. + u',', // ASCII comma. + u'\u3000', // 'IDEOGRAPHIC SPACE' (U+3000). + u'\u30FB', // 'KATAKANA MIDDLE DOT' (U+30FB). + u'\u00B7', // 'MIDDLE DOT' (U+00B7). + u'\0' // End of string. }; std::vector<base::StringPiece16> name_tokens = base::SplitStringPiece( name, kWordSeparators, base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); diff --git a/chromium/components/autofill/core/browser/autofill_data_util.h b/chromium/components/autofill/core/browser/autofill_data_util.h index cef371d6983..144a1acd942 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util.h +++ b/chromium/components/autofill/core/browser/autofill_data_util.h @@ -28,15 +28,15 @@ struct NameParts { namespace bit_field_type_groups { // Bits for FieldTypeGroup options. -// The form has a field associated with the NAME_HOME or NAME_BILLING +// The form has a field associated with the kName or kNameBilling // FieldTypeGroups. constexpr uint32_t kName = 1 << 0; -// The form has a field associated with the ADDRESS_HOME or ADDRESS_BILLING +// The form has a field associated with the :kAddressHome or kAddressBilling // FieldTypeGroups. constexpr uint32_t kAddress = 1 << 1; -// The form has a field associated with the EMAIL FieldTypeGroup. +// The form has a field associated with the kEmail FieldTypeGroup. constexpr uint32_t kEmail = 1 << 2; -// The form has a field associated with the PHONE_HOME or PHONE_BILLING +// The form has a field associated with the kPhoneHome or kPhoneBilling // FieldTypeGroups. constexpr uint32_t kPhone = 1 << 3; diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.cc b/chromium/components/autofill/core/browser/autofill_download_manager.cc index e356433a2f8..097f5b9db03 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager.cc @@ -590,10 +590,12 @@ ScopedActiveAutofillExperiments::~ScopedActiveAutofillExperiments() { std::vector<variations::VariationID>* AutofillDownloadManager::active_experiments_ = nullptr; -AutofillDownloadManager::AutofillDownloadManager(AutofillDriver* driver, - Observer* observer, - const std::string& api_key, - LogManager* log_manager) +AutofillDownloadManager::AutofillDownloadManager( + AutofillDriver* driver, + Observer* observer, + const std::string& api_key, + IsRawMetadataUploadingEnabled is_raw_metadata_uploading_enabled, + LogManager* log_manager) : driver_(driver), observer_(observer), api_key_(api_key), @@ -601,7 +603,8 @@ AutofillDownloadManager::AutofillDownloadManager(AutofillDriver* driver, autofill_server_url_(GetAutofillServerURL()), throttle_reset_period_(GetThrottleResetPeriod()), max_form_cache_size_(kAutofillDownloadManagerMaxFormCacheSize), - loader_backoff_(&kAutofillBackoffPolicy) { + loader_backoff_(&kAutofillBackoffPolicy), + is_raw_metadata_uploading_enabled_(is_raw_metadata_uploading_enabled) { DCHECK(observer_); } @@ -610,6 +613,7 @@ AutofillDownloadManager::AutofillDownloadManager(AutofillDriver* driver, : AutofillDownloadManager(driver, observer, kDefaultAPIKey, + IsRawMetadataUploadingEnabled(false), /*log_manager=*/nullptr) {} AutofillDownloadManager::~AutofillDownloadManager() = default; @@ -706,7 +710,8 @@ bool AutofillDownloadManager::StartUploadRequest( std::vector<FormSignature> form_signatures; if (!form.EncodeUploadRequest(available_field_types, form_was_autofilled, login_form_signature, observed_submission, - &upload, &form_signatures)) { + is_raw_metadata_uploading_enabled_, &upload, + &form_signatures)) { return false; } diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.h b/chromium/components/autofill/core/browser/autofill_download_manager.h index 8aaececf094..afd5e4dca0a 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager.h +++ b/chromium/components/autofill/core/browser/autofill_download_manager.h @@ -19,6 +19,7 @@ #include "base/memory/weak_ptr.h" #include "base/strings/string_piece.h" #include "base/time/time.h" +#include "base/types/strong_alias.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/common/signatures.h" @@ -50,6 +51,8 @@ class AutofillDownloadManager { REQUEST_QUERY, REQUEST_UPLOAD, }; + using IsRawMetadataUploadingEnabled = + base::StrongAlias<class IsRawMetadataUploadingEnabledTag, bool>; // An interface used to notify clients of AutofillDownloadManager. class Observer { @@ -79,16 +82,18 @@ class AutofillDownloadManager { // |driver| must outlive this instance. // |observer| - observer to notify on successful completion or error. - // Uses an API callback function that gives an empty string. - AutofillDownloadManager(AutofillDriver* driver, Observer* observer); - // |driver| must outlive this instance. - // |observer| - observer to notify on successful completion or error. // |api_key| - API key to add to API request query parameters. Will only take // effect if using API. - AutofillDownloadManager(AutofillDriver* driver, - Observer* observer, - const std::string& api_key, - LogManager* log_manager); + AutofillDownloadManager( + AutofillDriver* driver, + Observer* observer, + const std::string& api_key, + IsRawMetadataUploadingEnabled is_raw_metadata_uploading_enabled, + LogManager* log_manager); + // |driver| must outlive this instance. + // |observer| - observer to notify on successful completion or error. + // Uses an API callback function that gives an empty string. + AutofillDownloadManager(AutofillDriver* driver, Observer* observer); virtual ~AutofillDownloadManager(); // Starts a query request to Autofill servers. The observer is called with the @@ -225,6 +230,10 @@ class AutofillDownloadManager { // Used for exponential backoff of requests. net::BackoffEntry loader_backoff_; + // Whether form data (e.g. form and field names) can be uploaded in clear + // text. + bool is_raw_metadata_uploading_enabled_ = false; + base::WeakPtrFactory<AutofillDownloadManager> weak_factory_{this}; }; diff --git a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc index 923f8c9071c..bf06e7d2e2f 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc @@ -14,6 +14,7 @@ #include "base/base64url.h" #include "base/bind.h" #include "base/format_macros.h" +#include "base/numerics/safe_conversions.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -84,23 +85,15 @@ std::vector<FormStructure*> ToRawPointerVector( // the response body data is utilized. std::string GetStringFromDataElements( const std::vector<network::DataElement>* data_elements) { - network::DataElement unified_data_element; - auto data_elements_it = data_elements->begin(); - if (data_elements_it != data_elements->end()) { - unified_data_element.SetToBytes(data_elements_it->bytes(), - data_elements_it->length()); + std::string result; + for (const network::DataElement& e : *data_elements) { + DCHECK_EQ(e.type(), network::DataElement::Tag::kBytes); + // Provide the length of the bytes explicitly, not to rely on the null + // termination. + const auto piece = e.As<network::DataElementBytes>().AsStringPiece(); + result.append(piece.data(), piece.size()); } - ++data_elements_it; - while (data_elements_it != data_elements->end()) { - unified_data_element.AppendBytes(data_elements_it->bytes(), - data_elements_it->length()); - ++data_elements_it; - } - // Using the std::string constructor with length ensures that we don't rely - // on having a termination character to delimit the string. This is the - // safest approach. - return std::string(unified_data_element.bytes(), - unified_data_element.length()); + return result; } // Gets the AutofillUploadRequest proto from the HTTP loader request payload. @@ -165,10 +158,12 @@ class AutofillDownloadManagerWithCustomPayloadSize Observer* observer, const std::string& api_key, size_t length) - : AutofillDownloadManager(driver, - observer, - api_key, - /*log_manager=*/nullptr), + : AutofillDownloadManager( + driver, + observer, + api_key, + AutofillDownloadManager::IsRawMetadataUploadingEnabled(false), + /*log_manager=*/nullptr), length_(length) {} protected: @@ -350,8 +345,10 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) { form_structures.push_back(std::make_unique<FormStructure>(form)); // Make download manager. - AutofillDownloadManager download_manager(&driver_, this, "dummykey", - /*log_manager=*/nullptr); + AutofillDownloadManager download_manager( + &driver_, this, "dummykey", + AutofillDownloadManager::IsRawMetadataUploadingEnabled(false), + /*log_manager=*/nullptr); // Request with id 0. base::HistogramTester histogram; @@ -540,8 +537,10 @@ TEST_F(AutofillDownloadManagerTest, QueryAPITest) { std::vector<std::unique_ptr<FormStructure>> form_structures; form_structures.push_back(std::make_unique<FormStructure>(form)); - AutofillDownloadManager download_manager(&driver_, this, "dummykey", - /*log_manager=*/nullptr); + AutofillDownloadManager download_manager( + &driver_, this, "dummykey", + AutofillDownloadManager::IsRawMetadataUploadingEnabled(false), + /*log_manager=*/nullptr); // Start the query request and look if it is successful. No response was // received yet. @@ -729,7 +728,7 @@ TEST_F(AutofillDownloadManagerTest, UploadToAPITest) { // We don't want upload throttling for testing purpose. {features::kAutofillUploadThrottling}); - // Build the form structures that we want to query. + // Build the form structures that we want to upload. FormData form; FormFieldData field; @@ -746,8 +745,10 @@ TEST_F(AutofillDownloadManagerTest, UploadToAPITest) { form_structure.set_submission_source(SubmissionSource::FORM_SUBMISSION); std::unique_ptr<PrefService> pref_service = test::PrefServiceForTesting(); - AutofillDownloadManager download_manager(&driver_, this, "dummykey", - /*log_manager=*/nullptr); + AutofillDownloadManager download_manager( + &driver_, this, "dummykey", + AutofillDownloadManager::IsRawMetadataUploadingEnabled(false), + /*log_manager=*/nullptr); EXPECT_TRUE(download_manager.StartUploadRequest(form_structure, true, ServerFieldTypeSet(), "", true, pref_service.get())); @@ -793,6 +794,80 @@ TEST_F(AutofillDownloadManagerTest, UploadToAPITest) { net::HTTP_OK, 1); } +TEST_F(AutofillDownloadManagerTest, UploadWithRawMetadata) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + // Enabled + {}, + // Disabled + // We don't want upload throttling for testing purpose. + {features::kAutofillUploadThrottling}); + + for (bool is_raw_metadata_uploading_enabled : {false, true}) { + SCOPED_TRACE(testing::Message() << "is_raw_metadata_uploading_enabled = " + << is_raw_metadata_uploading_enabled); + // Build the form structures that we want to upload. + FormData form; + form.name = UTF8ToUTF16("form1"); + FormFieldData field; + + field.name = UTF8ToUTF16("firstname"); + field.form_control_type = "text"; + form.fields.push_back(field); + + field.name = UTF8ToUTF16("lastname"); + field.form_control_type = "text"; + form.fields.push_back(field); + FormStructure form_structure(form); + form_structure.set_submission_source(SubmissionSource::FORM_SUBMISSION); + + std::unique_ptr<PrefService> pref_service = test::PrefServiceForTesting(); + AutofillDownloadManager download_manager( + &driver_, this, "dummykey", + AutofillDownloadManager::IsRawMetadataUploadingEnabled( + is_raw_metadata_uploading_enabled), + /*log_manager=*/nullptr); + EXPECT_TRUE(download_manager.StartUploadRequest(form_structure, true, + ServerFieldTypeSet(), "", + true, pref_service.get())); + + // Inspect the request that the test URL loader sent. + ASSERT_EQ(1, test_url_loader_factory_.NumPending()); + network::TestURLLoaderFactory::PendingRequest* request = + test_url_loader_factory_.GetPendingRequest(0); + + // Assert some of the fields within the uploaded proto to make sure it was + // filled with something else than default data. + AutofillUploadRequest autofill_upload_request; + EXPECT_TRUE( + GetUploadRequestProtoFromRequest(request, &autofill_upload_request)); + AutofillUploadContents upload = autofill_upload_request.upload(); + EXPECT_GT(upload.client_version().size(), 0U); + EXPECT_EQ(FormSignature(upload.form_signature()), + form_structure.form_signature()); + // Only a few strings are tested, full testing happens in FormStructure's + // tests. + ASSERT_EQ(is_raw_metadata_uploading_enabled, upload.has_form_name()); + ASSERT_EQ(is_raw_metadata_uploading_enabled, upload.field()[0].has_name()); + ASSERT_EQ(is_raw_metadata_uploading_enabled, upload.field()[1].has_type()); + if (is_raw_metadata_uploading_enabled) { + EXPECT_EQ(form.name, UTF8ToUTF16(upload.form_name())); + EXPECT_EQ(form.fields[0].name, UTF8ToUTF16(upload.field()[0].name())); + EXPECT_EQ(form.fields[1].form_control_type, upload.field()[1].type()); + } + + test_url_loader_factory_.SimulateResponseForPendingRequest( + request->request.url.spec(), ""); + EXPECT_EQ(1U, responses_.size()); + EXPECT_EQ(AutofillDownloadManagerTest::UPLOAD_SUCCESSFULL, + responses_.front().type_of_response); + + ASSERT_EQ(0, test_url_loader_factory_.NumPending()); + test_url_loader_factory_.ClearResponses(); + responses_.clear(); + } +} + TEST_F(AutofillDownloadManagerTest, BackoffLogic_Query) { FormData form; FormFieldData field; @@ -1946,7 +2021,7 @@ TEST_P(AutofillUploadTest, RichMetadata) { AutofillDownloadManager download_manager(driver_.get(), this); FormStructure form_structure(form); - form_structure.set_page_language("fr-ca"); + form_structure.set_current_page_language(LanguageCode("fr")); pref_service_->SetBoolean( RandomizedEncoder::kUrlKeyedAnonymizedDataCollectionEnabled, true); @@ -1983,7 +2058,8 @@ TEST_P(AutofillUploadTest, RichMetadata) { ASSERT_TRUE(request.ParseFromString(payloads_.front())); ASSERT_TRUE(request.has_upload()); const AutofillUploadContents& upload = request.upload(); - EXPECT_EQ(upload.language(), form_structure.page_language()); + EXPECT_EQ(upload.language(), + form_structure.current_page_language().value()); ASSERT_TRUE(upload.has_randomized_form_metadata()); EXPECT_TRUE(upload.randomized_form_metadata().has_id()); EXPECT_TRUE(upload.randomized_form_metadata().has_name()); diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc index 3b1c49daf94..0217bbc8696 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/check.h" #include "base/command_line.h" +#include "base/containers/contains.h" #include "base/feature_list.h" #include "base/i18n/case_conversion.h" #include "base/notreached.h" diff --git a/chromium/components/autofill/core/browser/autofill_field.cc b/chromium/components/autofill/core/browser/autofill_field.cc index 41610aa4482..f563c4a4c90 100644 --- a/chromium/components/autofill/core/browser/autofill_field.cc +++ b/chromium/components/autofill/core/browser/autofill_field.cc @@ -20,11 +20,20 @@ AutofillField::AutofillField() = default; AutofillField::AutofillField(FieldSignature field_signature) : field_signature_(field_signature) {} +AutofillField::AutofillField(const FormFieldData& field) + : FormFieldData(field), + parseable_name_(field.name), + parseable_label_(field.label) { + field_signature_ = + CalculateFieldSignatureByNameAndType(name, form_control_type); +} + AutofillField::AutofillField(const FormFieldData& field, const base::string16& unique_name) : FormFieldData(field), unique_name_(unique_name), - parseable_name_(field.name) { + parseable_name_(field.name), + parseable_label_(field.label) { field_signature_ = CalculateFieldSignatureByNameAndType(name, form_control_type); } @@ -96,10 +105,14 @@ void AutofillField::SetTypeTo(const AutofillType& type) { AutofillType AutofillField::ComputedType() const { // If autocomplete=tel/tel-* and server confirms it really is a phone field, // we always user the server prediction as html types are not very reliable. - if ((GroupTypeOfHtmlFieldType(html_type_, html_mode_) == PHONE_BILLING || - GroupTypeOfHtmlFieldType(html_type_, html_mode_) == PHONE_HOME) && - (GroupTypeOfServerFieldType(server_type_) == PHONE_BILLING || - GroupTypeOfServerFieldType(server_type_) == PHONE_HOME)) { + if ((GroupTypeOfHtmlFieldType(html_type_, html_mode_) == + FieldTypeGroup::kPhoneBilling || + GroupTypeOfHtmlFieldType(html_type_, html_mode_) == + FieldTypeGroup::kPhoneHome) && + (GroupTypeOfServerFieldType(server_type_) == + FieldTypeGroup::kPhoneBilling || + GroupTypeOfServerFieldType(server_type_) == + FieldTypeGroup::kPhoneHome)) { return AutofillType(server_type_); } @@ -140,9 +153,10 @@ AutofillType AutofillField::ComputedType() const { // Either way, retain a preference for the the CVC heuristic over the // server's password predictions (http://crbug.com/469007) - believe_server = believe_server && - !(AutofillType(server_type_).group() == PASSWORD_FIELD && - heuristic_type_ == CREDIT_CARD_VERIFICATION_CODE); + believe_server = + believe_server && !(AutofillType(server_type_).group() == + FieldTypeGroup::kPasswordField && + heuristic_type_ == CREDIT_CARD_VERIFICATION_CODE); // For new name tokens the heuristic predictions get precedence over the // server predictions. @@ -172,6 +186,14 @@ AutofillType AutofillField::ComputedType() const { } AutofillType AutofillField::Type() const { + // If the corresponding feature is enabled, server predictions that are an + // override are granted precedence unconditionally. + if (base::FeatureList::IsEnabled( + features::kAutofillServerTypeTakesPrecedence) && + server_type_prediction_is_override_ && server_type_ != NO_SERVER_DATA) { + return AutofillType(server_type_); + } + if (overall_type_.GetStorableType() != NO_SERVER_DATA) { return overall_type_; } @@ -214,8 +236,8 @@ void AutofillField::NormalizePossibleTypesValidities() { } bool AutofillField::IsCreditCardPrediction() const { - return AutofillType(server_type_).group() == CREDIT_CARD || - AutofillType(heuristic_type_).group() == CREDIT_CARD; + return AutofillType(server_type_).group() == FieldTypeGroup::kCreditCard || + AutofillType(heuristic_type_).group() == FieldTypeGroup::kCreditCard; } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_field.h b/chromium/components/autofill/core/browser/autofill_field.h index 4f72790ea37..f3971ad3788 100644 --- a/chromium/components/autofill/core/browser/autofill_field.h +++ b/chromium/components/autofill/core/browser/autofill_field.h @@ -40,6 +40,7 @@ class AutofillField : public FormFieldData { }; AutofillField(); + explicit AutofillField(const FormFieldData& field); AutofillField(const FormFieldData& field, const base::string16& unique_name); virtual ~AutofillField(); @@ -49,10 +50,16 @@ class AutofillField : public FormFieldData { static std::unique_ptr<AutofillField> CreateForPasswordManagerUpload( FieldSignature field_signature); + // Unique names are not stable across dynamic change. Use renderer IDs instead + // if possible. + // TODO(crbug/896689): Remove unique_name. const base::string16& unique_name() const { return unique_name_; } ServerFieldType heuristic_type() const { return heuristic_type_; } ServerFieldType server_type() const { return server_type_; } + bool server_type_prediction_is_override() const { + return server_type_prediction_is_override_; + } const std::vector< AutofillQueryResponse::FormSuggestion::FieldSuggestion::FieldPrediction>& server_predictions() const { @@ -70,11 +77,16 @@ class AutofillField : public FormFieldData { PhonePart phone_part() const { return phone_part_; } bool previously_autofilled() const { return previously_autofilled_; } const base::string16& parseable_name() const { return parseable_name_; } + const base::string16& parseable_label() const { return parseable_label_; } bool only_fill_when_focused() const { return only_fill_when_focused_; } // Setters for the detected types. void set_heuristic_type(ServerFieldType type); void set_server_type(ServerFieldType type); + // Setter for the indicator that the server prediction is an override. + void set_server_type_prediction_is_override(bool is_override) { + server_type_prediction_is_override_ = is_override; + } void add_possible_types_validities( const ServerFieldTypeValidityStateMap& possible_types_validities); void set_server_predictions( @@ -103,6 +115,9 @@ class AutofillField : public FormFieldData { void set_parseable_name(const base::string16& parseable_name) { parseable_name_ = parseable_name; } + void set_parseable_label(const base::string16& parseable_label) { + parseable_label_ = parseable_label; + } void set_only_fill_when_focused(bool fill_when_focused) { only_fill_when_focused_ = fill_when_focused; @@ -182,6 +197,14 @@ class AutofillField : public FormFieldData { return password_requirements_; } + // Getter and Setter methods for |state_is_a_matching_type_|. + void set_state_is_a_matching_type(bool value = true) { + state_is_a_matching_type_ = value; + } + const bool& state_is_a_matching_type() const { + return state_is_a_matching_type_; + } + // For each type in |possible_types_| that's missing from // |possible_types_validities_|, will add it to the // |possible_types_validities_| and will set its validity to UNVALIDATED. This @@ -204,6 +227,9 @@ class AutofillField : public FormFieldData { // The type of the field, as determined by the Autofill server. ServerFieldType server_type_ = NO_SERVER_DATA; + // Indicates if the server type prediction is an override. + bool server_type_prediction_is_override_ = false; + // The possible types of the field, as determined by the Autofill server, // including |server_type_| as the first item. std::vector< @@ -262,6 +288,10 @@ class AutofillField : public FormFieldData { // parsing. base::string16 parseable_name_; + // The parseable label attribute is potentially only a part of the original + // label when the label is divided between subsequent fields. + base::string16 parseable_label_; + // The type of password generation event, if it happened. AutofillUploadContents::Field::PasswordGenerationType generation_type_ = AutofillUploadContents::Field::NO_GENERATION; @@ -275,6 +305,9 @@ class AutofillField : public FormFieldData { AutofillUploadContents::Field::VoteType vote_type_ = AutofillUploadContents::Field::NO_INFORMATION; + // Denotes if |ADDRESS_HOME_STATE| should be added to |possible_types_|. + bool state_is_a_matching_type_ = false; + DISALLOW_COPY_AND_ASSIGN(AutofillField); }; diff --git a/chromium/components/autofill/core/browser/autofill_form_test_utils.cc b/chromium/components/autofill/core/browser/autofill_form_test_utils.cc index f8889cfce9f..97d4e4c9b00 100644 --- a/chromium/components/autofill/core/browser/autofill_form_test_utils.cc +++ b/chromium/components/autofill/core/browser/autofill_form_test_utils.cc @@ -135,7 +135,7 @@ void FormStructureTest::CheckFormStructureTestData( auto form_structure = std::make_unique<FormStructure>(form); if (test_case.form_flags.determine_heuristic_type) - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); if (test_case.form_flags.is_autofillable) EXPECT_TRUE(form_structure->IsAutofillable()); diff --git a/chromium/components/autofill/core/browser/autofill_handler.cc b/chromium/components/autofill/core/browser/autofill_handler.cc index b8f647955ae..99752491c36 100644 --- a/chromium/components/autofill/core/browser/autofill_handler.cc +++ b/chromium/components/autofill/core/browser/autofill_handler.cc @@ -4,8 +4,11 @@ #include "components/autofill/core/browser/autofill_handler.h" +#include "base/bind.h" #include "base/containers/adapters.h" #include "base/feature_list.h" +#include "base/strings/string_number_conversions.h" +#include "base/threading/thread_task_runner_handle.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/common/autofill_data_validation.h" @@ -13,9 +16,10 @@ #include "components/autofill/core/common/autofill_internals/log_message.h" #include "components/autofill/core/common/autofill_internals/logging_scope.h" #include "components/autofill/core/common/autofill_payments_features.h" +#include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/autofill_tick_clock.h" -#include "components/autofill/core/common/renderer_id.h" -#include "components/autofill/core/common/signatures.h" +#include "components/translate/core/common/language_detection_details.h" +#include "google_apis/google_api_keys.h" #include "ui/gfx/geometry/rect_f.h" namespace autofill { @@ -42,7 +46,8 @@ AutofillField* FindAutofillFillField(const FormStructure& form, return nullptr; } -// Returns true if |live_form| does not match |cached_form|. +// Returns true if |live_form| does not match |cached_form|, assuming that +// |live_form|'s language is |live_form_language|. bool CachedFormNeedsUpdate(const FormData& live_form, const FormStructure& cached_form) { if (live_form.fields.size() != cached_form.field_count()) @@ -56,15 +61,128 @@ bool CachedFormNeedsUpdate(const FormData& live_form, return false; } +std::string GetAPIKeyForUrl(version_info::Channel channel) { + // First look if we can get API key from command line flag. + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kAutofillAPIKey)) + return command_line.GetSwitchValueASCII(switches::kAutofillAPIKey); + + // Get the API key from Chrome baked keys. + if (channel == version_info::Channel::STABLE) + return google_apis::GetAPIKey(); + return google_apis::GetNonStableAPIKey(); +} + } // namespace using base::TimeTicks; -AutofillHandler::AutofillHandler(AutofillDriver* driver, - LogManager* log_manager) - : driver_(driver), log_manager_(log_manager) {} +// static +void AutofillHandler::LogAutofillTypePredictionsAvailable( + LogManager* log_manager, + const std::vector<FormStructure*>& forms) { + if (VLOG_IS_ON(1)) { + VLOG(1) << "Parsed forms:"; + for (FormStructure* form : forms) + VLOG(1) << *form; + } + + if (!log_manager || !log_manager->IsLoggingActive()) + return; + + LogBuffer buffer; + for (FormStructure* form : forms) + buffer << *form; + + log_manager->Log() << LoggingScope::kParsing << LogMessage::kParsedForms + << std::move(buffer); +} + +// static +bool AutofillHandler::IsRichQueryEnabled(version_info::Channel channel) { + return base::FeatureList::IsEnabled(features::kAutofillRichMetadataQueries) && + channel != version_info::Channel::STABLE && + channel != version_info::Channel::BETA; +} -AutofillHandler::~AutofillHandler() = default; +// static +bool AutofillHandler::IsRawMetadataUploadingEnabled( + version_info::Channel channel) { + return channel == version_info::Channel::CANARY || + channel == version_info::Channel::DEV; +} + +AutofillHandler::AutofillHandler( + AutofillDriver* driver, + AutofillClient* client, + AutofillDownloadManagerState enable_download_manager) + : AutofillHandler(driver, + client, + enable_download_manager, + client->GetChannel()) { + DCHECK(driver); + DCHECK(client); +} + +AutofillHandler::AutofillHandler( + AutofillDriver* driver, + AutofillClient* client, + AutofillDownloadManagerState enable_download_manager, + version_info::Channel channel) + : driver_(driver), + client_(client), + log_manager_(client ? client->GetLogManager() : nullptr), + form_interactions_ukm_logger_(CreateFormInteractionsUkmLogger()), + is_rich_query_enabled_(IsRichQueryEnabled(channel)) { + if (enable_download_manager == ENABLE_AUTOFILL_DOWNLOAD_MANAGER) { + download_manager_ = std::make_unique<AutofillDownloadManager>( + driver, this, GetAPIKeyForUrl(channel), + AutofillDownloadManager::IsRawMetadataUploadingEnabled( + IsRawMetadataUploadingEnabled(channel)), + log_manager_); + } + if (client) { + translate::TranslateDriver* translate_driver = client->GetTranslateDriver(); + if (translate_driver) { + translate_observation_.Observe(translate_driver); + } + } +} + +AutofillHandler::~AutofillHandler() { + translate_observation_.Reset(); + if (!query_result_delay_task_.IsCancelled()) + query_result_delay_task_.Cancel(); +} + +void AutofillHandler::OnLanguageDetermined( + const translate::LanguageDetectionDetails& details) { + if (!base::FeatureList::IsEnabled( + features::kAutofillParsingPatternsLanguageDetection)) { + return; + } + for (auto& p : form_structures_) { + std::unique_ptr<FormStructure>& form_structure = p.second; + form_structure->set_current_page_language( + LanguageCode(details.adopted_language)); + form_structure->DetermineHeuristicTypes(form_interactions_ukm_logger(), + log_manager_); + } +} + +void AutofillHandler::OnTranslateDriverDestroyed( + translate::TranslateDriver* translate_driver) { + translate_observation_.Reset(); +} + +LanguageCode AutofillHandler::GetCurrentPageLanguage() const { + DCHECK(client_); + const translate::LanguageState* language_state = client_->GetLanguageState(); + if (!language_state) + return LanguageCode(); + return LanguageCode(language_state->current_language()); +} void AutofillHandler::OnFormSubmitted(const FormData& form, bool known_success, @@ -73,14 +191,13 @@ void AutofillHandler::OnFormSubmitted(const FormData& form, OnFormSubmittedImpl(form, known_success, source); } -void AutofillHandler::OnFormsSeen(const std::vector<FormData>& forms, - const base::TimeTicks timestamp) { +void AutofillHandler::OnFormsSeen(const std::vector<FormData>& forms) { if (!IsValidFormDataVector(forms) || !driver_->RendererIsAvailable()) return; // This should be called even forms is empty, AutofillProviderAndroid uses // this event to detect form submission. - if (!ShouldParseForms(forms, timestamp)) + if (!ShouldParseForms(forms)) return; if (forms.empty()) @@ -91,26 +208,15 @@ void AutofillHandler::OnFormsSeen(const std::vector<FormData>& forms, const auto parse_form_start_time = AutofillTickClock::NowTicks(); FormStructure* cached_form_structure = FindCachedFormByRendererId(form.unique_renderer_id); - // Autofill used to ignore cache hits for non-credit-card forms. The - // motivation behind this is probably to have credit-card forms preserve - // their original signature, whereas non-credit-card forms would use the - // most recent form signature. Ignoring cache hits however appears to be - // part of breaking profile imports and voting for dynamic forms. See - // crbug/1091401#c15 for details. - // - // Therefore, if the kAutofillKeepInitialFormValuesInCache experiment is - // enabled, we do not ignore cache hits, but in those cases where the old - // code would have ignored the cache hit we update the FormStructure's - // FormSignature. - // Otherwise, if the experiment disabled, we just ignore the cache hit. + + // Not updating signatures of credit card forms is legacy behaviour. We + // believe that the signatures are kept stable for voting purposes. bool update_form_signature = false; if (cached_form_structure) { - for (const FormType& form_type : cached_form_structure->GetFormTypes()) { - if (form_type != CREDIT_CARD_FORM) { - update_form_signature = true; - break; - } - } + const DenseSet<FormType>& form_types = + cached_form_structure->GetFormTypes(); + update_form_signature = + form_types.size() > form_types.count(FormType::kCreditCardForm); } FormStructure* form_structure = ParseForm(form, cached_form_structure); @@ -128,7 +234,55 @@ void AutofillHandler::OnFormsSeen(const std::vector<FormData>& forms, if (new_forms.empty()) return; - OnFormsParsed(new_forms, timestamp); + OnFormsParsed(new_forms); +} + +void AutofillHandler::OnFormsParsed(const std::vector<const FormData*>& forms) { + DCHECK(!forms.empty()); + OnBeforeProcessParsedForms(); + + driver()->HandleParsedForms(forms); + + std::vector<FormStructure*> non_queryable_forms; + std::vector<FormStructure*> queryable_forms; + DenseSet<FormType> form_types; + for (const FormData* form : forms) { + FormStructure* form_structure = + FindCachedFormByRendererId(form->unique_renderer_id); + if (!form_structure) { + NOTREACHED(); + continue; + } + + form_types.insert_all(form_structure->GetFormTypes()); + + // Configure the query encoding for this form and add it to the appropriate + // collection of forms: queryable vs non-queryable. + form_structure->set_is_rich_query_enabled(is_rich_query_enabled_); + if (form_structure->ShouldBeQueried()) + queryable_forms.push_back(form_structure); + else + non_queryable_forms.push_back(form_structure); + + OnFormProcessed(*form, *form_structure); + } + + if (!queryable_forms.empty() || !non_queryable_forms.empty()) { + OnAfterProcessParsedForms(form_types); + } + + // Send the current type predictions to the renderer. For non-queryable forms + // this is all the information about them that will ever be available. The + // queryable forms will be updated once the field type query is complete. + driver()->SendAutofillTypePredictionsToRenderer(non_queryable_forms); + driver()->SendAutofillTypePredictionsToRenderer(queryable_forms); + LogAutofillTypePredictionsAvailable(log_manager_, non_queryable_forms); + LogAutofillTypePredictionsAvailable(log_manager_, queryable_forms); + + // Query the server if at least one of the forms was parsed. + if (!queryable_forms.empty() && download_manager()) { + download_manager()->StartQueryRequest(queryable_forms); + } } void AutofillHandler::OnTextFieldDidChange(const FormData& form, @@ -203,6 +357,7 @@ void AutofillHandler::SendFormDataToRenderer( driver_->SendFormDataToRenderer(query_id, action, data); } +// Returns true if |live_form| does not match |cached_form|. bool AutofillHandler::GetCachedFormAndField(const FormData& form, const FormFieldData& field, FormStructure** form_structure, @@ -242,6 +397,15 @@ bool AutofillHandler::GetCachedFormAndField(const FormData& form, return *autofill_field != nullptr; } +std::unique_ptr<AutofillMetrics::FormInteractionsUkmLogger> +AutofillHandler::CreateFormInteractionsUkmLogger() { + if (!client()) + return nullptr; + + return std::make_unique<AutofillMetrics::FormInteractionsUkmLogger>( + client()->GetUkmRecorder(), client()->GetUkmSourceId()); +} + size_t AutofillHandler::FindCachedFormsBySignature( FormSignature form_signature, std::vector<FormStructure*>* form_structures) const { @@ -290,9 +454,10 @@ FormStructure* AutofillHandler::ParseForm(const FormData& form, value_from_dynamic_change_form_ = true; } - form_structure->set_page_language(GetPageLanguage()); + form_structure->set_current_page_language(GetCurrentPageLanguage()); - form_structure->DetermineHeuristicTypes(log_manager_); + form_structure->DetermineHeuristicTypes(form_interactions_ukm_logger(), + log_manager_); // Hold the parsed_form_structure we intend to return. We can use this to // reference the form_signature when transferring ownership below. @@ -309,12 +474,90 @@ FormStructure* AutofillHandler::ParseForm(const FormData& form, return parsed_form_structure; } -std::string AutofillHandler::GetPageLanguage() const { - return std::string(); -} - void AutofillHandler::Reset() { form_structures_.clear(); + form_interactions_ukm_logger_ = CreateFormInteractionsUkmLogger(); } +void AutofillHandler::OnLoadedServerPredictions( + std::string response, + const std::vector<FormSignature>& queried_form_signatures) { + // Get the current valid FormStructures represented by + // |queried_form_signatures|. + std::vector<FormStructure*> queried_forms; + queried_forms.reserve(queried_form_signatures.size()); + for (const auto& form_signature : queried_form_signatures) { + FindCachedFormsBySignature(form_signature, &queried_forms); + } + + // Each form signature in |queried_form_signatures| is supposed to be unique, + // and therefore appear only once. This ensures that + // FindCachedFormsBySignature() produces an output without duplicates in the + // forms. + // TODO(crbug/1064709): |queried_forms| could be a set data structure; their + // order should be irrelevant. + DCHECK_EQ(queried_forms.size(), + std::set<FormStructure*>(queried_forms.begin(), queried_forms.end()) + .size()); + + // If there are no current forms corresponding to the queried signatures, drop + // the query response. + if (queried_forms.empty()) + return; + + // Parse and store the server predictions. + FormStructure::ParseApiQueryResponse(std::move(response), queried_forms, + queried_form_signatures, + form_interactions_ukm_logger()); + + // Will log quality metrics for each FormStructure based on the presence of + // autocomplete attributes, if available. + if (auto* logger = form_interactions_ukm_logger()) { + for (FormStructure* cur_form : queried_forms) { + cur_form->LogQualityMetricsBasedOnAutocomplete(logger); + } + } + + // Send field type predictions to the renderer so that it can possibly + // annotate forms with the predicted types or add console warnings. + driver()->SendAutofillTypePredictionsToRenderer(queried_forms); + + LogAutofillTypePredictionsAvailable(log_manager_, queried_forms); + + // TODO(crbug.com/1176816): Remove the test code after initial integration. + int delay = 0; + if (auto* cmd = base::CommandLine::ForCurrentProcess()) { + // This command line helps to simulate query result arriving after autofill + // is triggered and shall be used for manual test only. + std::string value = cmd->GetSwitchValueASCII( + "autofill-server-query-result-delay-in-seconds"); + if (!base::StringToInt(value, &delay)) + delay = 0; + } + + if (delay > 0) { + query_result_delay_task_.Reset( + base::BindOnce(&AutofillHandler::PropagateAutofillPredictionsToDriver, + base::Unretained(this))); + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::BindOnce(query_result_delay_task_.callback(), queried_forms), + base::TimeDelta::FromSeconds(delay)); + } else { + PropagateAutofillPredictionsToDriver(queried_forms); + } +} + +void AutofillHandler::PropagateAutofillPredictionsToDriver( + const std::vector<FormStructure*>& queried_forms) { + // Forward form structures to the password generation manager to detect + // account creation forms. + driver()->PropagateAutofillPredictions(queried_forms); +} + +void AutofillHandler::OnServerRequestError( + FormSignature form_signature, + AutofillDownloadManager::RequestType request_type, + int http_error) {} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_handler.h b/chromium/components/autofill/core/browser/autofill_handler.h index 18b7d0b42c3..3f1dc3c1259 100644 --- a/chromium/components/autofill/core/browser/autofill_handler.h +++ b/chromium/components/autofill/core/browser/autofill_handler.h @@ -10,14 +10,22 @@ #include <string> #include <vector> +#include "base/cancelable_callback.h" #include "base/compiler_specific.h" +#include "base/scoped_observation.h" #include "base/time/time.h" #include "build/build_config.h" +#include "components/autofill/core/browser/autofill_download_manager.h" #include "components/autofill/core/browser/autofill_driver.h" +#include "components/autofill/core/browser/autofill_metrics.h" +#include "components/autofill/core/common/dense_set.h" #include "components/autofill/core/common/form_data.h" +#include "components/autofill/core/common/language_code.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" #include "components/autofill/core/common/renderer_id.h" #include "components/autofill/core/common/signatures.h" +#include "components/translate/core/browser/translate_driver.h" +#include "components/version_info/channel.h" namespace gfx { class RectF; @@ -33,7 +41,9 @@ class LogManager; // This class defines the interface should be implemented by autofill // implementation in browser side to interact with AutofillDriver. -class AutofillHandler { +class AutofillHandler + : public AutofillDownloadManager::Observer, + public translate::TranslateDriver::LanguageDetectionObserver { public: enum AutofillDownloadManagerState { ENABLE_AUTOFILL_DOWNLOAD_MANAGER, @@ -47,7 +57,24 @@ class AutofillHandler { virtual void OnFormParsed() = 0; }; - virtual ~AutofillHandler(); + // Rich queries are enabled by feature flag iff this chrome instance is + // neither on the STABLE nor BETA release channel. + static bool IsRichQueryEnabled(version_info::Channel channel); + + // Raw metadata uploading enabled iff this Chrome instance is on Canary or Dev + // channel. + static bool IsRawMetadataUploadingEnabled(version_info::Channel channel); + + // TODO(crbug.com/1151542): Move to anonymous namespace once + // AutofillManager::OnLoadedServerPredictions() moves to AutofillHandler. + static void LogAutofillTypePredictionsAvailable( + LogManager* log_manager, + const std::vector<FormStructure*>& forms); + + ~AutofillHandler() override; + + AutofillClient* client() { return client_; } + const AutofillClient* client() const { return client_; } // Invoked when the value of textfield is changed. void OnTextFieldDidChange(const FormData& form, @@ -86,8 +113,7 @@ class AutofillHandler { mojom::SubmissionSource source); // Invoked when |forms| has been detected. - void OnFormsSeen(const std::vector<FormData>& forms, - const base::TimeTicks timestamp); + void OnFormsSeen(const std::vector<FormData>& forms); // Invoked when focus is no longer on form. |had_interacted_form| indicates // whether focus was previously on a form with which the user had interacted. @@ -110,9 +136,25 @@ class AutofillHandler { // Invoked when the options of a select element in the |form| changed. virtual void SelectFieldOptionsDidChange(const FormData& form) = 0; + // Invoked when the field type predictions are downloaded from the autofill + // server. + virtual void PropagateAutofillPredictions( + content::RenderFrameHost* rfh, + const std::vector<FormStructure*>& forms) = 0; + // Resets cache. virtual void Reset(); + // translate::TranslateDriver::LanguageDetectionObserver: + void OnTranslateDriverDestroyed( + translate::TranslateDriver* translate_driver) override; + // Invoked when the language has been detected by the Translate component. + // As this usually happens after Autofill has parsed the forms for the first + // time, the heuristics need to be re-run by this function in order to run + // use language-specific patterns. + void OnLanguageDetermined( + const translate::LanguageDetectionDetails& details) override; + // Send the form |data| to renderer for the specified |action|. void SendFormDataToRenderer(int query_id, AutofillDriver::RendererFormDataAction action, @@ -127,6 +169,10 @@ class AutofillHandler { FormStructure** form_structure, AutofillField** autofill_field) WARN_UNUSED_RESULT; + // Returns nullptr if no cached form structure is found with a matching + // |renderer_id|. Runs in logarithmic time. + FormStructure* FindCachedFormByRendererId(FormRendererId renderer_id) const; + // Returns the number of forms this Autofill handler is aware of. size_t NumFormsDetected() const { return form_structures_.size(); } @@ -142,6 +188,29 @@ class AutofillHandler { AutofillDriver* driver() { return driver_; } + AutofillDownloadManager* download_manager() { + return download_manager_.get(); + } + + // The return value shouldn't be cached, retrieve it as needed. + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger() { + return form_interactions_ukm_logger_.get(); + } + + // A public wrapper that calls |OnLoadedServerPredictions| for testing + // purposes only, it is used by WebView integration test and unit test, so it + // can't be in #ifdef UNIT_TEST. + void OnLoadedServerPredictionsForTest( + std::string response, + const std::vector<FormSignature>& queried_form_signatures) { + OnLoadedServerPredictions(response, queried_form_signatures); + } + void OnServerRequestErrorForTest( + FormSignature form_signature, + AutofillDownloadManager::RequestType request_type, + int http_error) { + OnServerRequestError(form_signature, request_type, http_error); + } #ifdef UNIT_TEST // A public wrapper that calls |mutable_form_structures| for testing purposes // only. @@ -154,10 +223,22 @@ class AutofillHandler { FormStructure* ParseFormForTest(const FormData& form) { return ParseForm(form, nullptr); } -#endif + +#endif // UNIT_TEST protected: - AutofillHandler(AutofillDriver* driver, LogManager* log_manager); + AutofillHandler(AutofillDriver* driver, + AutofillClient* client, + AutofillDownloadManagerState enable_download_manager); + AutofillHandler(AutofillDriver* driver, + AutofillClient* client, + AutofillDownloadManagerState enable_download_manager, + version_info::Channel channel); + + LogManager* log_manager() { return log_manager_; } + + // Retrieves the page language from |client_| + LanguageCode GetCurrentPageLanguage() const; virtual void OnFormSubmittedImpl(const FormData& form, bool known_success, @@ -189,12 +270,19 @@ class AutofillHandler { // Return whether the |forms| from OnFormSeen() should be parsed to // form_structures. - virtual bool ShouldParseForms(const std::vector<FormData>& forms, - const base::TimeTicks timestamp) = 0; + virtual bool ShouldParseForms(const std::vector<FormData>& forms) = 0; - // Invoked when forms from OnFormsSeen() has been parsed to |form_structures|. - virtual void OnFormsParsed(const std::vector<const FormData*>& forms, - const base::TimeTicks timestamp) = 0; + // Invoked before parsing the forms. + virtual void OnBeforeProcessParsedForms() = 0; + + // Invoked when the given |form| has been processed to the given + // |form_structure|. + virtual void OnFormProcessed(const FormData& form, + const FormStructure& form_structure) = 0; + // Invoked after all forms have been processed, |form_types| is a set of + // FormType found. + virtual void OnAfterProcessParsedForms( + const DenseSet<FormType>& form_types) = 0; // Returns the number of FormStructures with the given |form_signature| and // appends them to |form_structures|. Runs in linear time. @@ -202,19 +290,12 @@ class AutofillHandler { FormSignature form_signature, std::vector<FormStructure*>* form_structures) const; - // Returns nullptr if no cached form structure is found with a matching - // |renderer_id|. Runs in logarithmic time. - FormStructure* FindCachedFormByRendererId(FormRendererId renderer_id) const; - // Parses the |form| with the server data retrieved from the |cached_form| // (if any). Returns nullptr if the form should not be parsed. Otherwise, adds // the returned form structure to the |form_structures_|. FormStructure* ParseForm(const FormData& form, const FormStructure* cached_form); - // Returns the page language, if available. - virtual std::string GetPageLanguage() const; - bool value_from_dynamic_change_form_ = false; std::map<FormRendererId, std::unique_ptr<FormStructure>>* @@ -222,16 +303,70 @@ class AutofillHandler { return &form_structures_; } +#ifdef UNIT_TEST + // Exposed for testing. + void set_download_manager_for_test( + std::unique_ptr<AutofillDownloadManager> manager) { + download_manager_ = std::move(manager); + } + + // Exposed for testing. + bool is_rich_query_enabled() const { return is_rich_query_enabled_; } +#endif // UNIT_TEST + private: + // AutofillDownloadManager::Observer: + void OnLoadedServerPredictions( + std::string response, + const std::vector<FormSignature>& queried_form_signatures) override; + void OnServerRequestError(FormSignature form_signature, + AutofillDownloadManager::RequestType request_type, + int http_error) override; + + // Invoked when forms from OnFormsSeen() have been parsed to + // |form_structures|. + void OnFormsParsed(const std::vector<const FormData*>& forms); + + void PropagateAutofillPredictionsToDriver( + const std::vector<FormStructure*>& forms); + + std::unique_ptr<AutofillMetrics::FormInteractionsUkmLogger> + CreateFormInteractionsUkmLogger(); + // Provides driver-level context to the shared code of the component. Must // outlive this object. AutofillDriver* const driver_; + AutofillClient* const client_; + LogManager* const log_manager_; + // Observer needed to re-run heuristics when the language has been detected. + base::ScopedObservation< + translate::TranslateDriver, + translate::TranslateDriver::LanguageDetectionObserver, + &translate::TranslateDriver::AddLanguageDetectionObserver, + &translate::TranslateDriver::RemoveLanguageDetectionObserver> + translate_observation_{this}; + // Our copy of the form data. std::map<FormRendererId, std::unique_ptr<FormStructure>> form_structures_; + // Handles queries and uploads to Autofill servers. Will be nullptr if + // the download manager functionality is disabled. + std::unique_ptr<AutofillDownloadManager> download_manager_; + + // Utility for logging URL keyed metrics. + std::unique_ptr<AutofillMetrics::FormInteractionsUkmLogger> + form_interactions_ukm_logger_; + + // Tracks whether or not rich query encoding is enabled for this client. + const bool is_rich_query_enabled_ = false; + + // Task to delay propagate the query result to driver for testing. + base::CancelableOnceCallback<void(const std::vector<FormStructure*>&)> + query_result_delay_task_; + // Will be not null only for |SaveCardBubbleViewsFullFormBrowserTest|. ObserverForTest* observer_for_testing_ = nullptr; diff --git a/chromium/components/autofill/core/browser/autofill_handler_proxy.cc b/chromium/components/autofill/core/browser/autofill_handler_proxy.cc index 41fd6d405e2..8698501e79c 100644 --- a/chromium/components/autofill/core/browser/autofill_handler_proxy.cc +++ b/chromium/components/autofill/core/browser/autofill_handler_proxy.cc @@ -10,10 +10,16 @@ namespace autofill { using base::TimeTicks; -AutofillHandlerProxy::AutofillHandlerProxy(AutofillDriver* driver, - LogManager* log_manager, - AutofillProvider* provider) - : AutofillHandler(driver, log_manager), provider_(provider) {} +AutofillHandlerProxy::AutofillHandlerProxy( + AutofillDriver* driver, + AutofillClient* client, + AutofillProvider* provider, + AutofillHandler::AutofillDownloadManagerState enable_download_manager) + : AutofillHandler(driver, + client, + enable_download_manager, + version_info::Channel::UNKNOWN), + provider_(provider) {} AutofillHandlerProxy::~AutofillHandlerProxy() {} @@ -62,18 +68,14 @@ void AutofillHandlerProxy::OnSelectControlDidChangeImpl( provider_->OnSelectControlDidChange(this, form, field, bounding_box); } -bool AutofillHandlerProxy::ShouldParseForms(const std::vector<FormData>& forms, - const base::TimeTicks timestamp) { - provider_->OnFormsSeen(this, forms, timestamp); +bool AutofillHandlerProxy::ShouldParseForms( + const std::vector<FormData>& forms) { + provider_->OnFormsSeen(this, forms); // Need to parse the |forms| to FormStructure, so heuristic_type can be // retrieved later. return true; } -void AutofillHandlerProxy::OnFormsParsed( - const std::vector<const FormData*>& form_structures, - const base::TimeTicks timestamp) {} - void AutofillHandlerProxy::OnFocusNoLongerOnForm(bool had_interacted_form) { provider_->OnFocusNoLongerOnForm(this, had_interacted_form); } @@ -84,17 +86,29 @@ void AutofillHandlerProxy::OnDidFillAutofillFormData( provider_->OnDidFillAutofillFormData(this, form, timestamp); } -void AutofillHandlerProxy::OnDidPreviewAutofillFormData() {} - -void AutofillHandlerProxy::OnDidEndTextFieldEditing() {} - void AutofillHandlerProxy::OnHidePopup() { provider_->OnHidePopup(this); } void AutofillHandlerProxy::SelectFieldOptionsDidChange(const FormData& form) {} +void AutofillHandlerProxy::PropagateAutofillPredictions( + content::RenderFrameHost* rfh, + const std::vector<FormStructure*>& forms) { + has_server_prediction_ = true; + provider_->OnServerPredictionsAvailable(this); +} + +void AutofillHandlerProxy::OnServerRequestError( + FormSignature form_signature, + AutofillDownloadManager::RequestType request_type, + int http_error) { + provider_->OnServerQueryRequestError(this, form_signature); +} + void AutofillHandlerProxy::Reset() { + AutofillHandler::Reset(); + has_server_prediction_ = false; provider_->Reset(this); } diff --git a/chromium/components/autofill/core/browser/autofill_handler_proxy.h b/chromium/components/autofill/core/browser/autofill_handler_proxy.h index 8d2f0287888..9a41754e3d6 100644 --- a/chromium/components/autofill/core/browser/autofill_handler_proxy.h +++ b/chromium/components/autofill/core/browser/autofill_handler_proxy.h @@ -7,6 +7,7 @@ #include "base/memory/weak_ptr.h" #include "components/autofill/core/browser/autofill_handler.h" +#include "components/autofill/core/common/dense_set.h" namespace autofill { @@ -15,9 +16,11 @@ class AutofillProvider; // This class forwards AutofillHandler calls to AutofillProvider. class AutofillHandlerProxy : public AutofillHandler { public: - AutofillHandlerProxy(AutofillDriver* driver, - LogManager* log_manager, - AutofillProvider* provider); + AutofillHandlerProxy( + AutofillDriver* driver, + AutofillClient* client, + AutofillProvider* provider, + AutofillHandler::AutofillDownloadManagerState enable_download_manager); ~AutofillHandlerProxy() override; void OnFocusNoLongerOnForm(bool had_interacted_form) override; @@ -25,8 +28,8 @@ class AutofillHandlerProxy : public AutofillHandler { void OnDidFillAutofillFormData(const FormData& form, const base::TimeTicks timestamp) override; - void OnDidPreviewAutofillFormData() override; - void OnDidEndTextFieldEditing() override; + void OnDidPreviewAutofillFormData() override {} + void OnDidEndTextFieldEditing() override {} void OnHidePopup() override; void SelectFieldOptionsDidChange(const FormData& form) override; @@ -36,6 +39,8 @@ class AutofillHandlerProxy : public AutofillHandler { return weak_ptr_factory_.GetWeakPtr(); } + bool has_server_prediction() const { return has_server_prediction_; } + protected: void OnFormSubmittedImpl(const FormData& form, bool known_success, @@ -64,13 +69,26 @@ class AutofillHandlerProxy : public AutofillHandler { const FormFieldData& field, const gfx::RectF& bounding_box) override; - bool ShouldParseForms(const std::vector<FormData>& forms, - const base::TimeTicks timestamp) override; + bool ShouldParseForms(const std::vector<FormData>& forms) override; + + void OnBeforeProcessParsedForms() override {} + + void OnFormProcessed(const FormData& form, + const FormStructure& form_structure) override {} + + void OnAfterProcessParsedForms( + const DenseSet<FormType>& form_types) override {} + + void PropagateAutofillPredictions( + content::RenderFrameHost* rfh, + const std::vector<FormStructure*>& forms) override; - void OnFormsParsed(const std::vector<const FormData*>& form_structures, - const base::TimeTicks timestamp) override; + void OnServerRequestError(FormSignature form_signature, + AutofillDownloadManager::RequestType request_type, + int http_error) override; private: + bool has_server_prediction_ = false; AutofillProvider* provider_; base::WeakPtrFactory<AutofillHandlerProxy> weak_ptr_factory_{this}; diff --git a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc index 9a951322bc7..f365bd94f19 100644 --- a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc +++ b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc @@ -75,13 +75,12 @@ bool IsEmptySalt(std::wstring const& salt) { return true; } -base::string16 ReadAndDecryptValue(const RegKey& key, - const wchar_t* value_name) { +std::wstring ReadAndDecryptValue(const RegKey& key, const wchar_t* value_name) { DWORD data_type = REG_BINARY; DWORD data_size = 0; LONG result = key.ReadValue(value_name, nullptr, &data_size, &data_type); if ((result != ERROR_SUCCESS) || !data_size || data_type != REG_BINARY) - return base::string16(); + return std::wstring(); std::string data; data.resize(data_size); result = key.ReadValue(value_name, &(data[0]), &data_size, &data_type); @@ -91,12 +90,11 @@ base::string16 ReadAndDecryptValue(const RegKey& key, // The actual data is in UTF16 already. if (!(out_data.size() & 1) && (out_data.size() > 2) && !out_data[out_data.size() - 1] && !out_data[out_data.size() - 2]) { - return base::string16( - reinterpret_cast<const wchar_t *>(out_data.c_str())); + return reinterpret_cast<const wchar_t*>(out_data.c_str()); } } } - return base::string16(); + return std::wstring(); } struct { @@ -157,16 +155,18 @@ bool ImportSingleFormGroup(const RegKey& key, if (it == reg_to_field.end()) continue; // This field is not imported. - base::string16 field_value = ReadAndDecryptValue(key, value_name.c_str()); + std::wstring field_value = ReadAndDecryptValue(key, value_name.c_str()); if (!field_value.empty()) { if (it->second == CREDIT_CARD_NUMBER) field_value = DecryptCCNumber(field_value); // Phone numbers are stored piece-by-piece, and then reconstructed from // the pieces. The rest of the fields are set "as is". - if (!phone || !phone->SetInfo(AutofillType(it->second), field_value)) { + if (!phone || !phone->SetInfo(AutofillType(it->second), + base::WideToUTF16(field_value))) { has_non_empty_fields = true; - form_group->SetInfo(AutofillType(it->second), field_value, app_locale); + form_group->SetInfo(AutofillType(it->second), + base::WideToUTF16(field_value), app_locale); } } } @@ -265,8 +265,8 @@ bool ImportCurrentUserProfiles(const std::string& app_locale, profiles->push_back(profile); } } - base::string16 password_hash; - base::string16 salt; + std::wstring password_hash; + std::wstring salt; RegKey cc_key(HKEY_CURRENT_USER, kCreditCardKey, KEY_READ); if (cc_key.Valid()) { password_hash = ReadAndDecryptValue(cc_key, kPasswordHashValue); diff --git a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc index 089f2cc97ba..f1b065de45a 100644 --- a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc @@ -8,6 +8,7 @@ #include "base/stl_util.h" #include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" #include "base/win/registry.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" @@ -173,32 +174,42 @@ TEST_F(AutofillIeToolbarImportTest, TestAutofillImport) { EXPECT_TRUE(ImportCurrentUserProfiles("en-US", &profiles, &credit_cards)); ASSERT_EQ(2U, profiles.size()); // The profiles are read in reverse order. - EXPECT_EQ(profile1[0].value, profiles[1].GetRawInfo(NAME_FIRST)); - EXPECT_EQ(profile1[1].value, profiles[1].GetRawInfo(NAME_MIDDLE)); - EXPECT_EQ(profile1[2].value, profiles[1].GetRawInfo(NAME_LAST)); - EXPECT_EQ(profile1[3].value, profiles[1].GetRawInfo(EMAIL_ADDRESS)); - EXPECT_EQ(profile1[4].value, profiles[1].GetRawInfo(COMPANY_NAME)); - EXPECT_EQ(profile1[7].value, + EXPECT_EQ(base::WideToUTF16(profile1[0].value), + profiles[1].GetRawInfo(NAME_FIRST)); + EXPECT_EQ(base::WideToUTF16(profile1[1].value), + profiles[1].GetRawInfo(NAME_MIDDLE)); + EXPECT_EQ(base::WideToUTF16(profile1[2].value), + profiles[1].GetRawInfo(NAME_LAST)); + EXPECT_EQ(base::WideToUTF16(profile1[3].value), + profiles[1].GetRawInfo(EMAIL_ADDRESS)); + EXPECT_EQ(base::WideToUTF16(profile1[4].value), + profiles[1].GetRawInfo(COMPANY_NAME)); + EXPECT_EQ(base::WideToUTF16(profile1[7].value), profiles[1].GetInfo(AutofillType(PHONE_HOME_COUNTRY_CODE), "US")); - EXPECT_EQ(profile1[6].value, + EXPECT_EQ(base::WideToUTF16(profile1[6].value), profiles[1].GetInfo(AutofillType(PHONE_HOME_CITY_CODE), "US")); - EXPECT_EQ(L"5555555", + EXPECT_EQ(STRING16_LITERAL("5555555"), profiles[1].GetInfo(AutofillType(PHONE_HOME_NUMBER), "US")); - EXPECT_EQ(L"1 650-555-5555", profiles[1].GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); - - EXPECT_EQ(profile2[0].value, profiles[0].GetRawInfo(NAME_FIRST)); - EXPECT_EQ(profile2[1].value, profiles[0].GetRawInfo(NAME_LAST)); - EXPECT_EQ(profile2[2].value, profiles[0].GetRawInfo(EMAIL_ADDRESS)); - EXPECT_EQ(profile2[3].value, profiles[0].GetRawInfo(COMPANY_NAME)); + EXPECT_EQ(STRING16_LITERAL("1 650-555-5555"), + profiles[1].GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); + + EXPECT_EQ(base::WideToUTF16(profile2[0].value), + profiles[0].GetRawInfo(NAME_FIRST)); + EXPECT_EQ(base::WideToUTF16(profile2[1].value), + profiles[0].GetRawInfo(NAME_LAST)); + EXPECT_EQ(base::WideToUTF16(profile2[2].value), + profiles[0].GetRawInfo(EMAIL_ADDRESS)); + EXPECT_EQ(base::WideToUTF16(profile2[3].value), + profiles[0].GetRawInfo(COMPANY_NAME)); ASSERT_EQ(1U, credit_cards.size()); - EXPECT_EQ(credit_card[0].value, + EXPECT_EQ(base::WideToUTF16(credit_card[0].value), credit_cards[0].GetRawInfo(CREDIT_CARD_NAME_FULL)); - EXPECT_EQ(L"4111111111111111", + EXPECT_EQ(STRING16_LITERAL("4111111111111111"), credit_cards[0].GetRawInfo(CREDIT_CARD_NUMBER)); - EXPECT_EQ(credit_card[2].value, + EXPECT_EQ(base::WideToUTF16(credit_card[2].value), credit_cards[0].GetRawInfo(CREDIT_CARD_EXP_MONTH)); - EXPECT_EQ(credit_card[3].value, + EXPECT_EQ(base::WideToUTF16(credit_card[3].value), credit_cards[0].GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR)); // Mock password encrypted cc. diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc index 9f4f5f3688e..5f901aba729 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_manager.cc @@ -20,6 +20,7 @@ #include "base/check_op.h" #include "base/command_line.h" #include "base/containers/adapters.h" +#include "base/containers/flat_map.h" #include "base/feature_list.h" #include "base/files/file_util.h" #include "base/guid.h" @@ -40,6 +41,7 @@ #include "base/task/thread_pool.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" +#include "build/chromeos_buildflags.h" #include "components/autofill/core/browser/autocomplete_history_manager.h" #include "components/autofill/core/browser/autofill_browser_util.h" #include "components/autofill/core/browser/autofill_client.h" @@ -76,7 +78,6 @@ #include "components/autofill/core/common/autofill_internals/logging_scope.h" #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_prefs.h" -#include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_data.h" @@ -89,8 +90,6 @@ #include "components/security_interstitials/core/pref_names.h" #include "components/security_state/core/security_state.h" #include "components/strings/grit/components_strings.h" -#include "components/version_info/channel.h" -#include "google_apis/google_api_keys.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/rect.h" @@ -137,7 +136,8 @@ base::string16 SanitizeCreditCardFieldValue(const base::string16& value) { // Returns whether the |field| is predicted as being any kind of name. bool IsNameType(const AutofillField& field) { - return field.Type().group() == NAME || field.Type().group() == NAME_BILLING || + return field.Type().group() == FieldTypeGroup::kName || + field.Type().group() == FieldTypeGroup::kNameBilling || field.Type().GetStorableType() == CREDIT_CARD_NAME_FULL || field.Type().GetStorableType() == CREDIT_CARD_NAME_FIRST || field.Type().GetStorableType() == CREDIT_CARD_NAME_LAST; @@ -157,8 +157,8 @@ void SelectRightNameType(AutofillField* field, bool is_credit_card) { for (auto type : old_types) { FieldTypeGroup group = AutofillType(type).group(); - if ((is_credit_card && group == CREDIT_CARD) || - (!is_credit_card && group == NAME)) { + if ((is_credit_card && group == FieldTypeGroup::kCreditCard) || + (!is_credit_card && group == FieldTypeGroup::kName)) { types_to_keep.insert(type); } } @@ -176,30 +176,17 @@ void SelectRightNameType(AutofillField* field, bool is_credit_card) { void LogDeveloperEngagementUkm(ukm::UkmRecorder* ukm_recorder, ukm::SourceId source_id, - FormStructure* form_structure) { - if (form_structure->developer_engagement_metrics()) { + const FormStructure& form_structure) { + if (form_structure.developer_engagement_metrics()) { AutofillMetrics::LogDeveloperEngagementUkm( - ukm_recorder, source_id, form_structure->main_frame_origin().GetURL(), - form_structure->IsCompleteCreditCardForm(), - form_structure->GetFormTypes(), - form_structure->developer_engagement_metrics(), - form_structure->form_signature()); + ukm_recorder, source_id, form_structure.main_frame_origin().GetURL(), + form_structure.IsCompleteCreditCardForm(), + form_structure.GetFormTypes(), + form_structure.developer_engagement_metrics(), + form_structure.form_signature()); } } -std::string GetAPIKeyForUrl(version_info::Channel channel) { - // First look if we can get API key from command line flag. - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kAutofillAPIKey)) - return command_line.GetSwitchValueASCII(switches::kAutofillAPIKey); - - // Get the API key from Chrome baked keys. - if (channel == version_info::Channel::STABLE) - return google_apis::GetAPIKey(); - return google_apis::GetNonStableAPIKey(); -} - ValuePatternsMetric GetValuePattern(const base::string16& value) { if (IsUPIVirtualPaymentAddress(value)) return ValuePatternsMetric::kUpiVpa; @@ -234,47 +221,27 @@ void LogLanguageMetrics(const translate::LanguageState* language_state) { bool IsAddressForm(FieldTypeGroup field_type_group) { switch (field_type_group) { - case NAME: - case NAME_BILLING: - case EMAIL: - case COMPANY: - case ADDRESS_HOME: - case ADDRESS_BILLING: - case PHONE_HOME: - case PHONE_BILLING: + case FieldTypeGroup::kName: + case FieldTypeGroup::kNameBilling: + case FieldTypeGroup::kEmail: + case FieldTypeGroup::kCompany: + case FieldTypeGroup::kAddressHome: + case FieldTypeGroup::kAddressBilling: + case FieldTypeGroup::kPhoneHome: + case FieldTypeGroup::kPhoneBilling: return true; - case CREDIT_CARD: - case TRANSACTION: - case PASSWORD_FIELD: - case USERNAME_FIELD: - case NO_GROUP: - case UNFILLABLE: + case FieldTypeGroup::kCreditCard: + case FieldTypeGroup::kTransaction: + case FieldTypeGroup::kPasswordField: + case FieldTypeGroup::kUsernameField: + case FieldTypeGroup::kNoGroup: + case FieldTypeGroup::kUnfillable: return false; } NOTREACHED(); return false; } -void LogAutofillTypePredictionsAvailable( - LogManager* log_manager, - const std::vector<FormStructure*>& forms) { - if (VLOG_IS_ON(1)) { - VLOG(1) << "Parsed forms:"; - for (FormStructure* form : forms) - VLOG(1) << *form; - } - - if (!log_manager || !log_manager->IsLoggingActive()) - return; - - LogBuffer buffer; - for (FormStructure* form : forms) - buffer << *form; - - log_manager->Log() << LoggingScope::kParsing << LogMessage::kParsedForms - << std::move(buffer); -} - // Finds the first field in |form_structure| with |field.value|=|value|. AutofillField* FindFirstFieldWithValue(const FormStructure& form_structure, const base::string16& value) { @@ -432,32 +399,40 @@ const char* SubmissionSourceToString(SubmissionSource source) { // Returns how many fields with type |field_type| may be filled in a form at // maximum. -int TypeValueFormFillingLimit(ServerFieldType field_type) { - return field_type == CREDIT_CARD_NUMBER ? kCreditCardTypeValueFormFillingLimit - : kTypeValueFormFillingLimit; +size_t TypeValueFormFillingLimit(ServerFieldType field_type) { + switch (field_type) { + case CREDIT_CARD_NUMBER: + return kCreditCardTypeValueFormFillingLimit; + case ADDRESS_HOME_STATE: + return kStateTypeValueFormFillingLimit; + default: + return kTypeValueFormFillingLimit; + } } } // namespace AutofillManager::FillingContext::FillingContext( const AutofillField& field, - const AutofillProfile* optional_profile, - const CreditCard* optional_credit_card, + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card, const base::string16* optional_cvc) - : profile(optional_profile ? base::make_optional(*optional_profile) - : base::nullopt), - credit_card(optional_credit_card - ? base::make_optional(std::make_pair( - *optional_credit_card, - optional_cvc ? *optional_cvc : base::string16())) - : base::nullopt), - filled_field_renderer_id(field.unique_renderer_id), + : filled_field_renderer_id(field.unique_renderer_id), filled_field_signature(field.GetFieldSignature()), filled_field_unique_name(field.unique_name()), original_fill_time(AutofillTickClock::NowTicks()) { - DCHECK(optional_profile || optional_credit_card); - DCHECK(optional_credit_card || !optional_cvc); - DCHECK(profile || credit_card); + DCHECK(absl::holds_alternative<const CreditCard*>(profile_or_credit_card) || + !optional_cvc); + + if (absl::holds_alternative<const AutofillProfile*>(profile_or_credit_card)) { + profile_or_credit_card_with_cvc = + *absl::get<const AutofillProfile*>(profile_or_credit_card); + } else if (absl::holds_alternative<const CreditCard*>( + profile_or_credit_card)) { + profile_or_credit_card_with_cvc = + std::make_pair(*absl::get<const CreditCard*>(profile_or_credit_card), + optional_cvc ? *optional_cvc : base::string16()); + } } AutofillManager::FillingContext::~FillingContext() = default; @@ -474,6 +449,37 @@ AutofillManager::AutofillManager( app_locale, enable_download_manager) {} +AutofillManager::AutofillManager( + AutofillDriver* driver, + AutofillClient* client, + PersonalDataManager* personal_data, + AutocompleteHistoryManager* autocomplete_history_manager, + const std::string app_locale, + AutofillDownloadManagerState enable_download_manager, + std::unique_ptr<CreditCardAccessManager> cc_access_manager) + : AutofillHandler(driver, client, enable_download_manager), + external_delegate_( + std::make_unique<AutofillExternalDelegate>(this, driver)), + app_locale_(app_locale), + personal_data_(personal_data), + field_filler_(app_locale, client->GetAddressNormalizer()), + autocomplete_history_manager_( + autocomplete_history_manager->GetWeakPtr()) { + address_form_event_logger_ = std::make_unique<AddressFormEventLogger>( + driver->IsInMainFrame(), form_interactions_ukm_logger(), client); + credit_card_form_event_logger_ = std::make_unique<CreditCardFormEventLogger>( + driver->IsInMainFrame(), form_interactions_ukm_logger(), personal_data_, + client); + + credit_card_access_manager_ = cc_access_manager + ? std::move(cc_access_manager) + : std::make_unique<CreditCardAccessManager>( + driver, client, personal_data_, + credit_card_form_event_logger_.get()); + CountryNames::SetLocaleString(app_locale_); + offer_manager_ = client->GetAutofillOfferManager(); +} + AutofillManager::~AutofillManager() { if (has_parsed_forms_) { base::UmaHistogramBoolean( @@ -488,20 +494,13 @@ AutofillManager::~AutofillManager() { } } -void AutofillManager::SetExternalDelegate(AutofillExternalDelegate* delegate) { - // TODO(jrg): consider passing delegate into the ctor. That won't - // work if the delegate has a pointer to the AutofillManager, but - // future directions may not need such a pointer. - external_delegate_ = delegate; -} - void AutofillManager::ShowAutofillSettings(bool show_credit_card_settings) { - client_->ShowAutofillSettings(show_credit_card_settings); + client()->ShowAutofillSettings(show_credit_card_settings); } bool AutofillManager::ShouldShowScanCreditCard(const FormData& form, const FormFieldData& field) { - if (!client_->HasCreditCardScanFeature()) + if (!client()->HasCreditCardScanFeature()) return false; AutofillField* autofill_field = GetAutofillField(form, field); @@ -530,26 +529,26 @@ PopupType AutofillManager::GetPopupType(const FormData& form, return PopupType::kUnspecified; switch (autofill_field->Type().group()) { - case NO_GROUP: - case PASSWORD_FIELD: - case TRANSACTION: - case USERNAME_FIELD: - case UNFILLABLE: + case FieldTypeGroup::kNoGroup: + case FieldTypeGroup::kPasswordField: + case FieldTypeGroup::kTransaction: + case FieldTypeGroup::kUsernameField: + case FieldTypeGroup::kUnfillable: return PopupType::kUnspecified; - case CREDIT_CARD: + case FieldTypeGroup::kCreditCard: return PopupType::kCreditCards; - case ADDRESS_HOME: - case ADDRESS_BILLING: + case FieldTypeGroup::kAddressHome: + case FieldTypeGroup::kAddressBilling: return PopupType::kAddresses; - case NAME: - case NAME_BILLING: - case EMAIL: - case COMPANY: - case PHONE_HOME: - case PHONE_BILLING: + case FieldTypeGroup::kName: + case FieldTypeGroup::kNameBilling: + case FieldTypeGroup::kEmail: + case FieldTypeGroup::kCompany: + case FieldTypeGroup::kPhoneHome: + case FieldTypeGroup::kPhoneBilling: return FormHasAddressField(form) ? PopupType::kAddresses : PopupType::kPersonalInformation; @@ -564,19 +563,20 @@ bool AutofillManager::ShouldShowCreditCardSigninPromo( // Check whether we are dealing with a credit card field and whether it's // appropriate to show the promo (e.g. the platform is supported). AutofillField* autofill_field = GetAutofillField(form, field); - if (!autofill_field || autofill_field->Type().group() != CREDIT_CARD || - !client_->ShouldShowSigninPromo()) + if (!autofill_field || + autofill_field->Type().group() != FieldTypeGroup::kCreditCard || + !client()->ShouldShowSigninPromo()) return false; if (IsFormNonSecure(form)) return false; // The last step is checking if we are under the limit of impressions. - int impression_count = client_->GetPrefs()->GetInteger( + int impression_count = client()->GetPrefs()->GetInteger( prefs::kAutofillCreditCardSigninPromoImpressionCount); if (impression_count < kCreditCardSigninPromoImpressionLimit) { // The promo will be shown. Increment the impression count. - client_->GetPrefs()->SetInteger( + client()->GetPrefs()->SetInteger( prefs::kAutofillCreditCardSigninPromoImpressionCount, impression_count + 1); return true; @@ -590,7 +590,8 @@ bool AutofillManager::ShouldShowCardsFromAccountOption( const FormFieldData& field) { // Check whether we are dealing with a credit card field. AutofillField* autofill_field = GetAutofillField(form, field); - if (!autofill_field || autofill_field->Type().group() != CREDIT_CARD || + if (!autofill_field || + autofill_field->Type().group() != FieldTypeGroup::kCreditCard || // Exclude CVC and card type fields, because these will not have // suggestions available after the user opts in. autofill_field->Type().GetStorableType() == @@ -617,7 +618,7 @@ void AutofillManager::RefetchCardsAndUpdatePopup( AutofillType type = autofill_field ? autofill_field->Type() : AutofillType(CREDIT_CARD_NUMBER); - DCHECK_EQ(CREDIT_CARD, type.group()); + DCHECK_EQ(FieldTypeGroup::kCreditCard, type.group()); bool should_display_gpay_logo; auto cards = @@ -638,7 +639,7 @@ void AutofillManager::FetchVirtualCardCandidates() { // ShouldShowVirtualCardOption() should fail. DCHECK(!candidates.empty()); - client_->OfferVirtualCardOptions( + client()->OfferVirtualCardOptions( candidates, base::BindOnce(&AutofillManager::OnVirtualCardCandidateSelected, weak_ptr_factory_.GetWeakPtr())); @@ -653,8 +654,7 @@ void AutofillManager::OnVirtualCardCandidateSelected( } #endif -bool AutofillManager::ShouldParseForms(const std::vector<FormData>& forms, - const base::TimeTicks timestamp) { +bool AutofillManager::ShouldParseForms(const std::vector<FormData>& forms) { bool autofill_enabled = IsAutofillEnabled(); sync_state_ = personal_data_ ? personal_data_->GetSyncSigninState() : AutofillSyncSigninState::kNumSyncStates; @@ -674,16 +674,16 @@ bool AutofillManager::ShouldParseForms(const std::vector<FormData>& forms, void AutofillManager::OnFormSubmittedImpl(const FormData& form, bool known_success, SubmissionSource source) { - if (log_manager_) { - log_manager_->Log() << LoggingScope::kSubmission - << LogMessage::kFormSubmissionDetected << Br{} - << "known_success: " << known_success << Br{} - << "source: " << SubmissionSourceToString(source) - << Br{} << form; + if (log_manager()) { + log_manager()->Log() << LoggingScope::kSubmission + << LogMessage::kFormSubmissionDetected << Br{} + << "known_success: " << known_success << Br{} + << "source: " << SubmissionSourceToString(source) + << Br{} << form; } // Always upload page language metrics. - LogLanguageMetrics(client_->GetLanguageState()); + LogLanguageMetrics(client()->GetLanguageState()); // Always let the value patterns metric upload data. LogValuePatternsMetric(form); @@ -692,7 +692,7 @@ void AutofillManager::OnFormSubmittedImpl(const FormData& form, std::unique_ptr<FormStructure> submitted_form = ValidateSubmittedForm(form); if (!submitted_form) { autocomplete_history_manager_->OnWillSubmitForm( - form, client_->IsAutocompleteEnabled()); + form, client()->IsAutocompleteEnabled()); return; } @@ -706,7 +706,11 @@ void AutofillManager::OnFormSubmittedImpl(const FormData& form, } } autocomplete_history_manager_->OnWillSubmitForm( - form_for_autocomplete, client_->IsAutocompleteEnabled()); + form_for_autocomplete, client()->IsAutocompleteEnabled()); + + // TODO(https://crbug.com/1167475): Add Test for this metric. + base::UmaHistogramEnumeration("Autofill.FormSubmission.PerProfileType", + client()->GetProfileType()); if (IsAutofillProfileEnabled()) { address_form_event_logger_->OnWillSubmitForm(sync_state_, *submitted_form); @@ -745,9 +749,9 @@ void AutofillManager::OnFormSubmittedImpl(const FormData& form, // Update Personal Data with the form's submitted data. // Also triggers offering local/upload credit card save, if applicable. - client_->GetFormDataImporter()->ImportFormData(*submitted_form, - IsAutofillProfileEnabled(), - IsAutofillCreditCardEnabled()); + client()->GetFormDataImporter()->ImportFormData( + *submitted_form, IsAutofillProfileEnabled(), + IsAutofillCreditCardEnabled()); } bool AutofillManager::MaybeStartVoteUploadProcess( @@ -790,13 +794,16 @@ bool AutofillManager::MaybeStartVoteUploadProcess( copied_credit_cards.push_back(*card); // Annotate the form with the source language of the page. - base::Optional<std::string> page_language = GetPageLanguage(); - if (page_language) - form_structure->set_page_language(page_language.value()); + form_structure->set_current_page_language(GetCurrentPageLanguage()); // Attach the Randomized Encoder. form_structure->set_randomized_encoder( - RandomizedEncoder::Create(client_->GetPrefs())); + RandomizedEncoder::Create(client()->GetPrefs())); + + // Determine |ADDRESS_HOME_STATE| as a possible types for the fields in the + // |form_structure| with the help of |AlternativeStateNameMap|. + // |AlternativeStateNameMap| can only be accessed on the main UI thread. + PreProcessStateMatchingTypes(copied_profiles, form_structure.get()); // Note that ownership of |form_structure| is passed to the second task, // using |base::Owned|. We MUST temporarily hang on to the raw form pointer @@ -878,8 +885,8 @@ void AutofillManager::OnTextFieldDidChangeImpl(const FormData& form, uint32_t profile_form_bitmask = 0; if (!user_did_type_ || autofill_field->is_autofilled) { - form_interactions_ukm_logger_->LogTextFieldDidChange(*form_structure, - *autofill_field); + form_interactions_ukm_logger()->LogTextFieldDidChange(*form_structure, + *autofill_field); profile_form_bitmask = data_util::DetermineGroups(*form_structure); } @@ -893,7 +900,7 @@ void AutofillManager::OnTextFieldDidChangeImpl(const FormData& form, user_did_type_ = true; AutofillMetrics::LogUserHappinessMetric( AutofillMetrics::USER_DID_TYPE, autofill_field->Type().group(), - client_->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); + client()->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); } if (autofill_field->is_autofilled) { @@ -902,7 +909,7 @@ void AutofillManager::OnTextFieldDidChangeImpl(const FormData& form, AutofillMetrics::LogUserHappinessMetric( AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, autofill_field->Type().group(), - client_->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); + client()->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); auto* logger = GetEventFormLogger(autofill_field->Type().group()); if (logger) @@ -913,7 +920,7 @@ void AutofillManager::OnTextFieldDidChangeImpl(const FormData& form, AutofillMetrics::LogUserHappinessMetric( AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD_ONCE, autofill_field->Type().group(), - client_->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); + client()->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); } } @@ -921,7 +928,7 @@ void AutofillManager::OnTextFieldDidChangeImpl(const FormData& form, } bool AutofillManager::IsFormNonSecure(const FormData& form) const { - return IsFormOrClientNonSecure(client_, form); + return IsFormOrClientNonSecure(client(), form); } void AutofillManager::OnQueryFormFieldAutofillImpl( @@ -930,6 +937,10 @@ void AutofillManager::OnQueryFormFieldAutofillImpl( const FormFieldData& field, const gfx::RectF& transformed_box, bool autoselect_first_suggestion) { + if (base::FeatureList::IsEnabled(features::kAutofillDisableFilling)) { + return; + } + SetDataList(field.datalist_values, field.datalist_labels); external_delegate_->OnQuery(query_id, form, field, transformed_box); @@ -991,9 +1002,9 @@ void AutofillManager::OnQueryFormFieldAutofillImpl( // Suggestions come back asynchronously, so the Autocomplete manager will // handle sending the results back to the renderer. autocomplete_history_manager_->OnGetAutocompleteSuggestions( - query_id, client_->IsAutocompleteEnabled(), autoselect_first_suggestion, - field.name, field.value, field.form_control_type, - weak_ptr_factory_.GetWeakPtr()); + query_id, client()->IsAutocompleteEnabled(), + autoselect_first_suggestion, field.name, field.value, + field.form_control_type, weak_ptr_factory_.GetWeakPtr()); return; } @@ -1064,9 +1075,8 @@ void AutofillManager::FillOrPreviewCreditCardForm( credit_card_, *form_structure, *autofill_field, sync_state_); } - FillOrPreviewDataModelForm(action, query_id, form, field, /*profile=*/nullptr, - &credit_card_, /*cvc=*/nullptr, form_structure, - autofill_field); + FillOrPreviewDataModelForm(action, query_id, form, field, &credit_card_, + /*cvc=*/nullptr, form_structure, autofill_field); } void AutofillManager::FillOrPreviewProfileForm( @@ -1085,7 +1095,6 @@ void AutofillManager::FillOrPreviewProfileForm( } FillOrPreviewDataModelForm(action, query_id, form, field, &profile, - /*credit_card=*/nullptr, /*cvc=*/nullptr, form_structure, autofill_field); } @@ -1128,8 +1137,8 @@ void AutofillManager::FillCreditCardForm(int query_id, return; FillOrPreviewDataModelForm(AutofillDriver::FORM_DATA_ACTION_FILL, query_id, - form, field, /*profile=*/nullptr, &credit_card, - &cvc, form_structure, autofill_field); + form, field, &credit_card, &cvc, form_structure, + autofill_field); } void AutofillManager::FillProfileForm(const autofill::AutofillProfile& profile, @@ -1148,7 +1157,7 @@ void AutofillManager::OnFocusNoLongerOnForm(bool had_interacted_form) { ProcessPendingFormForUpload(); -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) // There is no way of determining whether ChromeVox is in use, so assume it's // being used. external_delegate_->OnAutofillAvailabilityEvent( @@ -1168,7 +1177,7 @@ void AutofillManager::OnFocusOnFormFieldImpl(const FormData& form, // are suggestions to present. Ignore if a screen reader is not present. If // the platform is ChromeOS, then assume ChromeVox is in use as there is no // way of determining whether it's being used from this point in the code. -#if !defined(OS_CHROMEOS) +#if !BUILDFLAG(IS_CHROMEOS_ASH) if (!external_delegate_->HasActiveScreenReader()) return; #endif @@ -1204,12 +1213,12 @@ void AutofillManager::OnDidFillAutofillFormData(const FormData& form, UpdatePendingForm(form); - std::set<FormType> form_types; // Find the FormStructure that corresponds to |form|. Use default form type if // form is not present in our cache, which will happen rarely. FormStructure* form_structure = FindCachedFormByRendererId(form.unique_renderer_id); + DenseSet<FormType> form_types; if (form_structure) { form_types = form_structure->GetFormTypes(); } @@ -1219,12 +1228,12 @@ void AutofillManager::OnDidFillAutofillFormData(const FormData& form, AutofillMetrics::LogUserHappinessMetric( AutofillMetrics::USER_DID_AUTOFILL, form_types, - client_->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); + client()->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); if (!user_did_autofill_) { user_did_autofill_ = true; AutofillMetrics::LogUserHappinessMetric( AutofillMetrics::USER_DID_AUTOFILL_ONCE, form_types, - client_->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); + client()->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); } UpdateInitialInteractionTimestamp(timestamp); @@ -1251,13 +1260,13 @@ void AutofillManager::DidShowSuggestions(bool has_autofill_suggestions, uint32_t profile_form_bitmask = data_util::DetermineGroups(*form_structure); AutofillMetrics::LogUserHappinessMetric( AutofillMetrics::SUGGESTIONS_SHOWN, autofill_field->Type().group(), - client_->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); + client()->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); if (!did_show_suggestions_) { did_show_suggestions_ = true; AutofillMetrics::LogUserHappinessMetric( AutofillMetrics::SUGGESTIONS_SHOWN_ONCE, autofill_field->Type().group(), - client_->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); + client()->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); } auto* logger = GetEventFormLogger(autofill_field->Type().group()); @@ -1267,7 +1276,7 @@ void AutofillManager::DidShowSuggestions(bool has_autofill_suggestions, sync_state_, driver()->IsIncognito()); } - if (autofill_field->Type().group() == CREDIT_CARD && + if (autofill_field->Type().group() == FieldTypeGroup::kCreditCard && base::FeatureList::IsEnabled( features::kAutofillCreditCardAuthentication)) { credit_card_access_manager_->PrepareToFetchCreditCard(); @@ -1279,7 +1288,7 @@ void AutofillManager::OnHidePopup() { return; autocomplete_history_manager_->CancelPendingQueries(this); - client_->HideAutofillPopup(PopupHidingReason::kRendererEvent); + client()->HideAutofillPopup(PopupHidingReason::kRendererEvent); } bool AutofillManager::GetDeletionConfirmationText(const base::string16& value, @@ -1399,6 +1408,7 @@ void AutofillManager::SetDataList(const std::vector<base::string16>& values, external_delegate_->SetCurrentDataListValues(values, labels); } + void AutofillManager::SelectFieldOptionsDidChange(const FormData& form) { // Look for a cached version of the form. It will be a null pointer if none is // found, which is fine. @@ -1413,53 +1423,10 @@ void AutofillManager::SelectFieldOptionsDidChange(const FormData& form) { TriggerRefill(form); } -void AutofillManager::OnLoadedServerPredictions( - std::string response, - const std::vector<FormSignature>& queried_form_signatures) { - // Get the current valid FormStructures represented by - // |queried_form_signatures|. - std::vector<FormStructure*> queried_forms; - queried_forms.reserve(queried_form_signatures.size()); - for (const auto& form_signature : queried_form_signatures) { - FindCachedFormsBySignature(form_signature, &queried_forms); - } - - // Each form signature in |queried_form_signatures| is supposed to be unique, - // and therefore appear only once. This ensures that - // FindCachedFormsBySignature() produces an output without duplicates in the - // forms. - // TODO(crbug/1064709): |queried_forms| could be a set data structure; their - // order should be irrelevant. - DCHECK_EQ(queried_forms.size(), - std::set<FormStructure*>(queried_forms.begin(), queried_forms.end()) - .size()); - - // If there are no current forms corresponding to the queried signatures, drop - // the query response. - if (queried_forms.empty()) - return; - - // Parse and store the server predictions. - FormStructure::ParseApiQueryResponse(std::move(response), queried_forms, - queried_form_signatures, - form_interactions_ukm_logger_.get()); - - // Will log quality metrics for each FormStructure based on the presence of - // autocomplete attributes, if available. - for (FormStructure* cur_form : queried_forms) { - cur_form->LogQualityMetricsBasedOnAutocomplete( - form_interactions_ukm_logger_.get()); - } - - // Send field type predictions to the renderer so that it can possibly - // annotate forms with the predicted types or add console warnings. - driver()->SendAutofillTypePredictionsToRenderer(queried_forms); - - LogAutofillTypePredictionsAvailable(log_manager_, queried_forms); - - // Forward form structures to the password generation manager to detect - // account creation forms. - driver()->PropagateAutofillPredictions(queried_forms); +void AutofillManager::PropagateAutofillPredictions( + content::RenderFrameHost* rfh, + const std::vector<FormStructure*>& forms) { + client()->PropagateAutofillPredictions(rfh, forms); } void AutofillManager::OnCreditCardFetched(bool did_succeed, @@ -1487,8 +1454,7 @@ void AutofillManager::OnCreditCardFetched(bool did_succeed, DCHECK(credit_card); FillCreditCardForm(credit_card_query_id_, credit_card_form_, credit_card_field_, *credit_card, cvc); - if (base::FeatureList::IsEnabled(features::kAutofillCacheServerCardInfo) && - credit_card->record_type() == CreditCard::FULL_SERVER_CARD) { + if (credit_card->record_type() == CreditCard::FULL_SERVER_CARD) { credit_card_access_manager_->CacheUnmaskedCardInfo(*credit_card, cvc); } } @@ -1502,18 +1468,11 @@ bool AutofillManager::IsAutofillEnabled() const { } bool AutofillManager::IsAutofillProfileEnabled() const { - return ::autofill::prefs::IsAutofillProfileEnabled(client_->GetPrefs()); + return ::autofill::prefs::IsAutofillProfileEnabled(client()->GetPrefs()); } bool AutofillManager::IsAutofillCreditCardEnabled() const { - return ::autofill::prefs::IsAutofillCreditCardEnabled(client_->GetPrefs()); -} - -// static -bool AutofillManager::IsRichQueryEnabled(version_info::Channel channel) { - return base::FeatureList::IsEnabled(features::kAutofillRichMetadataQueries) && - channel != version_info::Channel::STABLE && - channel != version_info::Channel::BETA; + return ::autofill::prefs::IsAutofillCreditCardEnabled(client()->GetPrefs()); } const FormData& AutofillManager::last_query_form() const { @@ -1547,8 +1506,8 @@ void AutofillManager::UploadFormDataAsyncCallback( submitted_form->ShouldBeQueried()) { submitted_form->LogQualityMetrics( submitted_form->form_parsed_timestamp(), interaction_time, - submission_time, form_interactions_ukm_logger_.get(), - did_show_suggestions_, observed_submission); + submission_time, form_interactions_ukm_logger(), did_show_suggestions_, + observed_submission); } if (submitted_form->ShouldBeUploaded()) UploadFormData(*submitted_form, observed_submission); @@ -1556,7 +1515,7 @@ void AutofillManager::UploadFormDataAsyncCallback( void AutofillManager::UploadFormData(const FormStructure& submitted_form, bool observed_submission) { - if (!download_manager_) + if (!download_manager()) return; // Check if the form is among the forms that were recently auto-filled. @@ -1577,10 +1536,10 @@ void AutofillManager::UploadFormData(const FormStructure& submitted_form, non_empty_types.insert(CREDIT_CARD_VERIFICATION_CODE); } - download_manager_->StartUploadRequest( + download_manager()->StartUploadRequest( submitted_form, was_autofilled, non_empty_types, /*login_form_signature=*/std::string(), observed_submission, - client_->GetPrefs()); + client()->GetPrefs()); } void AutofillManager::Reset() { @@ -1589,16 +1548,13 @@ void AutofillManager::Reset() { ProcessPendingFormForUpload(); DCHECK(!pending_form_data_); AutofillHandler::Reset(); - form_interactions_ukm_logger_.reset( - new AutofillMetrics::FormInteractionsUkmLogger( - client_->GetUkmRecorder(), client_->GetUkmSourceId())); - address_form_event_logger_.reset(new AddressFormEventLogger( - driver()->IsInMainFrame(), form_interactions_ukm_logger_.get(), client_)); - credit_card_form_event_logger_.reset(new CreditCardFormEventLogger( - driver()->IsInMainFrame(), form_interactions_ukm_logger_.get(), - personal_data_, client_)); - credit_card_access_manager_.reset(new CreditCardAccessManager( - driver(), client_, personal_data_, credit_card_form_event_logger_.get())); + address_form_event_logger_ = std::make_unique<AddressFormEventLogger>( + driver()->IsInMainFrame(), form_interactions_ukm_logger(), client()); + credit_card_form_event_logger_ = std::make_unique<CreditCardFormEventLogger>( + driver()->IsInMainFrame(), form_interactions_ukm_logger(), personal_data_, + client()); + credit_card_access_manager_ = std::make_unique<CreditCardAccessManager>( + driver(), client(), personal_data_, credit_card_form_event_logger_.get()); has_logged_autofill_enabled_ = false; has_logged_address_suggestions_count_ = false; @@ -1619,52 +1575,6 @@ void AutofillManager::Reset() { filling_context_by_unique_name_.clear(); } -AutofillManager::AutofillManager( - AutofillDriver* driver, - AutofillClient* client, - PersonalDataManager* personal_data, - AutocompleteHistoryManager* autocomplete_history_manager, - const std::string app_locale, - AutofillDownloadManagerState enable_download_manager, - std::unique_ptr<CreditCardAccessManager> cc_access_manager) - : AutofillHandler(driver, client->GetLogManager()), - client_(client), - log_manager_(client_->GetLogManager()), - app_locale_(app_locale), - personal_data_(personal_data), - field_filler_(app_locale, client->GetAddressNormalizer()), - autocomplete_history_manager_(autocomplete_history_manager->GetWeakPtr()), - form_interactions_ukm_logger_( - std::make_unique<AutofillMetrics::FormInteractionsUkmLogger>( - client->GetUkmRecorder(), - client->GetUkmSourceId())), - address_form_event_logger_(std::make_unique<AddressFormEventLogger>( - driver->IsInMainFrame(), - form_interactions_ukm_logger_.get(), - client_)), - credit_card_form_event_logger_( - std::make_unique<CreditCardFormEventLogger>( - driver->IsInMainFrame(), - form_interactions_ukm_logger_.get(), - personal_data_, - client_)), - is_rich_query_enabled_(IsRichQueryEnabled(client->GetChannel())) { - DCHECK(driver); - DCHECK(client_); - credit_card_access_manager_ = cc_access_manager - ? std::move(cc_access_manager) - : std::make_unique<CreditCardAccessManager>( - driver, client_, personal_data_, - credit_card_form_event_logger_.get()); - if (enable_download_manager == ENABLE_AUTOFILL_DOWNLOAD_MANAGER) { - version_info::Channel channel = client_->GetChannel(); - download_manager_.reset(new AutofillDownloadManager( - driver, this, GetAPIKeyForUrl(channel), client_->GetLogManager())); - } - CountryNames::SetLocaleString(app_locale_); - offer_manager_ = client_->GetAutofillOfferManager(); -} - bool AutofillManager::RefreshDataModels() { if (!IsAutofillEnabled()) return false; @@ -1717,25 +1627,19 @@ void AutofillManager::FillOrPreviewDataModelForm( int query_id, const FormData& form, const FormFieldData& field, - const AutofillProfile* optional_profile, - const CreditCard* optional_credit_card, + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card, const base::string16* optional_cvc, FormStructure* form_structure, AutofillField* autofill_field, bool is_refill) { - DCHECK(optional_profile || optional_credit_card); - DCHECK(optional_credit_card || !optional_cvc); + bool is_credit_card = + absl::holds_alternative<const CreditCard*>(profile_or_credit_card); + + DCHECK(is_credit_card || !optional_cvc); DCHECK(form_structure); DCHECK(autofill_field); - const AutofillDataModel& data_model = [&]() -> const AutofillDataModel& { - if (optional_profile) - return *optional_profile; - else - return *optional_credit_card; - }(); - bool is_credit_card = !!optional_credit_card; - LogBuffer buffer; buffer << "is credit card section: " << is_credit_card << Br{}; buffer << "is refill: " << is_refill << Br{}; @@ -1745,12 +1649,17 @@ void AutofillManager::FillOrPreviewDataModelForm( form_structure->RationalizePhoneNumbersInSection(autofill_field->section); FormData result = form; - DCHECK_EQ(form_structure->field_count(), form.fields.size()); + + // TODO(crbug/1203667#c9): Skip if the form has changed in the meantime, which + // may happen with refills. + if (form_structure->field_count() != form.fields.size()) + return; if (action == AutofillDriver::FORM_DATA_ACTION_FILL && !is_refill) { - SetFillingContext(*form_structure, std::make_unique<FillingContext>( - *autofill_field, optional_profile, - optional_credit_card, optional_cvc)); + SetFillingContext( + *form_structure, + std::make_unique<FillingContext>(*autofill_field, + profile_or_credit_card, optional_cvc)); } // Only record the types that are filled for an eventual refill if all the @@ -1765,7 +1674,8 @@ void AutofillManager::FillOrPreviewDataModelForm( // Count the number of times the value of a specific type was filled into the // form. - std::map<ServerFieldType, int> type_filling_count; + base::flat_map<ServerFieldType, size_t> type_filling_count; + type_filling_count.reserve(form_structure->field_count()); for (size_t i = 0; i < form_structure->field_count(); ++i) { std::string field_number = base::StringPrintf("Field %zu", i); @@ -1784,8 +1694,10 @@ void AutofillManager::FillOrPreviewDataModelForm( continue; } - // The field order should be the same in |form_structure| and |result|. - DCHECK(form_structure->field(i)->SameFieldAs(result.fields[i])); + // TODO(crbug/1203667#c9): Skip if the form has changed in the meantime, + // which may happen with refills. + if (!form_structure->field(i)->SameFieldAs(result.fields[i])) + continue; AutofillField* cached_field = form_structure->field(i); FieldTypeGroup field_group_type = cached_field->Type().group(); @@ -1794,8 +1706,9 @@ void AutofillManager::FillOrPreviewDataModelForm( // the sake of filling the synthetic fields. if (!cached_field->IsVisible()) { bool skip = result.fields[i].form_control_type != "select-one"; - form_interactions_ukm_logger_->LogHiddenRepresentationalFieldSkipDecision( - *form_structure, *cached_field, skip); + form_interactions_ukm_logger() + ->LogHiddenRepresentationalFieldSkipDecision(*form_structure, + *cached_field, skip); if (skip) { buffer << Tr{} << field_number << "Skipped: invisible field"; continue; @@ -1806,9 +1719,7 @@ void AutofillManager::FillOrPreviewDataModelForm( // is empty and its initial value (= cached value) was empty as well. A // similar check is done in ForEachMatchingFormFieldCommon(), which // frequently has false negatives. - if (base::FeatureList::IsEnabled( - features::kAutofillSkipFillingFieldsWithChangedValues) && - ((form.fields[i].properties_mask & kUserTyped)) && + if ((form.fields[i].properties_mask & kUserTyped) && (!form.fields[i].value.empty() || !form_structure->field(i)->value.empty()) && !cached_field->SameFieldAs(field)) { @@ -1827,7 +1738,7 @@ void AutofillManager::FillOrPreviewDataModelForm( continue; } - if (field_group_type == NO_GROUP) { + if (field_group_type == FieldTypeGroup::kNoGroup) { buffer << Tr{} << field_number << "Skipped: field type has no fillable group"; continue; @@ -1848,8 +1759,8 @@ void AutofillManager::FillOrPreviewDataModelForm( // Don't fill expired cards expiration date. if (data_util::IsCreditCardExpirationType(field_type) && - (!optional_credit_card || - optional_credit_card->IsExpired(AutofillClock::Now()))) { + (is_credit_card && absl::get<const CreditCard*>(profile_or_credit_card) + ->IsExpired(AutofillClock::Now()))) { buffer << Tr{} << field_number << "Skipped: don't fill expiration date of expired cards"; continue; @@ -1857,7 +1768,7 @@ void AutofillManager::FillOrPreviewDataModelForm( // A field with a specific type is only allowed to be filled a limited // number of times given by |TypeValueFormFillingLimit(field_type)|. - if (type_filling_count[field_type] >= + if (++type_filling_count[field_type] > TypeValueFormFillingLimit(field_type)) { buffer << Tr{} << field_number << "Skipped: field-type filling-limit reached"; @@ -1882,9 +1793,9 @@ void AutofillManager::FillOrPreviewDataModelForm( const base::string16 kEmptyCvc{}; std::string failure_to_fill; // Reason for failing to fill. - // Fill the non-empty value from |data_model| into the result vector, which - // will be sent to the renderer. - FillFieldWithValue(cached_field, data_model, &result.fields[i], + // Fill the non-empty value from |profile_or_credit_card| into the result + // vector, which will be sent to the renderer. + FillFieldWithValue(cached_field, profile_or_credit_card, &result.fields[i], should_notify, optional_cvc ? *optional_cvc : kEmptyCvc, data_util::DetermineGroups(*form_structure), &failure_to_fill); @@ -1892,10 +1803,6 @@ void AutofillManager::FillOrPreviewDataModelForm( bool has_value_after = !result.fields[i].value.empty(); bool is_autofilled_after = result.fields[i].is_autofilled; - // If the field was actually filled, increment the filling counter. - if (is_autofilled_after) - type_filling_count[field_type]++; - buffer << Tr{} << field_number << base::StringPrintf( "Fillable - has value: %d->%d; autofilled: %d->%d. %s", @@ -1913,14 +1820,14 @@ void AutofillManager::FillOrPreviewDataModelForm( if (autofilled_form_signatures_.size() > kMaxRecentFormSignaturesToRemember) autofilled_form_signatures_.pop_back(); - // Note that this may invalidate |data_model|. + // Note that this may invalidate |profile_or_credit_card|. if (action == AutofillDriver::FORM_DATA_ACTION_FILL && !is_refill) - personal_data_->RecordUseOf(data_model); + personal_data_->RecordUseOf(profile_or_credit_card); - if (log_manager_) { - log_manager_->Log() << LoggingScope::kFilling - << LogMessage::kSendFillingData << Br{} - << std::move(buffer); + if (log_manager()) { + log_manager()->Log() << LoggingScope::kFilling + << LogMessage::kSendFillingData << Br{} + << std::move(buffer); } driver()->SendFormDataToRenderer(query_id, action, result); } @@ -1965,8 +1872,9 @@ AutofillField* AutofillManager::GetAutofillField(const FormData& form, bool AutofillManager::FormHasAddressField(const FormData& form) { for (const FormFieldData& field : form.fields) { const AutofillField* autofill_field = GetAutofillField(form, field); - if (autofill_field && (autofill_field->Type().group() == ADDRESS_HOME || - autofill_field->Type().group() == ADDRESS_BILLING)) { + if (autofill_field && + (autofill_field->Type().group() == FieldTypeGroup::kAddressHome || + autofill_field->Type().group() == FieldTypeGroup::kAddressBilling)) { return true; } } @@ -2014,11 +1922,11 @@ std::vector<Suggestion> AutofillManager::GetCreditCardSuggestions( std::vector<Suggestion> suggestions = personal_data_->GetCreditCardSuggestions( type, SanitizeCreditCardFieldValue(field.value), - client_->AreServerCardsSupported()); + client()->AreServerCardsSupported()); if (base::FeatureList::IsEnabled( features::kAutofillEnableOffersInDownstream) && offer_manager_) { - offer_manager_->UpdateSuggestionsWithOffers(client_->GetLastCommittedURL(), + offer_manager_->UpdateSuggestionsWithOffers(client()->GetLastCommittedURL(), suggestions); } *should_display_gpay_logo = @@ -2033,117 +1941,82 @@ std::vector<Suggestion> AutofillManager::GetCreditCardSuggestions( return suggestions; } -void AutofillManager::OnFormsParsed(const std::vector<const FormData*>& forms, - const base::TimeTicks timestamp) { - DCHECK(!forms.empty()); +void AutofillManager::OnBeforeProcessParsedForms() { has_parsed_forms_ = true; // Record the current sync state to be used for metrics on this page. sync_state_ = personal_data_->GetSyncSigninState(); // Setup the url for metrics that we will collect for this form. - form_interactions_ukm_logger_->OnFormsParsed(client_->GetUkmSourceId()); - - driver()->HandleParsedForms(forms); - - std::vector<FormStructure*> non_queryable_forms; - std::vector<FormStructure*> queryable_forms; - std::set<FormType> form_types; - for (const FormData* form : forms) { - FormStructure* form_structure = - FindCachedFormByRendererId(form->unique_renderer_id); - if (!form_structure) { - NOTREACHED(); - continue; - } + form_interactions_ukm_logger()->OnFormsParsed(client()->GetUkmSourceId()); +} - if (data_util::ContainsPhone(data_util::DetermineGroups(*form_structure))) { - has_observed_phone_number_field_ = true; - } +void AutofillManager::OnFormProcessed(const FormData& form, + const FormStructure& form_structure) { + if (data_util::ContainsPhone(data_util::DetermineGroups(form_structure))) { + has_observed_phone_number_field_ = true; + } - // TODO(crbug.com/869482): avoid logging developer engagement multiple - // times for a given form if it or other forms on the page are dynamic. - LogDeveloperEngagementUkm(client_->GetUkmRecorder(), - client_->GetUkmSourceId(), form_structure); - std::set<FormType> current_form_types = form_structure->GetFormTypes(); - form_types.insert(current_form_types.begin(), current_form_types.end()); - - // Configure the query encoding for this form and add it to the appropriate - // collection of forms: queryable vs non-queryable. - form_structure->set_is_rich_query_enabled(is_rich_query_enabled_); - if (form_structure->ShouldBeQueried()) - queryable_forms.push_back(form_structure); - else - non_queryable_forms.push_back(form_structure); - - // Log the type of form that was parsed. - bool card_form = false; - bool address_form = false; - for (const auto& field : *form_structure) { - if (field->Type().group() == CREDIT_CARD) { - card_form = true; - } else if (IsAddressForm(field->Type().group())) { - address_form = true; - } else if (field->Type().html_type() == HTML_TYPE_ONE_TIME_CODE) { - has_observed_one_time_code_field_ = true; - } - } - if (card_form) { - credit_card_form_event_logger_->OnDidParseForm(*form_structure); - } - if (address_form) { - address_form_event_logger_->OnDidParseForm(*form_structure); - } + // TODO(crbug.com/869482): avoid logging developer engagement multiple + // times for a given form if it or other forms on the page are dynamic. + LogDeveloperEngagementUkm(client()->GetUkmRecorder(), + client()->GetUkmSourceId(), form_structure); - // If a form with the same name was previously filled, and there has not - // been a refill attempt on that form yet, start the process of triggering a - // refill. - if (ShouldTriggerRefill(*form_structure)) { - FillingContext* filling_context = GetFillingContext(*form_structure); - DCHECK(filling_context != nullptr); - - // If a timer for the refill was already running, it means the form - // changed again. Stop the timer and start it again. - if (filling_context->on_refill_timer.IsRunning()) - filling_context->on_refill_timer.AbandonAndStop(); - - // Start a new timer to trigger refill. - filling_context->on_refill_timer.Start( - FROM_HERE, - base::TimeDelta::FromMilliseconds(kWaitTimeForDynamicFormsMs), - base::BindRepeating(&AutofillManager::TriggerRefill, - weak_ptr_factory_.GetWeakPtr(), *form)); + // Log the type of form that was parsed. + bool card_form = false; + bool address_form = false; + for (const auto& field : form_structure) { + if (field->Type().group() == FieldTypeGroup::kCreditCard) { + card_form = true; + } else if (IsAddressForm(field->Type().group())) { + address_form = true; + } else if (field->Type().html_type() == HTML_TYPE_ONE_TIME_CODE) { + has_observed_one_time_code_field_ = true; } } - if (!queryable_forms.empty() || !non_queryable_forms.empty()) { - AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::FORMS_LOADED, form_types, - client_->GetSecurityLevelForUmaHistograms(), - /*profile_form_bitmask=*/0); - -#if defined(OS_IOS) - // Log this from same location as AutofillMetrics::FORMS_LOADED to ensure - // that KeyboardAccessoryButtonsIOS and UserHappiness UMA metrics will be - // directly comparable. - KeyboardAccessoryMetricsLogger::OnFormsLoaded(); -#endif + if (card_form) { + credit_card_form_event_logger_->OnDidParseForm(form_structure); + } + if (address_form) { + address_form_event_logger_->OnDidParseForm(form_structure); } - // Send the current type predictions to the renderer. For non-queryable forms - // this is all the information about them that will ever be available. The - // queryable forms will be updated once the field type query is complete. - driver()->SendAutofillTypePredictionsToRenderer(non_queryable_forms); - driver()->SendAutofillTypePredictionsToRenderer(queryable_forms); - LogAutofillTypePredictionsAvailable(log_manager_, non_queryable_forms); - LogAutofillTypePredictionsAvailable(log_manager_, queryable_forms); + // If a form with the same name was previously filled, and there has not + // been a refill attempt on that form yet, start the process of triggering a + // refill. + if (ShouldTriggerRefill(form_structure)) { + FillingContext* filling_context = GetFillingContext(form_structure); + DCHECK(filling_context != nullptr); - // Query the server if at least one of the forms was parsed. - if (!queryable_forms.empty() && download_manager_) { - download_manager_->StartQueryRequest(queryable_forms); + // If a timer for the refill was already running, it means the form + // changed again. Stop the timer and start it again. + if (filling_context->on_refill_timer.IsRunning()) + filling_context->on_refill_timer.AbandonAndStop(); + + // Start a new timer to trigger refill. + filling_context->on_refill_timer.Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(kWaitTimeForDynamicFormsMs), + base::BindRepeating(&AutofillManager::TriggerRefill, + weak_ptr_factory_.GetWeakPtr(), form)); } } +void AutofillManager::OnAfterProcessParsedForms( + const DenseSet<FormType>& form_types) { + AutofillMetrics::LogUserHappinessMetric( + AutofillMetrics::FORMS_LOADED, form_types, + client()->GetSecurityLevelForUmaHistograms(), + /*profile_form_bitmask=*/0); +#if defined(OS_IOS) + // Log this from same location as AutofillMetrics::FORMS_LOADED to ensure + // that KeyboardAccessoryButtonsIOS and UserHappiness UMA metrics will be + // directly comparable. + KeyboardAccessoryMetricsLogger::OnFormsLoaded(); +#endif +} + int AutofillManager::BackendIDToInt(const std::string& backend_id) const { if (!base::IsValidGUID(backend_id)) return 0; @@ -2250,6 +2123,9 @@ void AutofillManager::DeterminePossibleFieldTypesForUpload( if (IsUPIVirtualPaymentAddress(value)) matching_types.insert(UPI_VPA); + if (field->state_is_a_matching_type()) + matching_types.insert(ADDRESS_HOME_STATE); + if (matching_types.empty()) { matching_types.insert(UNKNOWN_TYPE); ServerFieldTypeValidityStateMap matching_types_validities; @@ -2298,10 +2174,10 @@ void AutofillManager::DisambiguateUploadTypes(FormStructure* form) { bool undisambiuatable_types = false; for (const auto& type : upload_types) { switch (AutofillType(type).group()) { - case CREDIT_CARD: + case FieldTypeGroup::kCreditCard: ++credit_card_type_count; break; - case NAME: + case FieldTypeGroup::kName: ++name_type_count; break; // If there is any other type left, do not disambiguate. @@ -2399,7 +2275,8 @@ void AutofillManager::DisambiguateNameUploadTypes( AutofillField* prev_field = form->field(index); if (!IsNameType(*prev_field)) { has_found_previous_type = true; - is_previous_credit_card = prev_field->Type().group() == CREDIT_CARD; + is_previous_credit_card = + prev_field->Type().group() == FieldTypeGroup::kCreditCard; } } @@ -2411,7 +2288,8 @@ void AutofillManager::DisambiguateNameUploadTypes( AutofillField* next_field = form->field(index); if (!IsNameType(*next_field)) { has_found_next_type = true; - is_next_credit_card = next_field->Type().group() == CREDIT_CARD; + is_next_credit_card = + next_field->Type().group() == FieldTypeGroup::kCreditCard; } } @@ -2435,15 +2313,17 @@ void AutofillManager::DisambiguateNameUploadTypes( } } -void AutofillManager::FillFieldWithValue(AutofillField* autofill_field, - const AutofillDataModel& data_model, - FormFieldData* field_data, - bool should_notify, - const base::string16& cvc, - uint32_t profile_form_bitmask, - std::string* failure_to_fill) { - if (field_filler_.FillFormField(*autofill_field, data_model, field_data, cvc, - failure_to_fill)) { +void AutofillManager::FillFieldWithValue( + AutofillField* autofill_field, + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card, + FormFieldData* field_data, + bool should_notify, + const base::string16& cvc, + uint32_t profile_form_bitmask, + std::string* failure_to_fill) { + if (field_filler_.FillFormField(*autofill_field, profile_or_credit_card, + field_data, cvc, failure_to_fill)) { if (failure_to_fill) *failure_to_fill = "Decided to fill"; // Mark the cached field as autofilled, so that we can detect when a @@ -2456,13 +2336,17 @@ void AutofillManager::FillFieldWithValue(AutofillField* autofill_field, field_data->is_autofilled = true; AutofillMetrics::LogUserHappinessMetric( AutofillMetrics::FIELD_WAS_AUTOFILLED, autofill_field->Type().group(), - client_->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); + client()->GetSecurityLevelForUmaHistograms(), profile_form_bitmask); if (should_notify) { - client_->DidFillOrPreviewField( - /*value=*/data_model.GetInfo(autofill_field->Type(), app_locale_), - /*profile_full_name=*/data_model.GetInfo(AutofillType(NAME_FULL), - app_locale_)); + DCHECK(absl::holds_alternative<const AutofillProfile*>( + profile_or_credit_card)); + const AutofillProfile* profile = + absl::get<const AutofillProfile*>(profile_or_credit_card); + client()->DidFillOrPreviewField( + /*value=*/profile->GetInfo(autofill_field->Type(), app_locale_), + /*profile_full_name=*/profile->GetInfo(AutofillType(NAME_FULL), + app_locale_)); } } } @@ -2589,22 +2473,28 @@ void AutofillManager::TriggerRefill(const FormData& form) { return; FormFieldData field = *autofill_field; - if (filling_context->credit_card) { + if (absl::holds_alternative<std::pair<CreditCard, base::string16>>( + filling_context->profile_or_credit_card_with_cvc)) { FillOrPreviewDataModelForm( AutofillDriver::RendererFormDataAction::FORM_DATA_ACTION_FILL, /*query_id=*/-1, form, field, - /*profile=*/nullptr, &filling_context->credit_card.value().first, - &filling_context->credit_card.value().second, form_structure, - autofill_field, + &absl::get<std::pair<CreditCard, base::string16>>( + filling_context->profile_or_credit_card_with_cvc) + .first, + &absl::get<std::pair<CreditCard, base::string16>>( + filling_context->profile_or_credit_card_with_cvc) + .second, + form_structure, autofill_field, /*is_refill=*/true); } - if (filling_context->profile) { + if (absl::holds_alternative<AutofillProfile>( + filling_context->profile_or_credit_card_with_cvc)) { FillOrPreviewDataModelForm( AutofillDriver::RendererFormDataAction::FORM_DATA_ACTION_FILL, - /*query_id=*/-1, form, field, &filling_context->profile.value(), - /*credic_card=*/nullptr, /*cvc=*/nullptr, form_structure, - autofill_field, - /*is_refill=*/true); + /*query_id=*/-1, form, field, + &absl::get<AutofillProfile>( + filling_context->profile_or_credit_card_with_cvc), + /*cvc=*/nullptr, form_structure, autofill_field, /*is_refill=*/true); } } @@ -2627,7 +2517,7 @@ void AutofillManager::GetAvailableSuggestions( // Log interactions of forms that are autofillable. if (got_autofillable_form) { - if (context->focused_field->Type().group() == CREDIT_CARD) { + if (context->focused_field->Type().group() == FieldTypeGroup::kCreditCard) { context->is_filling_credit_card = true; } auto* logger = GetEventFormLogger(context->focused_field->Type().group()); @@ -2640,10 +2530,10 @@ void AutofillManager::GetAvailableSuggestions( // If the feature is enabled and this is a mixed content form, we show a // warning message and don't offer autofill. The warning is shown even if // there are no autofill suggestions available. - if (IsFormMixedContent(client_, form) && + if (IsFormMixedContent(client(), form) && base::FeatureList::IsEnabled( features::kAutofillPreventMixedFormsFilling) && - client_->GetPrefs()->GetBoolean(::prefs::kMixedFormsWarningsEnabled)) { + client()->GetPrefs()->GetBoolean(::prefs::kMixedFormsWarningsEnabled)) { suggestions->clear(); // If the user begins typing, we interpret that as dismissing the warning. // No suggestions are allowed, but the warning is no longer shown. @@ -2684,15 +2574,6 @@ void AutofillManager::GetAvailableSuggestions( return; } } else { - // On desktop, don't return non credit card related suggestions for forms - // or fields that have the "autocomplete" attribute set to off, only if - // the feature to always fill addresses is off. - if (!base::FeatureList::IsEnabled(features::kAutofillAlwaysFillAddresses) && - IsDesktopPlatform() && !field.should_autocomplete) { - context->suppress_reason = SuppressReason::kAutocompleteOff; - return; - } - *suggestions = GetProfileSuggestions(*context->form_structure, field, *context->focused_field); } @@ -2741,7 +2622,7 @@ bool AutofillManager::ShouldShowVirtualCardOption( // If merchant is not allowed, return false. std::vector<std::string> allowed_merchants = - client_->GetAllowedMerchantsForVirtualCards(); + client()->GetAllowedMerchantsForVirtualCards(); if (std::find(allowed_merchants.begin(), allowed_merchants.end(), form_structure->source_url().spec()) == allowed_merchants.end()) { @@ -2771,34 +2652,64 @@ bool AutofillManager::ShouldShowVirtualCardOption( FormEventLoggerBase* AutofillManager::GetEventFormLogger( FieldTypeGroup field_type_group) const { switch (field_type_group) { - case NAME: - case NAME_BILLING: - case EMAIL: - case COMPANY: - case ADDRESS_HOME: - case ADDRESS_BILLING: - case PHONE_HOME: - case PHONE_BILLING: + case FieldTypeGroup::kName: + case FieldTypeGroup::kNameBilling: + case FieldTypeGroup::kEmail: + case FieldTypeGroup::kCompany: + case FieldTypeGroup::kAddressHome: + case FieldTypeGroup::kAddressBilling: + case FieldTypeGroup::kPhoneHome: + case FieldTypeGroup::kPhoneBilling: return address_form_event_logger_.get(); - case CREDIT_CARD: + case FieldTypeGroup::kCreditCard: return credit_card_form_event_logger_.get(); - case TRANSACTION: - case PASSWORD_FIELD: - case USERNAME_FIELD: - case NO_GROUP: - case UNFILLABLE: + case FieldTypeGroup::kTransaction: + case FieldTypeGroup::kPasswordField: + case FieldTypeGroup::kUsernameField: + case FieldTypeGroup::kNoGroup: + case FieldTypeGroup::kUnfillable: return nullptr; } NOTREACHED(); return nullptr; } -std::string AutofillManager::GetPageLanguage() const { - DCHECK(client_); - const translate::LanguageState* language_state = client_->GetLanguageState(); - if (language_state) - return language_state->original_language(); - return std::string(); +void AutofillManager::PreProcessStateMatchingTypes( + const std::vector<AutofillProfile>& profiles, + FormStructure* form_structure) { + if (!base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap)) { + return; + } + + for (const auto& profile : profiles) { + base::Optional<AlternativeStateNameMap::CanonicalStateName> + canonical_state_name_from_profile = + profile.GetAddress().GetCanonicalizedStateName(); + + if (!canonical_state_name_from_profile) + continue; + + const AutofillType kCountryCode(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE); + const base::string16& country_code = + profile.GetInfo(kCountryCode, app_locale_); + + for (auto& field : *form_structure) { + if (field->state_is_a_matching_type()) + continue; + + base::Optional<AlternativeStateNameMap::CanonicalStateName> + canonical_state_name_from_text = + AlternativeStateNameMap::GetCanonicalStateName( + base::UTF16ToUTF8(country_code), field->value); + + if (canonical_state_name_from_text && + canonical_state_name_from_text.value() == + canonical_state_name_from_profile.value()) { + field->set_state_is_a_matching_type(); + } + } + } } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h index d2cecaf5e00..ab3482f1eb9 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.h +++ b/chromium/components/autofill/core/browser/autofill_manager.h @@ -24,10 +24,9 @@ #include "build/build_config.h" #include "components/autofill/core/browser/autocomplete_history_manager.h" #include "components/autofill/core/browser/autofill_client.h" -#include "components/autofill/core/browser/autofill_download_manager.h" #include "components/autofill/core/browser/autofill_driver.h" +#include "components/autofill/core/browser/autofill_external_delegate.h" #include "components/autofill/core/browser/autofill_handler.h" -#include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/field_filler.h" #include "components/autofill/core/browser/form_types.h" #include "components/autofill/core/browser/metrics/address_form_event_logger.h" @@ -39,8 +38,10 @@ #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/sync_utils.h" #include "components/autofill/core/browser/ui/popup_types.h" +#include "components/autofill/core/common/dense_set.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/signatures.h" +#include "third_party/abseil-cpp/absl/types/variant.h" namespace gfx { class RectF; @@ -48,9 +49,6 @@ class RectF; namespace autofill { -class AutofillDataModel; -class AutofillDownloadManager; -class AutofillExternalDelegate; class AutofillField; class AutofillClient; class AutofillManagerTestDelegate; @@ -58,7 +56,6 @@ class AutofillProfile; class AutofillType; class CreditCard; class FormStructureBrowserTest; -class LogManager; struct FormData; struct FormFieldData; @@ -78,7 +75,6 @@ enum class ValuePatternsMetric { // Manages saving and restoring the user's personal information entered into web // forms. One per frame; owned by the AutofillDriver. class AutofillManager : public AutofillHandler, - public AutofillDownloadManager::Observer, public AutocompleteHistoryManager::SuggestionsHandler, public CreditCardAccessManager::Accessor { public: @@ -88,9 +84,6 @@ class AutofillManager : public AutofillHandler, AutofillDownloadManagerState enable_download_manager); ~AutofillManager() override; - // Sets an external delegate. - void SetExternalDelegate(AutofillExternalDelegate* delegate); - void ShowAutofillSettings(bool show_credit_card_settings); // Whether the |field| should show an entry to scan a credit card. @@ -177,11 +170,7 @@ class AutofillManager : public AutofillHandler, // Returns true only if the previewed form should be cleared. bool ShouldClearPreviewedForm(); - AutofillClient* client() { return client_; } - - AutofillDownloadManager* download_manager() { - return download_manager_.get(); - } + AutofillOfferManager* offer_manager() { return offer_manager_; } CreditCardAccessManager* credit_card_access_manager() { return credit_card_access_manager_.get(); @@ -227,6 +216,9 @@ class AutofillManager : public AutofillHandler, void OnDidEndTextFieldEditing() override; void OnHidePopup() override; void SelectFieldOptionsDidChange(const FormData& form) override; + void PropagateAutofillPredictions( + content::RenderFrameHost* rfh, + const std::vector<FormStructure*>& forms) override; void Reset() override; // AutocompleteHistoryManager::SuggestionsHandler: @@ -251,10 +243,6 @@ class AutofillManager : public AutofillHandler, // to be uploadable. Exposed for testing. bool ShouldUploadForm(const FormStructure& form); - // Rich queries are enabled by feature flag iff this chrome instance is - // neither on the STABLE nor BETA release channel. - static bool IsRichQueryEnabled(version_info::Channel channel); - // Returns the last form the autofill manager considered in this frame. virtual const FormData& last_query_form() const; @@ -268,6 +256,11 @@ class AutofillManager : public AutofillHandler, } #if defined(UNIT_TEST) + void SetExternalDelegateForTest( + std::unique_ptr<AutofillExternalDelegate> external_delegate) { + external_delegate_ = std::move(external_delegate); + } + // A public wrapper that calls |DeterminePossibleFieldTypesForUpload| for // testing purposes only. static void DeterminePossibleFieldTypesForUploadForTest( @@ -281,27 +274,12 @@ class AutofillManager : public AutofillHandler, app_locale, submitted_form); } - // A public wrapper that calls |OnLoadedServerPredictions| for testing - // purposes only. - void OnLoadedServerPredictionsForTest( - std::string response, - const std::vector<FormSignature>& queried_form_signatures) { - OnLoadedServerPredictions(response, queried_form_signatures); - } - // A public wrapper that calls |MakeFrontendID| for testing purposes only. int MakeFrontendIDForTest(const std::string& cc_backend_id, const std::string& profile_backend_id) const { return MakeFrontendID(cc_backend_id, profile_backend_id); } - // A public wrapper that calls |form_interactions_ukm_logger| for testing - // purposes only. - AutofillMetrics::FormInteractionsUkmLogger* - form_interactions_ukm_logger_for_test() { - return form_interactions_ukm_logger(); - } - // A public wrapper that calls |ShouldTriggerRefill| for testing purposes // only. bool ShouldTriggerRefillForTest(const FormStructure& form_structure) { @@ -310,6 +288,14 @@ class AutofillManager : public AutofillHandler, // A public wrapper that calls |TriggerRefill| for testing purposes only. void TriggerRefillForTest(const FormData& form) { TriggerRefill(form); } + + // A public wrapper that calls |PreProcessStateMatchingTypes| for testing + // purposes. + void PreProcessStateMatchingTypesForTest( + const std::vector<AutofillProfile>& profiles, + FormStructure* form_structure) { + PreProcessStateMatchingTypes(profiles, form_structure); + } #endif protected: @@ -371,34 +357,31 @@ class AutofillManager : public AutofillHandler, void OnSelectControlDidChangeImpl(const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box) override; - bool ShouldParseForms(const std::vector<FormData>& forms, - const base::TimeTicks timestamp) override; - void OnFormsParsed(const std::vector<const FormData*>& forms, - const base::TimeTicks timestamp) override; - - AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger() { - return form_interactions_ukm_logger_.get(); - } - - // Exposed for testing. - void set_download_manager(AutofillDownloadManager* manager) { - download_manager_.reset(manager); - } - - // Exposed for testing. - bool is_rich_query_enabled() const { return is_rich_query_enabled_; } + bool ShouldParseForms(const std::vector<FormData>& forms) override; + void OnBeforeProcessParsedForms() override; + void OnFormProcessed(const FormData& form, + const FormStructure& form_structure) override; + void OnAfterProcessParsedForms(const DenseSet<FormType>& form_types) override; // Exposed for testing. FormData* pending_form_data() { return pending_form_data_.get(); } private: + FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, PageLanguageGetsCorrectlySet); + FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, + PageLanguageGetsCorrectlyDetected); + FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, DoNotFillIfFormFieldChanged); + FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, DoNotFillIfFormFieldRemoved); + // Keeps track of the filling context for a form, used to make refill attemps. struct FillingContext { - // |optional_profile| or |optional_credit_card| must be non-null. - // If |optional_credit_card| is non-null, |optional_cvc| may be non-null. + // |profile_or_credit_card| contains either AutofillProfile or CreditCard + // and must be non-null. + // If |profile_or_credit_card| contains a CreditCard, |optional_cvc| may be + // non-null. FillingContext(const AutofillField& field, - const AutofillProfile* optional_profile, - const CreditCard* optional_credit_card, + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card, const base::string16* optional_cvc); ~FillingContext(); @@ -407,8 +390,8 @@ class AutofillManager : public AutofillHandler, // The profile or credit card that was used for the initial fill. // The std::string associated with the credit card is the CVC, which may be // empty. - const base::Optional<AutofillProfile> profile; - const base::Optional<std::pair<CreditCard, base::string16>> credit_card; + absl::variant<AutofillProfile, std::pair<CreditCard, base::string16>> + profile_or_credit_card_with_cvc; // Possible identifiers of the field that was focused when the form was // initially filled. A refill shall be triggered from the same field. // TODO(crbug/896689): Remove |filled_field_unique_name|. @@ -451,11 +434,6 @@ class AutofillManager : public AutofillHandler, SuppressReason suppress_reason = SuppressReason::kNotSuppressed; }; - // AutofillDownloadManager::Observer: - void OnLoadedServerPredictions( - std::string response, - const std::vector<FormSignature>& queried_form_signatures) override; - // CreditCardAccessManager::Accessor void OnCreditCardFetched( bool did_succeed, @@ -497,16 +475,17 @@ class AutofillManager : public AutofillHandler, const AutofillProfile& profile); // Fills or previews |data_model| in the |form|. - void FillOrPreviewDataModelForm(AutofillDriver::RendererFormDataAction action, - int query_id, - const FormData& form, - const FormFieldData& field, - const AutofillProfile* optional_profile, - const CreditCard* optional_credit_card, - const base::string16* optional_cvc, - FormStructure* form_structure, - AutofillField* autofill_field, - bool is_refill = false); + void FillOrPreviewDataModelForm( + AutofillDriver::RendererFormDataAction action, + int query_id, + const FormData& form, + const FormFieldData& field, + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card, + const base::string16* optional_cvc, + FormStructure* form_structure, + AutofillField* autofill_field, + bool is_refill = false); // Creates a FormStructure using the FormData received from the renderer. Will // return an empty scoped_ptr if the data should not be processed for upload @@ -581,13 +560,15 @@ class AutofillManager : public AutofillHandler, size_t current_index, const ServerFieldTypeSet& upload_types); - void FillFieldWithValue(AutofillField* autofill_field, - const AutofillDataModel& data_model, - FormFieldData* field_data, - bool should_notify, - const base::string16& cvc, - uint32_t profile_form_bitmask, - std::string* failure_to_fill); + void FillFieldWithValue( + AutofillField* autofill_field, + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card, + FormFieldData* field_data, + bool should_notify, + const base::string16& cvc, + uint32_t profile_form_bitmask, + std::string* failure_to_fill); // TODO(crbug/896689): Remove code duplication once experiment is finished. void SetFillingContext(const FormStructure& form, @@ -617,8 +598,13 @@ class AutofillManager : public AutofillHandler, std::vector<Suggestion>* suggestions, SuggestionsContext* context); - // Retrieves the page language from |client_| - std::string GetPageLanguage() const override; + // For each submitted field in the |form_structure|, it determines whether + // |ADDRESS_HOME_STATE| is a possible matching type. + // This method is intended to run matching type detection on the browser UI + // thread. + void PreProcessStateMatchingTypes( + const std::vector<AutofillProfile>& profiles, + FormStructure* form_structure); #if !defined(OS_ANDROID) && !defined(OS_IOS) // Whether to show the option to use virtual card in the autofill popup. @@ -633,9 +619,9 @@ class AutofillManager : public AutofillHandler, void SetDataList(const std::vector<base::string16>& values, const std::vector<base::string16>& labels); - AutofillClient* const client_; - - LogManager* log_manager_; + // Delegate to perform external processing (display, selection) on + // our behalf. + std::unique_ptr<AutofillExternalDelegate> external_delegate_; std::string app_locale_; @@ -650,18 +636,10 @@ class AutofillManager : public AutofillHandler, base::circular_deque<std::string> autofilled_form_signatures_; - // Handles queries and uploads to Autofill servers. Will be NULL if - // the download manager functionality is disabled. - std::unique_ptr<AutofillDownloadManager> download_manager_; - // Handles single-field autocomplete form data. // May be NULL. NULL indicates OTR. base::WeakPtr<AutocompleteHistoryManager> autocomplete_history_manager_; - // Utility for logging URL keyed metrics. - std::unique_ptr<AutofillMetrics::FormInteractionsUkmLogger> - form_interactions_ukm_logger_; - // Utilities for logging form events. std::unique_ptr<AddressFormEventLogger> address_form_event_logger_; std::unique_ptr<CreditCardFormEventLogger> credit_card_form_event_logger_; @@ -698,7 +676,8 @@ class AutofillManager : public AutofillHandler, std::unique_ptr<CreditCardAccessManager> credit_card_access_manager_; // The autofill offer manager, used to to retrieve offers for card - // suggestions. + // suggestions. Initialized when AutofillManager is created. |offer_manager_| + // is never null. AutofillOfferManager* offer_manager_; // Collected information about the autofill form where a credit card will be @@ -720,10 +699,6 @@ class AutofillManager : public AutofillHandler, mutable std::map<std::string, int> backend_to_int_map_; mutable std::map<int, std::string> int_to_backend_map_; - // Delegate to perform external processing (display, selection) on - // our behalf. Weak. - AutofillExternalDelegate* external_delegate_ = nullptr; - // Delegate used in test to get notifications on certain events. AutofillManagerTestDelegate* test_delegate_ = nullptr; @@ -735,9 +710,6 @@ class AutofillManager : public AutofillHandler, std::map<base::string16, std::unique_ptr<FillingContext>> filling_context_by_unique_name_; - // Tracks whether or not rich query encoding is enabled for this client. - const bool is_rich_query_enabled_ = false; - // Used to record metrics. This should be set at the beginning of the // interaction and re-used throughout the context of this manager. AutofillSyncSigninState sync_state_ = AutofillSyncSigninState::kNumSyncStates; diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc index d801bda0abb..d00a45b5848 100644 --- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc @@ -30,14 +30,15 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" +#include "build/chromeos_buildflags.h" #include "components/autofill/core/browser/autocomplete_history_manager.h" #include "components/autofill/core/browser/autofill_download_manager.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h" #include "components/autofill/core/browser/metrics/form_events.h" #include "components/autofill/core/browser/mock_autocomplete_history_manager.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" #include "components/autofill/core/browser/payments/test_credit_card_save_manager.h" #include "components/autofill/core/browser/payments/test_credit_card_save_strike_database.h" #include "components/autofill/core/browser/payments/test_payments_client.h" @@ -69,6 +70,7 @@ #include "components/security_state/core/security_state.h" #include "components/strings/grit/components_strings.h" #include "components/sync/driver/test_sync_service.h" +#include "components/translate/core/common/language_detection_details.h" #include "components/variations/variations_associated_data.h" #include "components/version_info/channel.h" #include "net/base/url_util.h" @@ -79,6 +81,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/rect.h" #include "url/gurl.h" +#include "url/url_canon.h" using base::ASCIIToUTF16; using base::UTF8ToUTF16; @@ -86,6 +89,8 @@ using testing::_; using testing::AnyOf; using testing::AtLeast; using testing::Contains; +using testing::DoAll; +using testing::Each; using testing::ElementsAre; using testing::HasSubstr; using testing::Not; @@ -182,13 +187,8 @@ void ExpectFilledForm(int page_id, EXPECT_EQ(expected_page_id, page_id); EXPECT_EQ(ASCIIToUTF16("MyForm"), filled_form.name); - if (has_credit_card_fields) { - EXPECT_EQ(GURL("https://myform.com/form.html"), filled_form.url); - EXPECT_EQ(GURL("https://myform.com/submit.html"), filled_form.action); - } else { - EXPECT_EQ(GURL("http://myform.com/form.html"), filled_form.url); - EXPECT_EQ(GURL("http://myform.com/submit.html"), filled_form.action); - } + EXPECT_EQ(GURL("https://myform.com/form.html"), filled_form.url); + EXPECT_EQ(GURL("https://myform.com/submit.html"), filled_form.action); size_t form_size = 0; if (has_address_fields) @@ -326,6 +326,7 @@ class AutofillManagerTest : public testing::Test { personal_data_.Init(/*profile_database=*/database_, /*account_database=*/nullptr, /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, @@ -360,17 +361,20 @@ class AutofillManagerTest : public testing::Test { autofill_manager_ = std::make_unique<TestAutofillManager>( autofill_driver_.get(), &autofill_client_, &personal_data_, autocomplete_history_manager_.get()); - download_manager_ = new MockAutofillDownloadManager( + + auto download_manager = std::make_unique<MockAutofillDownloadManager>( autofill_driver_.get(), autofill_manager_.get()); - // AutofillManager takes ownership of |download_manager_|. - autofill_manager_->set_download_manager(download_manager_); - external_delegate_ = std::make_unique<TestAutofillExternalDelegate>( + download_manager_ = download_manager.get(); + autofill_manager_->set_download_manager_for_test( + std::move(download_manager)); + + auto external_delegate = std::make_unique<TestAutofillExternalDelegate>( autofill_manager_.get(), autofill_driver_.get(), /*call_parent_methods=*/false); - autofill_manager_->SetExternalDelegate(external_delegate_.get()); + external_delegate_ = external_delegate.get(); + autofill_manager_->SetExternalDelegateForTest(std::move(external_delegate)); - std::unique_ptr<TestStrikeDatabase> test_strike_database = - std::make_unique<TestStrikeDatabase>(); + auto test_strike_database = std::make_unique<TestStrikeDatabase>(); strike_database_ = test_strike_database.get(); autofill_client_.set_test_strike_database(std::move(test_strike_database)); @@ -431,7 +435,6 @@ class AutofillManagerTest : public testing::Test { // Order of destruction is important as AutofillManager relies on // PersonalDataManager to be around when it gets destroyed. autofill_manager_.reset(); - autofill_driver_.reset(); personal_data_.SetPrefService(nullptr); personal_data_.ClearCreditCards(); @@ -463,7 +466,7 @@ class AutofillManagerTest : public testing::Test { } void FormsSeen(const std::vector<FormData>& forms) { - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); } void FormSubmitted(const FormData& form) { @@ -517,6 +520,14 @@ class AutofillManagerTest : public testing::Test { form->url = GURL("https://myform.com/form.html"); form->action = GURL("https://myform.com/submit.html"); } else { + // If we are testing a form that submits over HTTP, we also need to set + // the main frame to HTTP, otherwise mixed form warnings will trigger and + // autofill will be disabled. + GURL::Replacements replacements; + replacements.SetScheme(url::kHttpScheme, + url::Component(0, strlen(url::kHttpScheme))); + autofill_client_.set_form_origin( + autofill_client_.form_origin().ReplaceComponents(replacements)); form->url = GURL("http://myform.com/form.html"); form->action = GURL("http://myform.com/submit.html"); } @@ -628,14 +639,13 @@ class AutofillManagerTest : public testing::Test { MockAutofillClient autofill_client_; std::unique_ptr<MockAutofillDriver> autofill_driver_; std::unique_ptr<TestAutofillManager> autofill_manager_; - std::unique_ptr<TestAutofillExternalDelegate> external_delegate_; + TestAutofillExternalDelegate* external_delegate_; scoped_refptr<AutofillWebDataService> database_; MockAutofillDownloadManager* download_manager_; TestPersonalDataManager personal_data_; std::unique_ptr<MockAutocompleteHistoryManager> autocomplete_history_manager_; base::test::ScopedFeatureList scoped_feature_list_; TestStrikeDatabase* strike_database_; - TestPatternProvider test_pattern_provider_; private: int ToHistogramSample(AutofillMetrics::CardUploadDecisionMetric metric) { @@ -715,21 +725,25 @@ class AutofillManagerStructuredProfileTest void InitializeFeatures(); - bool StructuredNames() const { return structured_names_enabled_; } + bool StructuredNamesAndAddresses() const { + return structured_names_and_addresses_; + } private: - bool structured_names_enabled_; + bool structured_names_and_addresses_; base::test::ScopedFeatureList scoped_features_; }; void AutofillManagerStructuredProfileTest::InitializeFeatures() { - structured_names_enabled_ = GetParam(); - if (structured_names_enabled_) { - scoped_features_.InitAndEnableFeature( - features::kAutofillEnableSupportForMoreStructureInNames); + structured_names_and_addresses_ = GetParam(); + + std::vector<base::Feature> features = { + features::kAutofillEnableSupportForMoreStructureInAddresses, + features::kAutofillEnableSupportForMoreStructureInNames}; + if (structured_names_and_addresses_) { + scoped_features_.InitWithFeatures(features, {}); } else { - scoped_features_.InitAndDisableFeature( - features::kAutofillEnableSupportForMoreStructureInNames); + scoped_features_.InitWithFeatures({}, features); } } @@ -1226,8 +1240,8 @@ TEST_P(AutofillManagerStructuredProfileTest, // Set up our form data. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); FormFieldData field; test::CreateTestFormField("Username", "username", "", "text", &field); @@ -1653,7 +1667,8 @@ TEST_P(AutofillManagerStructuredProfileTest, FormsSeen(forms); // Set the field being edited to CC field. const FormFieldData& credit_card_number_field = form.fields[1]; - const std::string google_issued_card_value = "Google"; + const std::string google_issued_card_value = base::JoinString( + {"Plex Mastercard ", test::ObfuscatedCardDigitsAsUTF8("4444")}, ""); #if defined(OS_ANDROID) || defined(OS_IOS) const std::string google_issued_card_label = std::string("10/98"); #else @@ -1692,13 +1707,16 @@ TEST_P(AutofillManagerStructuredProfileTest, // Set the field being edited to the cardholder name field. const FormFieldData& cardholder_name_field = form.fields[0]; #if defined(OS_ANDROID) - const std::string google_issued_card_label = std::string("Google"); + const std::string google_issued_card_label = base::JoinString( + {"Plex Mastercard ", test::ObfuscatedCardDigitsAsUTF8("4444")}, ""); #elif defined(OS_IOS) const std::string google_issued_card_label = test::ObfuscatedCardDigitsAsUTF8("4444"); #else - const std::string google_issued_card_label = - std::string("Google, expires on 10/98"); + const std::string google_issued_card_label = base::JoinString( + {"Plex Mastercard ", test::ObfuscatedCardDigitsAsUTF8("4444"), + ", expires on 10/98"}, + ""); #endif GetAutofillSuggestions(form, cardholder_name_field); @@ -1790,16 +1808,19 @@ TEST_P(AutofillManagerStructuredProfileTest, GetAutofillSuggestions(form, field); // Test that we sent the right values to the external delegate. - CheckSuggestions(kDefaultPageID, - Suggestion(l10n_util::GetStringUTF8( - IDS_AUTOFILL_WARNING_INSECURE_CONNECTION), - "", "", -1)); + CheckSuggestions( + kDefaultPageID, + Suggestion(l10n_util::GetStringUTF8(IDS_AUTOFILL_WARNING_MIXED_FORM), "", + "", -26)); - // Clear the test credit cards and try again -- we shouldn't return a warning. + // Clear the test credit cards and try again -- we should still show the + // mixed form warning. personal_data_.ClearCreditCards(); GetAutofillSuggestions(form, field); - // Autocomplete suggestions are queried, but not Autofill. - EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen()); + CheckSuggestions( + kDefaultPageID, + Suggestion(l10n_util::GetStringUTF8(IDS_AUTOFILL_WARNING_MIXED_FORM), "", + "", -26)); } // Test that we return credit card suggestions for secure pages that have an @@ -2095,6 +2116,71 @@ TEST_F(AutofillManagerTest, FillTriggeredSection) { false); } +MATCHER(HasEmptyValue, "") { + return arg.value.empty(); +} + +// Test that if the form cache is outdated because a field has changed, filling +// is aborted after that field. +TEST_F(AutofillManagerTest, DoNotFillIfFormFieldChanged) { + FormData form; + test::CreateTestAddressFormData(&form); + FormsSeen({form}); + + FormStructure* form_structure = nullptr; + AutofillField* autofill_field = nullptr; + ASSERT_TRUE(autofill_manager_->GetCachedFormAndField( + form, form.fields.front(), &form_structure, &autofill_field)); + + // Modify |form| so that it doesn't match |form_structure| anymore. + ASSERT_GE(form.fields.size(), 3u); + for (auto it = form.fields.begin() + 2; it != form.fields.end(); ++it) + *it = FormFieldData(); + + const char guid[] = "00000000-0000-0000-0000-000000000001"; + AutofillProfile* profile = personal_data_.GetProfileWithGUID(guid); + ASSERT_TRUE(profile); + + int response_query_id = 0; + FormData response_data; + EXPECT_CALL(*autofill_driver_, SendFormDataToRenderer(_, _, _)) + .WillOnce((DoAll(testing::SaveArg<0>(&response_query_id), + testing::SaveArg<2>(&response_data)))); + autofill_manager_->FillOrPreviewDataModelForm( + AutofillDriver::FORM_DATA_ACTION_FILL, kDefaultPageID, form, + form.fields.front(), profile, nullptr, form_structure, autofill_field); + std::vector<FormFieldData> filled_fields(response_data.fields.begin(), + response_data.fields.begin() + 2); + std::vector<FormFieldData> skipped_fields(response_data.fields.begin() + 2, + response_data.fields.end()); + + EXPECT_THAT(filled_fields, Each(Not(HasEmptyValue()))); + EXPECT_THAT(skipped_fields, Each(HasEmptyValue())); +} + +// Test that if the form cache is outdated because a field was removed, filling +// is aborted. +TEST_F(AutofillManagerTest, DoNotFillIfFormFieldRemoved) { + FormData form; + test::CreateTestAddressFormData(&form); + FormsSeen({form}); + + FormStructure* form_structure = nullptr; + AutofillField* autofill_field = nullptr; + ASSERT_TRUE(autofill_manager_->GetCachedFormAndField( + form, form.fields.front(), &form_structure, &autofill_field)); + + // Modify |form| so that it doesn't match |form_structure| anymore. + ASSERT_GE(form.fields.size(), 2u); + form.fields.pop_back(); + + const char guid[] = "00000000-0000-0000-0000-000000000001"; + AutofillProfile* profile = personal_data_.GetProfileWithGUID(guid); + ASSERT_TRUE(profile); + + EXPECT_CALL(*autofill_driver_, SendFormDataToRenderer(_, _, _)).Times(0); +} + // Tests that AutofillManager ignores loss of focus events sent from the // renderer if the renderer did not have a previously-interacted form. // TODO(crbug.com/1140473): Remove this test when workaround is no longer @@ -2151,6 +2237,86 @@ TEST_F(AutofillManagerTest, 1); } +// Test that we properly match typed values to stored state data. +TEST_F(AutofillManagerTest, DetermineStateFieldTypeForUpload) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillUseAlternativeStateNameMap); + + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting(); + + AutofillProfile profile; + test::SetProfileInfo(&profile, "", "", "", "", "", "", "", "", "Bavaria", "", + "DE", ""); + + const char* const kValidMatches[] = {"by", "Bavaria", "Bayern", + "BY", "B.Y", "B-Y"}; + for (const char* valid_match : kValidMatches) { + SCOPED_TRACE(valid_match); + FormData form; + FormFieldData field; + + test::CreateTestFormField("Name", "Name", "Test", "text", &field); + form.fields.push_back(field); + + test::CreateTestFormField("State", "state", valid_match, "text", &field); + form.fields.push_back(field); + + FormStructure form_structure(form); + EXPECT_EQ(form_structure.field_count(), 2U); + + autofill_manager_->PreProcessStateMatchingTypesForTest({profile}, + &form_structure); + EXPECT_TRUE(form_structure.field(1)->state_is_a_matching_type()); + } + + const char* const kInvalidMatches[] = {"Garbage", "BYA", "BYA is a state", + "Bava", "Empty", ""}; + for (const char* invalid_match : kInvalidMatches) { + SCOPED_TRACE(invalid_match); + FormData form; + FormFieldData field; + + test::CreateTestFormField("Name", "Name", "Test", "text", &field); + form.fields.push_back(field); + + test::CreateTestFormField("State", "state", invalid_match, "text", &field); + form.fields.push_back(field); + + FormStructure form_structure(form); + EXPECT_EQ(form_structure.field_count(), 2U); + + autofill_manager_->PreProcessStateMatchingTypesForTest({profile}, + &form_structure); + EXPECT_FALSE(form_structure.field(1)->state_is_a_matching_type()); + } + + test::PopulateAlternativeStateNameMapForTesting( + "US", "California", + {{.canonical_name = "California", + .abbreviations = {"CA"}, + .alternative_names = {}}}); + + test::SetProfileInfo(&profile, "", "", "", "", "", "", "", "", "California", + "", "US", ""); + + FormData form; + FormFieldData field; + + test::CreateTestFormField("Name", "Name", "Test", "text", &field); + form.fields.push_back(field); + + test::CreateTestFormField("State", "state", "CA", "text", &field); + form.fields.push_back(field); + + FormStructure form_structure(form); + EXPECT_EQ(form_structure.field_count(), 2U); + + autofill_manager_->PreProcessStateMatchingTypesForTest({profile}, + &form_structure); + EXPECT_TRUE(form_structure.field(1)->state_is_a_matching_type()); +} + // Test that we return normal Autofill suggestions when trying to autofill // already filled forms. TEST_P(SuggestionMatchingTest, GetFieldSuggestionsWhenFormIsAutofilled) { @@ -2332,8 +2498,8 @@ TEST_P(AutofillManagerStructuredProfileTest, // Set up our form data. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); struct { const char* const label; @@ -2384,8 +2550,8 @@ TEST_P(AutofillManagerStructuredProfileTest, // Set up our form data. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); struct { const char* const label; @@ -2900,6 +3066,11 @@ TEST_P(AutofillManagerStructuredProfileTest, FillCreditCardForm_SplitName) { // Test that only filled selection boxes are counted for the type filling limit. TEST_P(AutofillManagerStructuredProfileTest, OnlyCountFilledSelectionBoxesForTypeFillingLimit) { + test::PopulateAlternativeStateNameMapForTesting( + "US", "Tennessee", + {{.canonical_name = "Tennessee", + .abbreviations = {"TN"}, + .alternative_names = {}}}); // Set up our form data. FormData form; form.name = ASCIIToUTF16("MyForm"); @@ -2938,6 +3109,14 @@ TEST_P(AutofillManagerStructuredProfileTest, form.fields.push_back(field); } + // Create a selection box for the state that hat the correct entry to be + // filled with user data. Note, TN is the official abbreviation for Tennessee. + for (int i = 0; i < 20; ++i) { + test::CreateTestSelectField("Country", "country", "", {"DE", "FR", "US"}, + {"DE", "FR", "US"}, 3, &field); + form.fields.push_back(field); + } + std::vector<FormData> forms(1, form); FormsSeen(forms); @@ -2974,17 +3153,18 @@ TEST_P(AutofillManagerStructuredProfileTest, response_data.fields[4 + i]); } - // Verify that the next 8 selection boxes are correctly filled again. - for (int i = 0; i < 8; i++) { + // Verify that the remaining selection boxes are correctly filled again + // because there's no limit on filling ADDRESS_HOME_STATE fields. + for (int i = 0; i < 20; i++) { ExpectFilledField("State", "state", "TN", "select-one", response_data.fields[24 + i]); } - // Verify that the last 12 boxes are not filled because the filling limit for - // the state type is already reached. - for (int i = 0; i < 12; i++) { - ExpectFilledField("State", "state", "", "select-one", - response_data.fields[32 + i]); + // Verify that only the first 9 of the remaining selection boxes are + // correctly filled due to the limit on filling ADDRESS_HOME_COUNTRY fields. + for (int i = 0; i < 20; i++) { + ExpectFilledField("Country", "country", i < 9 ? "US" : "", "select-one", + response_data.fields[44 + i]); } } @@ -3067,68 +3247,6 @@ TEST_P(AutofillManagerStructuredProfileTest, } // Test that non credit card related fields with the autocomplete attribute set -// to off are not filled on desktop when the feature to autofill all addresses -// is disabled. -TEST_P(AutofillManagerStructuredProfileTest, - FillAddressForm_AutocompleteOffRespected) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature(features::kAutofillAlwaysFillAddresses); - - FormData address_form; - address_form.name = ASCIIToUTF16("MyForm"); - address_form.url = GURL("https://myform.com/form.html"); - address_form.action = GURL("https://myform.com/submit.html"); - FormFieldData field; - test::CreateTestFormField("First name", "firstname", "", "text", &field); - address_form.fields.push_back(field); - test::CreateTestFormField("Middle name", "middle", "", "text", &field); - field.should_autocomplete = false; - address_form.fields.push_back(field); - test::CreateTestFormField("Last name", "lastname", "", "text", &field); - field.should_autocomplete = true; - address_form.fields.push_back(field); - test::CreateTestFormField("Address Line 1", "addr1", "", "text", &field); - field.should_autocomplete = false; - address_form.fields.push_back(field); - std::vector<FormData> address_forms(1, address_form); - FormsSeen(address_forms); - - // Fill the address form. - const char guid[] = "00000000-0000-0000-0000-000000000001"; - int response_page_id = 0; - FormData response_data; - FillAutofillFormDataAndSaveResults( - kDefaultPageID, address_form, address_form.fields[0], - MakeFrontendID(std::string(), guid), &response_page_id, &response_data); - - // The fist name should be filled. - ExpectFilledField("First name", "firstname", "Elvis", "text", - response_data.fields[0]); - - // The middle name should not be filled on desktop. - if (IsDesktopPlatform()) { - ExpectFilledField("Middle name", "middle", "", "text", - response_data.fields[1]); - } else { - ExpectFilledField("Middle name", "middle", "Aaron", "text", - response_data.fields[1]); - } - - // The last name should be filled. - ExpectFilledField("Last name", "lastname", "Presley", "text", - response_data.fields[2]); - - // The address line 1 should not be filled on desktop. - if (IsDesktopPlatform()) { - ExpectFilledField("Address Line 1", "addr1", "", "text", - response_data.fields[3]); - } else { - ExpectFilledField("Address Line 1", "addr1", "3734 Elvis Presley Blvd.", - "text", response_data.fields[3]); - } -} - -// Test that non credit card related fields with the autocomplete attribute set // to off are filled on all platforms when the feature to autofill all addresses // is enabled (default). TEST_P(AutofillManagerStructuredProfileTest, @@ -3808,14 +3926,6 @@ TEST_P(AutofillManagerStructuredProfileTest, FillPartlyAutofilledForm) { // Test that we correctly fill a previously partly auto-filled form. TEST_P(AutofillManagerStructuredProfileTest, FillPartlyManuallyFilledForm) { - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures( - // Enabled - {features::kAutofillSkipFillingFieldsWithChangedValues}, - // Disabled - // We want to query the legacy server rather than the API server. - {}); - // Set up our form data. FormData form; test::CreateTestAddressFormData(&form); @@ -3872,9 +3982,9 @@ TEST_P(AutofillManagerStructuredProfileTest, FillPhoneNumber) { FormData form_with_us_number_max_length; form_with_us_number_max_length.name = ASCIIToUTF16("MyMaxlengthPhoneForm"); form_with_us_number_max_length.url = - GURL("http://myform.com/phone_form.html"); + GURL("https://myform.com/phone_form.html"); form_with_us_number_max_length.action = - GURL("http://myform.com/phone_submit.html"); + GURL("https://myform.com/phone_submit.html"); FormData form_with_autocompletetype = form_with_us_number_max_length; form_with_autocompletetype.name = ASCIIToUTF16("MyAutocompletetypePhoneForm"); @@ -4002,7 +4112,7 @@ TEST_P(AutofillManagerStructuredProfileTest, // componentized number fields. FormData form_with_multiple_componentized_phone_fields; form_with_multiple_componentized_phone_fields.url = - GURL("http://www.foo.com/"); + GURL("https://www.foo.com/"); FormFieldData field; // Default is zero, have to set to a number autofill can process. @@ -4069,7 +4179,7 @@ TEST_P(AutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_multiple_whole_number_fields; - form_with_multiple_whole_number_fields.url = GURL("http://www.foo.com/"); + form_with_multiple_whole_number_fields.url = GURL("https://www.foo.com/"); FormFieldData field; // Default is zero, have to set to a number autofill can process. @@ -4125,7 +4235,7 @@ TEST_P(AutofillManagerStructuredProfileTest, // componentized number fields. FormData form_with_multiple_componentized_phone_fields; form_with_multiple_componentized_phone_fields.url = - GURL("http://www.foo.com/"); + GURL("https://www.foo.com/"); FormFieldData field; // Default is zero, have to set to a number autofill can process. @@ -4197,7 +4307,7 @@ TEST_P(AutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_misclassified_extension; - form_with_misclassified_extension.url = GURL("http://www.foo.com/"); + form_with_misclassified_extension.url = GURL("https://www.foo.com/"); FormFieldData field; // Default is zero, have to set to a number autofill can process. @@ -4259,7 +4369,7 @@ TEST_P(AutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_no_complete_number; - form_with_no_complete_number.url = GURL("http://www.foo.com/"); + form_with_no_complete_number.url = GURL("https://www.foo.com/"); FormFieldData field; // Default is zero, have to set to a number autofill can process. @@ -4319,7 +4429,7 @@ TEST_P(AutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_multiple_whole_number_fields; - form_with_multiple_whole_number_fields.url = GURL("http://www.foo.com/"); + form_with_multiple_whole_number_fields.url = GURL("https://www.foo.com/"); FormFieldData field; // Default is zero, have to set to a number autofill can process. @@ -4375,7 +4485,7 @@ TEST_P(AutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_multiple_whole_number_fields; - form_with_multiple_whole_number_fields.url = GURL("http://www.foo.com/"); + form_with_multiple_whole_number_fields.url = GURL("https://www.foo.com/"); FormFieldData field; // Default is zero, have to set to a number autofill can process. @@ -4426,8 +4536,8 @@ TEST_P(AutofillManagerStructuredProfileTest, FormWithHiddenOrPresentationalSelects) { FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); FormFieldData field; @@ -4502,7 +4612,7 @@ TEST_P(AutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_multiple_sections; - form_with_multiple_sections.url = GURL("http://www.foo.com/"); + form_with_multiple_sections.url = GURL("https://www.foo.com/"); FormFieldData field; // Default is zero, have to set to a number autofill can process. @@ -4647,7 +4757,7 @@ TEST_P(AutofillManagerStructuredProfileTest, FormChangesAddField) { TEST_P(AutofillManagerStructuredProfileTest, FormChangesVisibilityOfFields) { // Set up our form data. FormData form; - form.url = GURL("http://www.foo.com/"); + form.url = GURL("https://www.foo.com/"); FormFieldData field; @@ -4799,7 +4909,7 @@ TEST_P(AutofillManagerStructuredProfileTest, ValuePatternsMetric) { &field); field.is_focusable = true; // The metric skips hidden fields. form.name = ASCIIToUTF16("my-form"); - form.url = GURL("http://myform.com/form.html"); + form.url = GURL("https://myform.com/form.html"); form.action = GURL("https://myform.com/submit.html"); form.fields.push_back(field); std::vector<FormData> forms(1, form); @@ -4822,10 +4932,11 @@ TEST_P(AutofillManagerStructuredProfileTest, autocomplete_history_manager_.get())); autofill_manager_->SetAutofillProfileEnabled(false); autofill_manager_->SetAutofillCreditCardEnabled(false); - external_delegate_ = std::make_unique<TestAutofillExternalDelegate>( + auto external_delegate = std::make_unique<TestAutofillExternalDelegate>( autofill_manager_.get(), autofill_driver_.get(), /*call_parent_methods=*/false); - autofill_manager_->SetExternalDelegate(external_delegate_.get()); + external_delegate_ = external_delegate.get(); + autofill_manager_->SetExternalDelegateForTest(std::move(external_delegate)); // Set up our form data. FormData form; @@ -4851,10 +4962,11 @@ TEST_P(AutofillManagerStructuredProfileTest, autocomplete_history_manager_.get())); autofill_manager_->SetAutofillProfileEnabled(false); autofill_manager_->SetAutofillCreditCardEnabled(false); - external_delegate_ = std::make_unique<TestAutofillExternalDelegate>( + auto external_delegate = std::make_unique<TestAutofillExternalDelegate>( autofill_manager_.get(), autofill_driver_.get(), /*call_parent_methods=*/false); - autofill_manager_->SetExternalDelegate(external_delegate_.get()); + external_delegate_ = external_delegate.get(); + autofill_manager_->SetExternalDelegateForTest(std::move(external_delegate)); // Set up our form data. FormData form; @@ -4921,15 +5033,23 @@ TEST_P(AutofillManagerStructuredProfileTest, TEST_P(AutofillManagerStructuredProfileTest, AutocompleteSuggestions_CreditCardNameFieldShouldAutocomplete) { TestAutofillClient client; + // Since we are testing a form that submits over HTTP, we also need to set + // the main frame to HTTP in the client, otherwise mixed form warnings will + // trigger and autofill will be disabled. + GURL::Replacements replacements; + replacements.SetScheme(url::kHttpScheme, + url::Component(0, strlen(url::kHttpScheme))); + client.set_form_origin(client.form_origin().ReplaceComponents(replacements)); autofill_manager_.reset( new TestAutofillManager(autofill_driver_.get(), &client, &personal_data_, autocomplete_history_manager_.get())); autofill_manager_->SetAutofillProfileEnabled(false); autofill_manager_->SetAutofillCreditCardEnabled(false); - external_delegate_ = std::make_unique<TestAutofillExternalDelegate>( + auto external_delegate = std::make_unique<TestAutofillExternalDelegate>( autofill_manager_.get(), autofill_driver_.get(), /*call_parent_methods=*/false); - autofill_manager_->SetExternalDelegate(external_delegate_.get()); + external_delegate_ = external_delegate.get(); + autofill_manager_->SetExternalDelegateForTest(std::move(external_delegate)); // Set up our form data. FormData form; @@ -4952,15 +5072,23 @@ TEST_P(AutofillManagerStructuredProfileTest, TEST_P(AutofillManagerStructuredProfileTest, AutocompleteSuggestions_CreditCardNumberShouldNotAutocomplete) { TestAutofillClient client; + // Since we are testing a form that submits over HTTP, we also need to set + // the main frame to HTTP in the client, otherwise mixed form warnings will + // trigger and autofill will be disabled. + GURL::Replacements replacements; + replacements.SetScheme(url::kHttpScheme, + url::Component(0, strlen(url::kHttpScheme))); + client.set_form_origin(client.form_origin().ReplaceComponents(replacements)); autofill_manager_.reset( new TestAutofillManager(autofill_driver_.get(), &client, &personal_data_, autocomplete_history_manager_.get())); autofill_manager_->SetAutofillProfileEnabled(false); autofill_manager_->SetAutofillCreditCardEnabled(false); - external_delegate_ = std::make_unique<TestAutofillExternalDelegate>( + auto external_delegate = std::make_unique<TestAutofillExternalDelegate>( autofill_manager_.get(), autofill_driver_.get(), /*call_parent_methods=*/false); - autofill_manager_->SetExternalDelegate(external_delegate_.get()); + external_delegate_ = external_delegate.get(); + autofill_manager_->SetExternalDelegateForTest(std::move(external_delegate)); // Set up our form data. FormData form; @@ -5011,10 +5139,11 @@ TEST_P(AutofillManagerStructuredProfileTest, autocomplete_history_manager_.get())); autofill_manager_->SetAutofillProfileEnabled(false); autofill_manager_->SetAutofillCreditCardEnabled(false); - external_delegate_ = std::make_unique<TestAutofillExternalDelegate>( + auto external_delegate = std::make_unique<TestAutofillExternalDelegate>( autofill_manager_.get(), autofill_driver_.get(), /*call_parent_methods=*/false); - autofill_manager_->SetExternalDelegate(external_delegate_.get()); + external_delegate_ = external_delegate.get(); + autofill_manager_->SetExternalDelegateForTest(std::move(external_delegate)); EXPECT_CALL(*(autocomplete_history_manager_.get()), OnGetAutocompleteSuggestions) @@ -5046,18 +5175,6 @@ TEST_P(AutofillManagerStructuredProfileTest, autofill_manager_.reset(); } -namespace { -void AddFieldSuggestionToForm( - ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion, - autofill::FormFieldData field_data, - ServerFieldType field_type) { - auto* field_suggestion = form_suggestion->add_field_suggestions(); - field_suggestion->set_field_signature( - CalculateFieldSignatureForField(field_data).value()); - field_suggestion->set_primary_type_prediction(field_type); -} -} // namespace - // Test that OnLoadedServerPredictions can obtain the FormStructure with the // signature of the queried form from the API and apply type predictions. // What we test here: @@ -5068,8 +5185,8 @@ TEST_P(AutofillManagerStructuredProfileTest, OnLoadedServerPredictionsFromApi) { FormData form; form.unique_renderer_id.value() = 1; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); FormFieldData field; test::CreateTestFormField(/*label=*/"City", /*name=*/"city", /*value=*/"", /*type=*/"text", /*field=*/&field); @@ -5085,15 +5202,15 @@ TEST_P(AutofillManagerStructuredProfileTest, OnLoadedServerPredictionsFromApi) { auto form_structure_instance = std::make_unique<TestFormStructure>(form); // This pointer is valid as long as autofill manager lives. TestFormStructure* form_structure = form_structure_instance.get(); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); autofill_manager_->AddSeenFormStructure(std::move(form_structure_instance)); // Second form on the page. FormData form2; form2.unique_renderer_id.value() = 2; form2.name = ASCIIToUTF16("MyForm2"); - form2.url = GURL("http://myform.com/form.html"); - form2.action = GURL("http://myform.com/submit.html"); + form2.url = GURL("https://myform.com/form.html"); + form2.action = GURL("https://myform.com/submit.html"); test::CreateTestFormField("Last Name", "lastname", "", "text", &field); form2.fields.push_back(field); test::CreateTestFormField("Middle Name", "middlename", "", "text", &field); @@ -5103,7 +5220,7 @@ TEST_P(AutofillManagerStructuredProfileTest, OnLoadedServerPredictionsFromApi) { auto form_structure_instance2 = std::make_unique<TestFormStructure>(form2); // This pointer is valid as long as autofill manager lives. TestFormStructure* form_structure2 = form_structure_instance2.get(); - form_structure2->DetermineHeuristicTypes(); + form_structure2->DetermineHeuristicTypes(nullptr, nullptr); autofill_manager_->AddSeenFormStructure(std::move(form_structure_instance2)); // Make API response with suggestions. @@ -5111,14 +5228,20 @@ TEST_P(AutofillManagerStructuredProfileTest, OnLoadedServerPredictionsFromApi) { AutofillQueryResponse::FormSuggestion* form_suggestion; // Set suggestions for form 1. form_suggestion = response.add_form_suggestions(); - AddFieldSuggestionToForm(form_suggestion, form.fields[0], ADDRESS_HOME_CITY); - AddFieldSuggestionToForm(form_suggestion, form.fields[1], ADDRESS_HOME_STATE); - AddFieldSuggestionToForm(form_suggestion, form.fields[2], ADDRESS_HOME_ZIP); + autofill::test::AddFieldSuggestionToForm(form.fields[0], ADDRESS_HOME_CITY, + form_suggestion); + autofill::test::AddFieldSuggestionToForm(form.fields[1], ADDRESS_HOME_STATE, + form_suggestion); + autofill::test::AddFieldSuggestionToForm(form.fields[2], ADDRESS_HOME_ZIP, + form_suggestion); // Set suggestions for form 2. form_suggestion = response.add_form_suggestions(); - AddFieldSuggestionToForm(form_suggestion, form2.fields[0], NAME_LAST); - AddFieldSuggestionToForm(form_suggestion, form2.fields[1], NAME_MIDDLE); - AddFieldSuggestionToForm(form_suggestion, form2.fields[2], ADDRESS_HOME_ZIP); + autofill::test::AddFieldSuggestionToForm(form2.fields[0], NAME_LAST, + form_suggestion); + autofill::test::AddFieldSuggestionToForm(form2.fields[1], NAME_MIDDLE, + form_suggestion); + autofill::test::AddFieldSuggestionToForm(form2.fields[2], ADDRESS_HOME_ZIP, + form_suggestion); std::string response_string; ASSERT_TRUE(response.SerializeToString(&response_string)); @@ -5168,7 +5291,7 @@ TEST_P(AutofillManagerStructuredProfileTest, // Simulate having seen this form on page load. // |form_structure| will be owned by |autofill_manager_|. TestFormStructure* form_structure = new TestFormStructure(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); std::vector<FormSignature> signatures = test::GetEncodedSignatures(*form_structure); autofill_manager_->AddSeenFormStructure( @@ -5228,21 +5351,22 @@ TEST_P(AutofillManagerStructuredProfileTest, // Simulate having seen this form on page load. // |form_structure| will be owned by |autofill_manager_|. TestFormStructure* form_structure = new TestFormStructure(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); autofill_manager_->AddSeenFormStructure( std::unique_ptr<TestFormStructure>(form_structure)); AutofillQueryResponse response; auto* form_suggestion = response.add_form_suggestions(); - AddFieldSuggestionToForm(form_suggestion, form.fields[0], - CREDIT_CARD_NAME_FIRST); - AddFieldSuggestionToForm(form_suggestion, form.fields[1], - CREDIT_CARD_NAME_LAST); - AddFieldSuggestionToForm(form_suggestion, form.fields[2], CREDIT_CARD_NUMBER); - AddFieldSuggestionToForm(form_suggestion, form.fields[3], - CREDIT_CARD_EXP_MONTH); - AddFieldSuggestionToForm(form_suggestion, form.fields[4], - CREDIT_CARD_EXP_4_DIGIT_YEAR); + autofill::test::AddFieldSuggestionToForm( + form.fields[0], CREDIT_CARD_NAME_FIRST, form_suggestion); + autofill::test::AddFieldSuggestionToForm( + form.fields[1], CREDIT_CARD_NAME_LAST, form_suggestion); + autofill::test::AddFieldSuggestionToForm(form.fields[2], CREDIT_CARD_NUMBER, + form_suggestion); + autofill::test::AddFieldSuggestionToForm( + form.fields[3], CREDIT_CARD_EXP_MONTH, form_suggestion); + autofill::test::AddFieldSuggestionToForm( + form.fields[4], CREDIT_CARD_EXP_4_DIGIT_YEAR, form_suggestion); std::string response_string; ASSERT_TRUE(response.SerializeToString(&response_string)); @@ -5301,7 +5425,7 @@ TEST_P(AutofillManagerStructuredProfileTest, FormSubmittedServerTypes) { // Simulate having seen this form on page load. // |form_structure| will be owned by |autofill_manager_|. TestFormStructure* form_structure = new TestFormStructure(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); // Clear the heuristic types, and instead set the appropriate server types. std::vector<ServerFieldType> heuristic_types, server_types; @@ -5445,9 +5569,8 @@ TEST_P(AutofillManagerStructuredProfileTest, FormSubmittedWithDefaultValues) { struct ProfileMatchingTypesTestCase { const char* input_value; // The value to input in the field. - std::set<ServerFieldType> - field_types; // The expected field types to be determined. - std::set<ServerFieldType> + ServerFieldTypeSet field_types; // The expected field types to be determined. + ServerFieldTypeSet structured_field_types; // The expected field types to be determined. }; @@ -5464,24 +5587,27 @@ class ProfileMatchingTypesTest InitializeFeatures(); } - bool StructuredNames() const { return structured_names_enabled_; } + bool StructuredNamesAndAddresses() const { + return structured_names_and_addresses_; + } void InitializeFeatures(); private: - bool structured_names_enabled_; + bool structured_names_and_addresses_; base::test::ScopedFeatureList scoped_features_; }; void ProfileMatchingTypesTest::InitializeFeatures() { - structured_names_enabled_ = std::get<2>(GetParam()); + structured_names_and_addresses_ = std::get<2>(GetParam()); - if (structured_names_enabled_) { - scoped_features_.InitAndEnableFeature( - features::kAutofillEnableSupportForMoreStructureInNames); + std::vector<base::Feature> features = { + features::kAutofillEnableSupportForMoreStructureInAddresses, + features::kAutofillEnableSupportForMoreStructureInNames}; + if (structured_names_and_addresses_) { + scoped_features_.InitWithFeatures(features, {}); } else { - scoped_features_.InitAndDisableFeature( - features::kAutofillEnableSupportForMoreStructureInNames); + scoped_features_.InitWithFeatures({}, features); } } @@ -5495,7 +5621,11 @@ const ProfileMatchingTypesTestCase kProfileMatchingTypesTestCases[] = { {"theking@gmail.com", {EMAIL_ADDRESS}, {EMAIL_ADDRESS}}, {"RCA", {COMPANY_NAME}, {COMPANY_NAME}}, {"3734 Elvis Presley Blvd.", {ADDRESS_HOME_LINE1}, {ADDRESS_HOME_LINE1}}, - {"Apt. 10", {ADDRESS_HOME_LINE2}, {ADDRESS_HOME_LINE2}}, + {"3734", {UNKNOWN_TYPE}, {ADDRESS_HOME_HOUSE_NUMBER}}, + {"Elvis Presley Blvd.", {UNKNOWN_TYPE}, {ADDRESS_HOME_STREET_NAME}}, + {"Apt. 10", + {ADDRESS_HOME_LINE2}, + {ADDRESS_HOME_LINE2, ADDRESS_HOME_SUBPREMISE}}, {"Memphis", {ADDRESS_HOME_CITY}, {ADDRESS_HOME_CITY}}, {"Tennessee", {ADDRESS_HOME_STATE}, {ADDRESS_HOME_STATE}}, {"38116", {ADDRESS_HOME_ZIP}, {ADDRESS_HOME_ZIP}}, @@ -5607,13 +5737,14 @@ TEST_P(ProfileMatchingTypesTest, DeterminePossibleFieldTypesForUpload) { "structured_names=%s ", test_case.input_value, AutofillType(*test_case.field_types.begin()).ToString().c_str(), - validity_state, validation_source, StructuredNames() ? "true" : "false")); + validity_state, validation_source, + StructuredNamesAndAddresses() ? "true" : "false")); // Take the field types depending on the state of the structured names // feature. - const std::set<ServerFieldType>& expected_possible_types = - StructuredNames() ? test_case.structured_field_types - : test_case.field_types; + const ServerFieldTypeSet& expected_possible_types = + StructuredNamesAndAddresses() ? test_case.structured_field_types + : test_case.field_types; ASSERT_LE(AutofillDataModel::UNVALIDATED, validity_state); ASSERT_LE(validity_state, AutofillDataModel::UNSUPPORTED); @@ -5640,7 +5771,7 @@ TEST_P(ProfileMatchingTypesTest, DeterminePossibleFieldTypesForUpload) { // Set the validity state for the matching field type. for (auto type : expected_possible_types) { - if (GroupTypeOfServerFieldType(type) != CREDIT_CARD) { + if (GroupTypeOfServerFieldType(type) != FieldTypeGroup::kCreditCard) { for (auto& profile : profiles) { ASSERT_GT(test_case.field_types.size(), 0U); if (type == UNKNOWN_TYPE) { @@ -5666,8 +5797,8 @@ TEST_P(ProfileMatchingTypesTest, DeterminePossibleFieldTypesForUpload) { FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); FormFieldData field; test::CreateTestFormField("", "1", "", "text", &field); @@ -5687,7 +5818,7 @@ TEST_P(ProfileMatchingTypesTest, DeterminePossibleFieldTypesForUpload) { for (auto type : expected_possible_types) { // We don't add validity states for credit card fields. - if (GroupTypeOfServerFieldType(type) != CREDIT_CARD) { + if (GroupTypeOfServerFieldType(type) != FieldTypeGroup::kCreditCard) { ServerFieldTypeValidityStatesMap possible_types_validities = form_structure.field(0)->possible_types_validities(); ASSERT_EQ(expected_possible_types.size(), @@ -5708,8 +5839,8 @@ TEST_P(AutofillManagerStructuredProfileTest, DeterminePossibleFieldTypesForUpload_IsTriggered) { FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); std::vector<ServerFieldTypeSet> expected_types; std::vector<base::string16> expected_values; @@ -5811,8 +5942,8 @@ TEST_P(AutofillManagerStructuredProfileTest, for (const std::vector<TestFieldData>& test_fields : test_cases) { FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); // Create the form fields specified in the test case. FormFieldData field; @@ -6000,8 +6131,8 @@ TEST_P(AutofillManagerStructuredProfileTest, DisambiguateUploadTypes) { for (const std::vector<TestFieldData>& test_fields : test_cases) { FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); // Create the form fields specified in the test case. FormFieldData field; @@ -6028,12 +6159,13 @@ TEST_P(AutofillManagerStructuredProfileTest, DisambiguateUploadTypes) { // For structured names it is possible that a field as two out of three // possible classifications: NAME_FULL, NAME_LAST, // NAME_LAST_FIRST/SECOND. Note, all cases contain NAME_LAST. - if (StructuredNames() && possible_types.size() == 2) { + if (StructuredNamesAndAddresses() && possible_types.size() == 2) { EXPECT_TRUE(possible_types.count(NAME_LAST) && (possible_types.count(NAME_LAST_SECOND) || possible_types.count(NAME_LAST_FIRST) || possible_types.count(NAME_FULL))); - } else if (StructuredNames() && possible_types.size() == 3) { + } else if (StructuredNamesAndAddresses() && + possible_types.size() == 3) { // Or even all three. EXPECT_TRUE(possible_types.count(NAME_FULL) && possible_types.count(NAME_LAST) && @@ -6429,8 +6561,8 @@ TEST_P(AutofillManagerStructuredProfileTest, // Set up our form data (it's already filled out with user data). FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); std::vector<ServerFieldTypeSet> expected_types; ServerFieldTypeSet types; @@ -6485,8 +6617,8 @@ TEST_P(AutofillManagerStructuredProfileTest, // Set up our form data (it's already filled out with user data). FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); std::vector<ServerFieldTypeSet> expected_types; ServerFieldTypeSet types; @@ -6500,7 +6632,7 @@ TEST_P(AutofillManagerStructuredProfileTest, test::CreateTestFormField("Last Name", "lastname", "", "text", &field); form.fields.push_back(field); types.clear(); - if (StructuredNames()) + if (StructuredNamesAndAddresses()) types.insert(NAME_LAST_SECOND); types.insert(NAME_LAST); expected_types.push_back(types); @@ -6538,8 +6670,8 @@ TEST_P(AutofillManagerStructuredProfileTest, // Set up our form data (empty). FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); std::vector<ServerFieldTypeSet> expected_types; @@ -6554,7 +6686,7 @@ TEST_P(AutofillManagerStructuredProfileTest, test::CreateTestFormField("Last Name", "lastname", "", "text", &field); form.fields.push_back(field); types.clear(); - if (StructuredNames()) + if (StructuredNamesAndAddresses()) types.insert(NAME_LAST_SECOND); types.insert(NAME_LAST); expected_types.push_back(types); @@ -6696,8 +6828,8 @@ TEST_P(AutofillManagerStructuredProfileTest, DontSaveCvcInAutocompleteHistory) { FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); struct { const char* label; @@ -7271,8 +7403,8 @@ TEST_P(AutofillManagerStructuredProfileTest, ShouldUploadForm) { // scenarios. FormData form; form.name = ASCIIToUTF16("TestForm"); - form.url = GURL("http://example.com/form.html"); - form.action = GURL("http://example.com/submit.html"); + form.url = GURL("https://example.com/form.html"); + form.action = GURL("https://example.com/submit.html"); // Empty Form. EXPECT_FALSE(autofill_manager_->ShouldUploadForm(FormStructure(form))); @@ -7362,47 +7494,6 @@ TEST_P(AutofillManagerStructuredProfileTest, } } -// Verify that no suggestions are shown on desktop for non credit card related -// fields if the initiating field has the "autocomplete" attribute set to off -// and the feature to autofill all addresses is also off. -TEST_P(AutofillManagerStructuredProfileTest, - DisplaySuggestions_AutocompleteOffRespected_AddressField) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature(features::kAutofillAlwaysFillAddresses); - - // Set up an address form. - FormData mixed_form; - mixed_form.name = ASCIIToUTF16("MyForm"); - mixed_form.url = GURL("https://myform.com/form.html"); - mixed_form.action = GURL("https://myform.com/submit.html"); - FormFieldData field; - test::CreateTestFormField("First name", "firstname", "", "text", &field); - field.should_autocomplete = false; - mixed_form.fields.push_back(field); - test::CreateTestFormField("Last name", "lastname", "", "text", &field); - field.should_autocomplete = true; - mixed_form.fields.push_back(field); - test::CreateTestFormField("Address", "address", "", "text", &field); - field.should_autocomplete = true; - mixed_form.fields.push_back(field); - std::vector<FormData> mixed_forms(1, mixed_form); - FormsSeen(mixed_forms); - - // Suggestions should not be displayed on desktop for this field. - GetAutofillSuggestions(mixed_form, mixed_form.fields[0]); - if (IsDesktopPlatform()) { - EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen()); - } else { - EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen()); - } - - // Suggestions should always be displayed for all the other fields. - for (size_t i = 1U; i < mixed_form.fields.size(); ++i) { - GetAutofillSuggestions(mixed_form, mixed_form.fields[i]); - EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen()); - } -} - // Verify that suggestions are shown on desktop for credit card related fields // even if the initiating field field has the "autocomplete" attribute set to // off. @@ -7442,8 +7533,8 @@ TEST_P(AutofillManagerStructuredProfileTest, // Create a form with unknown heuristic fields. FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); FormFieldData field; test::CreateTestFormField("Field 1", "field1", "", "text", &field); @@ -7454,7 +7545,7 @@ TEST_P(AutofillManagerStructuredProfileTest, form.fields.push_back(field); auto form_structure = std::make_unique<TestFormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); // Make sure the form can not be autofilled now. ASSERT_EQ(0u, form_structure->autofill_count()); for (size_t idx = 0; idx < form_structure->field_count(); ++idx) { @@ -7502,8 +7593,8 @@ TEST_P(AutofillManagerStructuredProfileTest, FormWithLongOptionValuesIsAcceptable) { FormData form; form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); FormFieldData field; test::CreateTestFormField("First name", "firstname", "", "text", &field); @@ -7583,7 +7674,8 @@ TEST_P(AutofillManagerStructuredProfileTest, version_info::Channel::UNKNOWN}) { SCOPED_TRACE(::testing::Message() << "Channel " << static_cast<int>(channel)); - EXPECT_CALL(autofill_client_, GetChannel()).WillOnce(Return(channel)); + // One more call is from TestAutofillManager constructor. + EXPECT_CALL(autofill_client_, GetChannel()).WillRepeatedly(Return(channel)); TestAutofillManager test_instance(autofill_driver_.get(), &autofill_client_, &personal_data_, autocomplete_history_manager_.get()); @@ -7618,7 +7710,8 @@ TEST_P(AutofillManagerStructuredProfileTest, SCOPED_TRACE(::testing::Message() << "Channel " << static_cast<int>(channel)); EXPECT_FALSE(AutofillManager::IsRichQueryEnabled(channel)); - EXPECT_CALL(autofill_client_, GetChannel()).WillOnce(Return(channel)); + // One more call is from TestAutofillManager constructor. + EXPECT_CALL(autofill_client_, GetChannel()).WillRepeatedly(Return(channel)); TestAutofillManager test_instance(autofill_driver_.get(), &autofill_client_, &personal_data_, autocomplete_history_manager_.get()); @@ -7687,8 +7780,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -7732,8 +7825,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -7777,8 +7870,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -7821,8 +7914,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -7865,8 +7958,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -7909,8 +8002,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -7953,8 +8046,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -8006,8 +8099,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -8057,8 +8150,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -8110,8 +8203,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -8161,8 +8254,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -8215,8 +8308,8 @@ TEST_P(AutofillManagerStructuredProfileTest, form.button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); form.submission_event = SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; @@ -8392,8 +8485,9 @@ TEST_F(AutofillManagerTest, DontImportUpiIdWhenIncognito) { // Tests the vote generation for the address enhancement types. TEST_F(AutofillManagerTest, PossibleFieldTypesForEnhancementVotes) { base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - features::kAutofillAddressEnhancementVotes); + scoped_feature_list.InitWithFeatures( + {features::kAutofillAddressEnhancementVotes}, + {features::kAutofillEnableSupportForMoreStructureInAddresses}); std::vector<AutofillProfile> profiles = {AutofillProfile()}; profiles[0].SetRawInfo(ADDRESS_HOME_STREET_NAME, @@ -8459,13 +8553,47 @@ TEST_F(AutofillManagerTest, PageLanguageGetsCorrectlySet) { FormData form; test::CreateTestAddressFormData(&form); - // Set up language state mock. - autofill_client_.GetLanguageState()->SetOriginalLanguage("test_lang"); + autofill_manager_->OnFormsSeen({form}); + FormStructure* parsed_form = + autofill_manager_->FindCachedFormByRendererId(form.unique_renderer_id); + + ASSERT_TRUE(parsed_form); + ASSERT_EQ(LanguageCode(), parsed_form->current_page_language()); - FormStructure* parsed_form = autofill_manager_->ParseFormForTest(form); + autofill_client_.GetLanguageState()->SetCurrentLanguage("zh"); + + autofill_manager_->OnFormsSeen({form}); + parsed_form = + autofill_manager_->FindCachedFormByRendererId(form.unique_renderer_id); + + ASSERT_EQ(LanguageCode("zh"), parsed_form->current_page_language()); +} + +TEST_F(AutofillManagerTest, PageLanguageGetsCorrectlyDetected) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillParsingPatternsLanguageDetection); + + FormData form; + test::CreateTestAddressFormData(&form); + + autofill_manager_->OnFormsSeen({form}); + FormStructure* parsed_form = + autofill_manager_->FindCachedFormByRendererId(form.unique_renderer_id); ASSERT_TRUE(parsed_form); - ASSERT_EQ("test_lang", parsed_form->page_language()); + ASSERT_EQ(LanguageCode(), parsed_form->current_page_language()); + + translate::LanguageDetectionDetails language_detection_details; + language_detection_details.adopted_language = "zh"; + autofill_manager_->OnLanguageDetermined(language_detection_details); + + autofill_client_.GetLanguageState()->SetCurrentLanguage("zh"); + + parsed_form = + autofill_manager_->FindCachedFormByRendererId(form.unique_renderer_id); + + ASSERT_EQ(LanguageCode("zh"), parsed_form->current_page_language()); } // AutofillManagerTest with kAutofillDisabledMixedForms feature enabled. @@ -8886,7 +9014,7 @@ class OnFocusOnFormFieldTest : public AutofillManagerTest, } void CheckSuggestionsAvailableIfScreenReaderRunning() { -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) // The only existing functions for determining whether ChromeVox is in use // are in the src/chrome directory, which cannot be included in components. // Thus, if the platform is ChromeOS, we assume that ChromeVox is in use at @@ -8896,7 +9024,7 @@ class OnFocusOnFormFieldTest : public AutofillManagerTest, #else EXPECT_EQ(has_active_screen_reader_, external_delegate_->has_suggestions_available_on_field_focus()); -#endif // defined(OS_CHROMEOS) +#endif // BUILDFLAG(IS_CHROMEOS_ASH) } void CheckNoSuggestionsAvailableOnFieldFocus() { @@ -8936,10 +9064,6 @@ TEST_P(OnFocusOnFormFieldTest, AddressSuggestions) { } TEST_P(OnFocusOnFormFieldTest, AddressSuggestions_AutocompleteOffNotRespected) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - features::kAutofillAlwaysFillAddresses); - FormData form; form.name = ASCIIToUTF16("MyForm"); form.url = GURL("https://myform.com/form.html"); @@ -8960,34 +9084,6 @@ TEST_P(OnFocusOnFormFieldTest, AddressSuggestions_AutocompleteOffNotRespected) { CheckSuggestionsAvailableIfScreenReaderRunning(); } -TEST_P(OnFocusOnFormFieldTest, AddressSuggestions_AutocompleteOffRespected) { - if (!IsDesktopPlatform()) - return; - - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndDisableFeature( - features::kAutofillAlwaysFillAddresses); - - FormData form; - form.name = ASCIIToUTF16("MyForm"); - form.url = GURL("https://myform.com/form.html"); - form.action = GURL("https://myform.com/submit.html"); - FormFieldData field; - // Set a valid autocomplete attribute for the first name. - test::CreateTestFormField("First name", "firstname", "", "text", &field); - field.autocomplete_attribute = "given-name"; - form.fields.push_back(field); - // Set an autocomplete=off attribute for the last name. - test::CreateTestFormField("Last Name", "lastname", "", "text", &field); - field.should_autocomplete = false; - form.fields.push_back(field); - std::vector<FormData> forms(1, form); - FormsSeen(forms); - - autofill_manager_->OnFocusOnFormFieldImpl(form, form.fields[1], gfx::RectF()); - CheckNoSuggestionsAvailableOnFieldFocus(); -} - TEST_P(OnFocusOnFormFieldTest, CreditCardSuggestions_SecureContext) { // Set up our form data. FormData form; diff --git a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc index 53593fd85bd..1602cab0588 100644 --- a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc @@ -76,10 +76,9 @@ const std::vector<base::FilePath> GetTestFiles() { base::FilePath dir = GetTestDataDir(); bool structured_names = base::FeatureList::IsEnabled( features::kAutofillEnableSupportForMoreStructureInNames); - dir = - dir.AppendASCII("autofill") - .AppendASCII(structured_names ? "merge_structured_names" : "merge") - .AppendASCII("input"); + dir = dir.AppendASCII("autofill") + .AppendASCII(structured_names ? "merge_structured_names" : "merge") + .AppendASCII("input"); base::FileEnumerator input_files(dir, false, base::FileEnumerator::FILES, kFileNamePattern); std::vector<base::FilePath> files; @@ -104,7 +103,7 @@ std::string SerializeProfiles(const std::vector<AutofillProfile*>& profiles) { result += "\n"; for (const ServerFieldType& type : kProfileFieldTypes) { base::string16 value = profiles[i]->GetRawInfo(type); - result += AutofillType(type).ToString(); + result += AutofillType::ServerFieldTypeToString(type); result += kFieldSeparator; if (!value.empty()) { base::ReplaceFirstSubstringAfterOffset( @@ -208,12 +207,19 @@ class AutofillMergeTest : public DataDrivenTest, AutofillMergeTest::AutofillMergeTest() : DataDrivenTest(GetTestDataDir()) { CountryNames::SetLocaleString("en-US"); for (size_t i = NO_SERVER_DATA; i < MAX_VALID_FIELD_TYPE; ++i) { + // Some ServerFieldTypes are deprecated and removed from the enum + // definition. + if ((i >= 15 && i <= 19) || (i >= 25 && i <= 29) || (i >= 44 && i <= 50) || + (i == 94)) { + continue; + } ServerFieldType field_type = static_cast<ServerFieldType>(i); - string_to_field_type_map_[AutofillType(field_type).ToString()] = field_type; + string_to_field_type_map_[AutofillType::ServerFieldTypeToString( + field_type)] = field_type; } } -AutofillMergeTest::~AutofillMergeTest() {} +AutofillMergeTest::~AutofillMergeTest() = default; void AutofillMergeTest::SetUp() { test::DisableSystemServices(nullptr); diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc index 1f74c2b7180..0aa01bd33cb 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics.cc @@ -26,7 +26,7 @@ #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/form_data.h" -#include "components/language_usage_metrics/language_usage_metrics.h" +#include "components/language/core/browser/language_usage_metrics.h" #include "services/metrics/public/cpp/metrics_utils.h" #include "services/metrics/public/cpp/ukm_builders.h" @@ -37,10 +37,11 @@ using mojom::SubmissionSource; namespace { // Exponential bucket spacing for UKM event data. -const double kAutofillEventDataBucketSpacing = 2.0; +constexpr double kAutofillEventDataBucketSpacing = 2.0; // Note: if adding an enum value here, update the corresponding description for -// AutofillTypeQualityByFieldType in histograms.xml. +// AutofillFieldPredictionQualityByFieldType in +// tools/metrics/histograms/enums.xml. enum FieldTypeGroupForMetrics { GROUP_AMBIGUOUS = 0, GROUP_NAME, @@ -69,6 +70,16 @@ enum FieldTypeGroupForMetrics { GROUP_STREET_ADDRESS, GROUP_CREDIT_CARD_VERIFICATION, GROUP_UNFILLABLE, + GROUP_ADDRESS_HOME_APT_NUM, + GROUP_ADDRESS_HOME_SORTING_CODE, + GROUP_ADDRESS_HOME_DEPENDENT_LOCALITY, + GROUP_ADDRESS_HOME_STREET_AND_DEPENDENT_STREET_NAME, + GROUP_ADDRESS_HOME_OTHER_SUBUNIT, + GROUP_ADDRESS_HOME_ADDRESS, + GROUP_ADDRESS_HOME_ADDRESS_WITH_NAME, + GROUP_ADDRESS_HOME_FLOOR, + GROUP_UNKNOWN_TYPE, + // Add new entries here and update enums.xml. NUM_FIELD_TYPE_GROUPS_FOR_METRICS }; @@ -99,9 +110,9 @@ std::string PreviousSaveCreditCardPromptUserDecisionToString( // Returns the interpolated index. // // The interpolation maps the pair (|group|, |metric|) to a single index, so -// that all the indicies for a given group are adjacent. In particular, with -// the groups {AMBIGUOUS, NAME, ...} combining with the metrics {UNKNOWN, MATCH, -// MISMATCH}, we create this set of mapped indices: +// that all the indices for a given group are adjacent. In particular, with +// the groups {AMBIGUOUS, NAME, ...} combining with the metrics +// {UNKNOWN, MATCH, MISMATCH}, we create this set of mapped indices: // { // AMBIGUOUS+UNKNOWN, // AMBIGUOUS+MATCH, @@ -125,21 +136,21 @@ int GetFieldTypeGroupPredictionQualityMetric( FieldTypeGroupForMetrics group = GROUP_AMBIGUOUS; switch (AutofillType(field_type).group()) { - case NO_GROUP: + case FieldTypeGroup::kNoGroup: group = GROUP_AMBIGUOUS; break; - case NAME: - case NAME_BILLING: + case FieldTypeGroup::kName: + case FieldTypeGroup::kNameBilling: group = GROUP_NAME; break; - case COMPANY: + case FieldTypeGroup::kCompany: group = GROUP_COMPANY; break; - case ADDRESS_HOME: - case ADDRESS_BILLING: + case FieldTypeGroup::kAddressHome: + case FieldTypeGroup::kAddressBilling: switch (AutofillType(field_type).GetStorableType()) { case ADDRESS_HOME_LINE1: group = GROUP_ADDRESS_LINE_1; @@ -150,6 +161,9 @@ int GetFieldTypeGroupPredictionQualityMetric( case ADDRESS_HOME_LINE3: group = GROUP_ADDRESS_LINE_3; break; + case ADDRESS_HOME_APT_NUM: + group = GROUP_ADDRESS_HOME_APT_NUM; + break; case ADDRESS_HOME_STREET_ADDRESS: group = GROUP_STREET_ADDRESS; break; @@ -168,6 +182,12 @@ int GetFieldTypeGroupPredictionQualityMetric( case ADDRESS_HOME_STREET_NAME: group = GROUP_ADDRESS_HOME_STREET_NAME; break; + case ADDRESS_HOME_SORTING_CODE: + group = GROUP_ADDRESS_HOME_SORTING_CODE; + break; + case ADDRESS_HOME_DEPENDENT_LOCALITY: + group = GROUP_ADDRESS_HOME_DEPENDENT_LOCALITY; + break; case ADDRESS_HOME_DEPENDENT_STREET_NAME: group = GROUP_ADDRESS_HOME_DEPENDENT_STREET_NAME; break; @@ -177,26 +197,122 @@ int GetFieldTypeGroupPredictionQualityMetric( case ADDRESS_HOME_PREMISE_NAME: group = GROUP_ADDRESS_HOME_PREMISE_NAME; break; + case ADDRESS_HOME_STREET_AND_DEPENDENT_STREET_NAME: + group = GROUP_ADDRESS_HOME_STREET_AND_DEPENDENT_STREET_NAME; + break; case ADDRESS_HOME_SUBPREMISE: group = GROUP_ADDRESS_HOME_SUBPREMISE; break; - default: - NOTREACHED() << field_type << " has no group assigned (ambiguous)"; + case ADDRESS_HOME_OTHER_SUBUNIT: + group = GROUP_ADDRESS_HOME_OTHER_SUBUNIT; + break; + case ADDRESS_HOME_ADDRESS: + group = GROUP_ADDRESS_HOME_ADDRESS; + break; + case ADDRESS_HOME_ADDRESS_WITH_NAME: + group = GROUP_ADDRESS_HOME_ADDRESS_WITH_NAME; + break; + case ADDRESS_HOME_FLOOR: + group = GROUP_ADDRESS_HOME_FLOOR; + break; + case UNKNOWN_TYPE: + group = GROUP_UNKNOWN_TYPE; + break; + case NO_SERVER_DATA: + case EMPTY_TYPE: + case NAME_FIRST: + case NAME_MIDDLE: + case NAME_LAST: + case NAME_MIDDLE_INITIAL: + case NAME_FULL: + case NAME_SUFFIX: + case EMAIL_ADDRESS: + case PHONE_HOME_NUMBER: + case PHONE_HOME_CITY_CODE: + case PHONE_HOME_COUNTRY_CODE: + case PHONE_HOME_CITY_AND_NUMBER: + case PHONE_HOME_WHOLE_NUMBER: + case PHONE_FAX_NUMBER: + case PHONE_FAX_CITY_CODE: + case PHONE_FAX_COUNTRY_CODE: + case PHONE_FAX_CITY_AND_NUMBER: + case PHONE_FAX_WHOLE_NUMBER: + case ADDRESS_BILLING_LINE1: + case ADDRESS_BILLING_LINE2: + case ADDRESS_BILLING_APT_NUM: + case ADDRESS_BILLING_CITY: + case ADDRESS_BILLING_STATE: + case ADDRESS_BILLING_ZIP: + case ADDRESS_BILLING_COUNTRY: + case CREDIT_CARD_NAME_FULL: + case CREDIT_CARD_NUMBER: + case CREDIT_CARD_EXP_MONTH: + case CREDIT_CARD_EXP_2_DIGIT_YEAR: + case CREDIT_CARD_EXP_4_DIGIT_YEAR: + case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: + case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: + case CREDIT_CARD_TYPE: + case CREDIT_CARD_VERIFICATION_CODE: + case COMPANY_NAME: + case FIELD_WITH_DEFAULT_VALUE: + case PHONE_BILLING_NUMBER: + case PHONE_BILLING_CITY_CODE: + case PHONE_BILLING_COUNTRY_CODE: + case PHONE_BILLING_CITY_AND_NUMBER: + case PHONE_BILLING_WHOLE_NUMBER: + case NAME_BILLING_FIRST: + case NAME_BILLING_MIDDLE: + case NAME_BILLING_LAST: + case NAME_BILLING_MIDDLE_INITIAL: + case NAME_BILLING_FULL: + case NAME_BILLING_SUFFIX: + case MERCHANT_EMAIL_SIGNUP: + case MERCHANT_PROMO_CODE: + case PASSWORD: + case ACCOUNT_CREATION_PASSWORD: + case ADDRESS_BILLING_STREET_ADDRESS: + case ADDRESS_BILLING_SORTING_CODE: + case ADDRESS_BILLING_DEPENDENT_LOCALITY: + case ADDRESS_BILLING_LINE3: + case NOT_ACCOUNT_CREATION_PASSWORD: + case USERNAME: + case USERNAME_AND_EMAIL_ADDRESS: + case NEW_PASSWORD: + case PROBABLY_NEW_PASSWORD: + case NOT_NEW_PASSWORD: + case CREDIT_CARD_NAME_FIRST: + case CREDIT_CARD_NAME_LAST: + case PHONE_HOME_EXTENSION: + case CONFIRMATION_PASSWORD: + case AMBIGUOUS_TYPE: + case SEARCH_TERM: + case PRICE: + case NOT_PASSWORD: + case SINGLE_USERNAME: + case NOT_USERNAME: + case UPI_VPA: + case NAME_LAST_FIRST: + case NAME_LAST_CONJUNCTION: + case NAME_LAST_SECOND: + case NAME_HONORIFIC_PREFIX: + case NAME_FULL_WITH_HONORIFIC_PREFIX: + case MAX_VALID_FIELD_TYPE: + NOTREACHED() << field_type << " type is not in that group."; group = GROUP_AMBIGUOUS; break; } break; - case EMAIL: + case FieldTypeGroup::kEmail: group = GROUP_EMAIL; break; - case PHONE_HOME: - case PHONE_BILLING: + case FieldTypeGroup::kPhoneHome: + case FieldTypeGroup::kPhoneBilling: group = GROUP_PHONE; break; - case CREDIT_CARD: + case FieldTypeGroup::kCreditCard: switch (field_type) { case CREDIT_CARD_NAME_FULL: case CREDIT_CARD_NAME_FIRST: @@ -226,19 +342,19 @@ int GetFieldTypeGroupPredictionQualityMetric( } break; - case PASSWORD_FIELD: + case FieldTypeGroup::kPasswordField: group = GROUP_PASSWORD; break; - case USERNAME_FIELD: + case FieldTypeGroup::kUsernameField: group = GROUP_USERNAME; break; - case UNFILLABLE: + case FieldTypeGroup::kUnfillable: group = GROUP_UNFILLABLE; break; - case TRANSACTION: + case FieldTypeGroup::kTransaction: NOTREACHED(); break; } @@ -346,7 +462,8 @@ ServerFieldType GetActualFieldType(const ServerFieldTypeSet& possible_types, if (collapsed_field_types.size() == 1) actual_type = *collapsed_field_types.begin(); - DVLOG(2) << "Inferred Type: " << AutofillType(actual_type).ToString(); + DVLOG(2) << "Inferred Type: " + << AutofillType::ServerFieldTypeToString(actual_type); return actual_type; } @@ -553,8 +670,9 @@ void LogPredictionQualityMetrics( ServerFieldType actual_type = GetActualFieldType(possible_types, predicted_type); - DVLOG(2) << "Predicted: " << AutofillType(predicted_type).ToString() << "; " - << "Actual: " << AutofillType(actual_type).ToString(); + DVLOG(2) << "Predicted: " + << AutofillType::ServerFieldTypeToString(predicted_type) << "; " + << "Actual: " << AutofillType::ServerFieldTypeToString(actual_type); DCHECK_LE(predicted_type, UINT16_MAX); DCHECK_LE(actual_type, UINT16_MAX); @@ -850,77 +968,6 @@ void AutofillMetrics::LogSaveCardPromptResultMetric( } // static -void AutofillMetrics::LogSaveCardPromptMetric( - SaveCardPromptMetric metric, - bool is_uploading, - bool is_reshow, - AutofillClient::SaveCreditCardOptions options, - int previous_save_credit_card_prompt_user_decision, - security_state::SecurityLevel security_level, - AutofillSyncSigninState sync_state) { - DCHECK_LT(metric, NUM_SAVE_CARD_PROMPT_METRICS); - std::string destination = is_uploading ? ".Upload" : ".Local"; - std::string show = is_reshow ? ".Reshows" : ".FirstShow"; - std::string metric_with_destination_and_show = - "Autofill.SaveCreditCardPrompt" + destination + show; - base::UmaHistogramEnumeration(metric_with_destination_and_show, metric, - NUM_SAVE_CARD_PROMPT_METRICS); - base::UmaHistogramEnumeration( - metric_with_destination_and_show + GetMetricsSyncStateSuffix(sync_state), - metric, NUM_SAVE_CARD_PROMPT_METRICS); - if (options.should_request_name_from_user) { - base::UmaHistogramEnumeration( - metric_with_destination_and_show + ".RequestingCardholderName", metric, - NUM_SAVE_CARD_PROMPT_METRICS); - } - if (options.should_request_expiration_date_from_user) { - base::UmaHistogramEnumeration( - metric_with_destination_and_show + ".RequestingExpirationDate", metric, - NUM_SAVE_CARD_PROMPT_METRICS); - } - if (options.has_non_focusable_field) { - base::UmaHistogramEnumeration( - metric_with_destination_and_show + ".FromNonFocusableForm", metric, - NUM_SAVE_CARD_PROMPT_METRICS); - } - if (options.from_dynamic_change_form) { - base::UmaHistogramEnumeration( - metric_with_destination_and_show + ".FromDynamicChangeForm", metric, - NUM_SAVE_CARD_PROMPT_METRICS); - } - base::UmaHistogramEnumeration( - metric_with_destination_and_show + - PreviousSaveCreditCardPromptUserDecisionToString( - previous_save_credit_card_prompt_user_decision), - metric, NUM_SAVE_CARD_PROMPT_METRICS); - - LogSaveCardPromptMetricBySecurityLevel(metric, is_uploading, security_level); -} - -// static -void AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( - SaveCardPromptMetric metric, - bool is_uploading, - security_state::SecurityLevel security_level) { - // Getting a SECURITY_LEVEL_COUNT security level means that it was not - // possible to get the real security level. Don't log. - if (security_level == security_state::SecurityLevel::SECURITY_LEVEL_COUNT) { - return; - } - - std::string histogram_name = "Autofill.SaveCreditCardPrompt."; - if (is_uploading) { - histogram_name += "Upload"; - } else { - histogram_name += "Local"; - } - - base::UmaHistogramEnumeration(security_state::GetSecurityLevelHistogramName( - histogram_name, security_level), - metric, NUM_SAVE_CARD_PROMPT_METRICS); -} - -// static void AutofillMetrics::LogCreditCardUploadLegalMessageLinkClicked() { base::RecordAction(base::UserMetricsAction( "Autofill_CreditCardUpload_LegalMessageLinkClicked")); @@ -981,19 +1028,6 @@ void AutofillMetrics::LogLocalCardMigrationBubbleOfferMetric( } // static -void AutofillMetrics::LogLocalCardMigrationBubbleUserInteractionMetric( - LocalCardMigrationBubbleUserInteractionMetric metric, - bool is_reshow) { - DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_BUBBLE_USER_INTERACTION_METRICS); - std::string histogram_name = - "Autofill.LocalCardMigrationBubbleUserInteraction."; - histogram_name += is_reshow ? "Reshows" : "FirstShow"; - base::UmaHistogramEnumeration( - histogram_name, metric, - NUM_LOCAL_CARD_MIGRATION_BUBBLE_USER_INTERACTION_METRICS); -} - -// static void AutofillMetrics::LogLocalCardMigrationBubbleResultMetric( LocalCardMigrationBubbleResultMetric metric, bool is_reshow) { @@ -1080,6 +1114,43 @@ void AutofillMetrics::LogLocalCardMigrationPromptMetric( } // static +void AutofillMetrics::LogOfferNotificationBubbleOfferMetric(bool is_reshow) { + base::UmaHistogramBoolean( + "Autofill.OfferNotificationBubbleOffer.CardLinkedOffer", is_reshow); +} + +// static +void AutofillMetrics::LogOfferNotificationBubbleResultMetric( + OfferNotificationBubbleResultMetric metric, + bool is_reshow) { + DCHECK_LE(metric, OfferNotificationBubbleResultMetric::kMaxValue); + static const char first_show[] = + "Autofill.OfferNotificationBubbleResult.CardLinkedOffer.FirstShow"; + static const char reshows[] = + "Autofill.OfferNotificationBubbleResult.CardLinkedOffer.Reshows"; + base::UmaHistogramEnumeration(is_reshow ? reshows : first_show, metric); +} + +// static +void AutofillMetrics::LogOfferNotificationInfoBarDeepLinkClicked() { + base::RecordAction(base::UserMetricsAction( + "Autofill_OfferNotificationInfoBar_DeepLinkClicked")); +} + +// static +void AutofillMetrics::LogOfferNotificationInfoBarResultMetric( + OfferNotificationInfoBarResultMetric metric) { + DCHECK_LE(metric, OfferNotificationInfoBarResultMetric::kMaxValue); + base::UmaHistogramEnumeration( + "Autofill.OfferNotificationInfoBarResult.CardLinkedOffer", metric); +} + +void AutofillMetrics::LogOfferNotificationInfoBarShown() { + base::UmaHistogramBoolean( + "Autofill.OfferNotificationInfoBarOffer.CardLinkedOffer", true); +} + +// static void AutofillMetrics::LogSaveCardWithFirstAndLastNameOffered(bool is_local) { std::string histogram_name = "Autofill.SaveCardWithFirstAndLastNameOffered."; histogram_name += is_local ? "Local" : "Server"; @@ -1452,14 +1523,14 @@ void AutofillMetrics::LogEditedAutofilledFieldAtSubmission( field.Type().GetStorableType(), editing_metric)); // Record the UMA statistics spliced by the autocomplete attribute value. - FormType form_type = - FormTypes::FieldTypeGroupToFormType(field.Type().group()); - if (form_type == ADDRESS_FORM || form_type == CREDIT_CARD_FORM) { + FormType form_type = FieldTypeGroupToFormType(field.Type().group()); + if (form_type == FormType::kAddressForm || + form_type == FormType::kCreditCardForm) { bool autocomplete_off = field.autocomplete_attribute == "off"; const std::string autocomplete_histogram = base::StrCat( {"Autofill.Autocomplete.", autocomplete_off ? "Off" : "NotOff", ".EditedAutofilledFieldAtSubmission.", - form_type == ADDRESS_FORM ? "Address" : "CreditCard"}); + form_type == FormType::kAddressForm ? "Address" : "CreditCard"}); base::UmaHistogramEnumeration(autocomplete_histogram, editing_metric); } @@ -1484,42 +1555,45 @@ void AutofillMetrics::LogUserHappinessMetric( FieldTypeGroup field_type_group, security_state::SecurityLevel security_level, uint32_t profile_form_bitmask) { - LogUserHappinessMetric( - metric, {FormTypes::FieldTypeGroupToFormType(field_type_group)}, - security_level, profile_form_bitmask); + LogUserHappinessMetric(metric, {FieldTypeGroupToFormType(field_type_group)}, + security_level, profile_form_bitmask); } // static void AutofillMetrics::LogUserHappinessMetric( UserHappinessMetric metric, - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, security_state::SecurityLevel security_level, uint32_t profile_form_bitmask) { DCHECK_LT(metric, NUM_USER_HAPPINESS_METRICS); UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness", metric, NUM_USER_HAPPINESS_METRICS); - if (base::Contains(form_types, CREDIT_CARD_FORM)) { + if (base::Contains(form_types, FormType::kCreditCardForm)) { UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness.CreditCard", metric, NUM_USER_HAPPINESS_METRICS); - LogUserHappinessBySecurityLevel(metric, CREDIT_CARD_FORM, security_level); + LogUserHappinessBySecurityLevel(metric, FormType::kCreditCardForm, + security_level); } - if (base::Contains(form_types, ADDRESS_FORM)) { + if (base::Contains(form_types, FormType::kAddressForm)) { UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness.Address", metric, NUM_USER_HAPPINESS_METRICS); if (metric != AutofillMetrics::FORMS_LOADED) { LogUserHappinessByProfileFormType(metric, profile_form_bitmask); } - LogUserHappinessBySecurityLevel(metric, ADDRESS_FORM, security_level); + LogUserHappinessBySecurityLevel(metric, FormType::kAddressForm, + security_level); } - if (base::Contains(form_types, PASSWORD_FORM)) { + if (base::Contains(form_types, FormType::kPasswordForm)) { UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness.Password", metric, NUM_USER_HAPPINESS_METRICS); - LogUserHappinessBySecurityLevel(metric, PASSWORD_FORM, security_level); + LogUserHappinessBySecurityLevel(metric, FormType::kPasswordForm, + security_level); } - if (base::Contains(form_types, UNKNOWN_FORM_TYPE)) { + if (base::Contains(form_types, FormType::kUnknownFormType)) { UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness.Unknown", metric, NUM_USER_HAPPINESS_METRICS); - LogUserHappinessBySecurityLevel(metric, UNKNOWN_FORM_TYPE, security_level); + LogUserHappinessBySecurityLevel(metric, FormType::kUnknownFormType, + security_level); } } @@ -1534,19 +1608,19 @@ void AutofillMetrics::LogUserHappinessBySecurityLevel( std::string histogram_name = "Autofill.UserHappiness."; switch (form_type) { - case CREDIT_CARD_FORM: + case FormType::kCreditCardForm: histogram_name += "CreditCard"; break; - case ADDRESS_FORM: + case FormType::kAddressForm: histogram_name += "Address"; break; - case PASSWORD_FORM: + case FormType::kPasswordForm: histogram_name += "Password"; break; - case UNKNOWN_FORM_TYPE: + case FormType::kUnknownFormType: histogram_name += "Unknown"; break; @@ -1592,7 +1666,7 @@ void AutofillMetrics::LogFormFillDurationFromLoadWithoutAutofill( // static void AutofillMetrics::LogFormFillDurationFromInteraction( - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, bool used_autofill, const base::TimeDelta& duration) { std::string parent_metric; @@ -1602,16 +1676,16 @@ void AutofillMetrics::LogFormFillDurationFromInteraction( parent_metric = "Autofill.FillDuration.FromInteraction.WithoutAutofill"; } LogFormFillDuration(parent_metric, duration); - if (base::Contains(form_types, CREDIT_CARD_FORM)) { + if (base::Contains(form_types, FormType::kCreditCardForm)) { LogFormFillDuration(parent_metric + ".CreditCard", duration); } - if (base::Contains(form_types, ADDRESS_FORM)) { + if (base::Contains(form_types, FormType::kAddressForm)) { LogFormFillDuration(parent_metric + ".Address", duration); } - if (base::Contains(form_types, PASSWORD_FORM)) { + if (base::Contains(form_types, FormType::kPasswordForm)) { LogFormFillDuration(parent_metric + ".Password", duration); } - if (base::Contains(form_types, UNKNOWN_FORM_TYPE)) { + if (base::Contains(form_types, FormType::kUnknownFormType)) { LogFormFillDuration(parent_metric + ".Unknown", duration); } } @@ -1926,7 +2000,7 @@ void AutofillMetrics::LogAutofillFormSubmittedState( AutofillFormSubmittedState state, bool is_for_credit_card, bool has_upi_vpa_field, - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, const base::TimeTicks& form_parsed_timestamp, FormSignature form_signature, AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) { @@ -2107,7 +2181,7 @@ void AutofillMetrics::LogDeveloperEngagementUkm( ukm::SourceId source_id, const GURL& url, bool is_for_credit_card, - std::set<FormType> form_types, + DenseSet<FormType> form_types, int developer_engagement_metrics, FormSignature form_signature) { DCHECK(developer_engagement_metrics); @@ -2313,7 +2387,7 @@ void AutofillMetrics::FormInteractionsUkmLogger:: } int64_t AutofillMetrics::FormTypesToBitVector( - const std::set<FormType>& form_types) { + const DenseSet<FormType>& form_types) { int64_t form_type_bv = 0; for (const FormType& form_type : form_types) { DCHECK_LT(static_cast<int64_t>(form_type), 63); @@ -2366,7 +2440,7 @@ const char* AutofillMetrics::GetMetricsSyncStateSuffix( void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted( bool is_for_credit_card, bool has_upi_vpa_field, - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, AutofillFormSubmittedState state, const base::TimeTicks& form_parsed_timestamp, FormSignature form_signature) { @@ -2392,7 +2466,7 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted( void AutofillMetrics::FormInteractionsUkmLogger::LogFormEvent( FormEvent form_event, - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, const base::TimeTicks& form_parsed_timestamp) { if (!CanLog()) return; @@ -2481,7 +2555,7 @@ void AutofillMetrics::LogFieldParsingTranslatedFormLanguageMetric( base::StringPiece locale) { base::UmaHistogramSparse( "Autofill.ParsedFieldTypesUsingTranslatedPageLanguage", - language_usage_metrics::LanguageUsageMetrics::ToLanguageCode(locale)); + language::LanguageUsageMetrics::ToLanguageCode(locale)); } // static @@ -2498,4 +2572,18 @@ void AutofillMetrics::LogWebOTPPhoneCollectionMetricStateUkm( builder.Record(recorder); } +// static +void AutofillMetrics::LogNumberOfAutofilledFieldsAtSubmission( + size_t number_of_accepted_fields, + size_t number_of_corrected_fields) { + base::UmaHistogramExactLinear( + "Autofill.NumberOfAutofilledFieldsAtSubmission.Total", + number_of_accepted_fields + number_of_corrected_fields, 50); + base::UmaHistogramExactLinear( + "Autofill.NumberOfAutofilledFieldsAtSubmission.Accepted", + number_of_accepted_fields, 50); + base::UmaHistogramExactLinear( + "Autofill.NumberOfAutofilledFieldsAtSubmission.Corrected", + number_of_corrected_fields, 50); +} } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h index b7213dc6318..b2c0772525a 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.h +++ b/chromium/components/autofill/core/browser/autofill_metrics.h @@ -22,6 +22,7 @@ #include "components/autofill/core/browser/metrics/form_events.h" #include "components/autofill/core/browser/sync_utils.h" #include "components/autofill/core/browser/ui/popup_types.h" +#include "components/autofill/core/common/dense_set.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" #include "components/autofill/core/common/signatures.h" @@ -261,67 +262,34 @@ class AutofillMetrics { NUM_SAVE_CARD_PROMPT_RESULT_METRICS, }; - // Metrics to measure user interaction with the save credit card prompt. - // - // SAVE_CARD_PROMPT_DISMISS_FOCUS is not stored explicitly, but can be - // inferred from the other metrics: - // SAVE_CARD_PROMPT_DISMISS_FOCUS = SHOW_REQUESTED - END_* - DISMISS_* - enum SaveCardPromptMetric { - // Prompt was requested to be shown due to: - // CC info being submitted (first show), or - // location bar icon being clicked while bubble is hidden (reshows). - SAVE_CARD_PROMPT_SHOW_REQUESTED, - // The prompt was shown successfully. - SAVE_CARD_PROMPT_SHOWN_DEPRECATED, - // The prompt was not shown because the legal message was invalid. - SAVE_CARD_PROMPT_END_INVALID_LEGAL_MESSAGE, - // The user explicitly accepted the prompt. - SAVE_CARD_PROMPT_END_ACCEPTED, - // The user explicitly denied the prompt. - SAVE_CARD_PROMPT_END_DENIED, - // The prompt and icon were removed because of navigation away from the - // page that caused the prompt to be shown. The navigation occurred while - // the prompt was showing. - SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, - // The prompt and icon were removed because of navigation away from the - // page that caused the prompt to be shown. The navigation occurred while - // the prompt was hidden. - SAVE_CARD_PROMPT_END_NAVIGATION_HIDDEN, - // The prompt was dismissed because the user clicked the "Learn more" link. - // Deprecated. - DEPRECATED_SAVE_CARD_PROMPT_DISMISS_CLICK_LEARN_MORE, - // The prompt was dismissed because the user clicked a legal message link. - SAVE_CARD_PROMPT_DISMISS_CLICK_LEGAL_MESSAGE, - - // The following _CVC_FIX_FLOW_ metrics are independent of the ones above, - // and were relevant when the CVC fix flow was active M62-M64. During that - // time, for instance, accepting the CVC fix flow would trigger both - // SAVE_CARD_PROMPT_CVC_FIX_FLOW_END_ACCEPTED as well as - // SAVE_CARD_PROMPT_END_ACCEPTED. They were split apart in order to track - // acceptance/abandonment rates of the multi-stage dialog user experience. - // (SAVE_CARD_PROMPT_CVC_FIX_FLOW_END_DENIED was an impossible state because - // the CVC fix flow uses a close button instead of a cancel button.) - - // The prompt moved to a second stage that requested CVC from the user. - SAVE_CARD_PROMPT_CVC_FIX_FLOW_SHOWN, - // The user explicitly entered CVC and accepted the prompt. - SAVE_CARD_PROMPT_CVC_FIX_FLOW_END_ACCEPTED, - // The prompt and icon were removed because of navigation away from the page - // that caused the prompt to be shown. The navigation occurred while the - // prompt was showing, at the CVC request stage. - SAVE_CARD_PROMPT_CVC_FIX_FLOW_END_NAVIGATION_SHOWING, - // The prompt and icon were removed because of navigation away from the page - // that caused the prompt to be shown. The navigation occurred while the - // prompt was hidden, at the CVC request stage. - SAVE_CARD_PROMPT_CVC_FIX_FLOW_END_NAVIGATION_HIDDEN, - // The prompt was dismissed because the user clicked a legal message link. - SAVE_CARD_PROMPT_CVC_FIX_FLOW_DISMISS_CLICK_LEGAL_MESSAGE, - - // The save card bubble was not shown due to the card having too many - // offer-to-save strikes, but the omnibox icon was still displayed. - SAVE_CARD_ICON_SHOWN_WITHOUT_PROMPT, - - NUM_SAVE_CARD_PROMPT_METRICS, + // Metrics to track event when the offer notification bubble is closed. + enum class OfferNotificationBubbleResultMetric { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // The user explicitly acknowledged the bubble by clicking the ok button. + OFFER_NOTIFICATION_BUBBLE_ACKNOWLEDGED = 0, + // The user explicitly closed the prompt with the close button or ESC. + OFFER_NOTIFICATION_BUBBLE_CLOSED = 1, + // The user did not interact with the prompt. + OFFER_NOTIFICATION_BUBBLE_NOT_INTERACTED = 2, + // The prompt lost focus and was deactivated. + OFFER_NOTIFICATION_BUBBLE_LOST_FOCUS = 3, + kMaxValue = OFFER_NOTIFICATION_BUBBLE_LOST_FOCUS, + }; + + // Metrics to track event when the offer notification infobar is closed. + enum class OfferNotificationInfoBarResultMetric { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // User acknowledged the infobar by clicking the ok button. + OFFER_NOTIFICATION_INFOBAR_ACKNOWLEDGED = 0, + // User explicitly closed the infobar with the close button. + OFFER_NOTIFICATION_INFOBAR_CLOSED = 1, + // InfoBar was shown but user did not interact with the it. + OFFER_NOTIFICATION_INFOBAR_IGNORED = 2, + kMaxValue = OFFER_NOTIFICATION_INFOBAR_IGNORED, }; enum CreditCardUploadFeedbackMetric { @@ -543,23 +511,6 @@ class AutofillMetrics { NUM_LOCAL_CARD_MIGRATION_BUBBLE_OFFER_METRICS, }; - // Metrics to track user interactions with the bubble. - // TODO(crbug.com/1070799): Remove this enum once the old logging is cleaned - // up. - enum LocalCardMigrationBubbleUserInteractionMetric { - // The user explicitly accepts the offer. - LOCAL_CARD_MIGRATION_BUBBLE_CLOSED_ACCEPTED = 0, - // The user explicitly denies the offer (clicks the cancel button). - LOCAL_CARD_MIGRATION_BUBBLE_CLOSED_DENIED = 1, - // The bubble is closed due to user navigating away from the page - // while the bubble was showing. - LOCAL_CARD_MIGRATION_BUBBLE_CLOSED_NAVIGATED_WHILE_SHOWING = 2, - // The bubble is closed due to user navigating away from the page - // while the bubble was hidden. - LOCAL_CARD_MIGRATION_BUBBLE_CLOSED_NAVIGATED_WHILE_HIDDEN = 3, - NUM_LOCAL_CARD_MIGRATION_BUBBLE_USER_INTERACTION_METRICS, - }; - // Metrics to track user action result of the bubble when the bubble is // closed. enum LocalCardMigrationBubbleResultMetric { @@ -1059,12 +1010,12 @@ class AutofillMetrics { ServerFieldType actual_type); void LogFormSubmitted(bool is_for_credit_card, bool has_upi_vpa_field, - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, AutofillFormSubmittedState state, const base::TimeTicks& form_parsed_timestamp, FormSignature form_signature); void LogFormEvent(FormEvent form_event, - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, const base::TimeTicks& form_parsed_timestamp); // Log whether the autofill decided to skip or to fill each @@ -1171,18 +1122,6 @@ class AutofillMetrics { int previous_save_credit_card_prompt_user_decision, security_state::SecurityLevel security_level, AutofillSyncSigninState sync_state); - static void LogSaveCardPromptMetric( - SaveCardPromptMetric metric, - bool is_uploading, - bool is_reshow, - AutofillClient::SaveCreditCardOptions options, - int previous_save_credit_card_prompt_user_decision, - security_state::SecurityLevel security_level, - AutofillSyncSigninState sync_state); - static void LogSaveCardPromptMetricBySecurityLevel( - SaveCardPromptMetric metric, - bool is_uploading, - security_state::SecurityLevel security_level); static void LogCreditCardUploadLegalMessageLinkClicked(); static void LogCreditCardUploadFeedbackMetric( CreditCardUploadFeedbackMetric metric); @@ -1194,11 +1133,6 @@ class AutofillMetrics { static void LogLocalCardMigrationBubbleOfferMetric( LocalCardMigrationBubbleOfferMetric metric, bool is_reshow); - // TODO(crbug.com/1070799): Delete the user interaction metrics when the - // experiment is fully launched. - static void LogLocalCardMigrationBubbleUserInteractionMetric( - LocalCardMigrationBubbleUserInteractionMetric metric, - bool is_reshow); static void LogLocalCardMigrationBubbleResultMetric( LocalCardMigrationBubbleResultMetric metric, bool is_reshow); @@ -1213,6 +1147,14 @@ class AutofillMetrics { static void LogLocalCardMigrationPromptMetric( LocalCardMigrationOrigin local_card_migration_origin, LocalCardMigrationPromptMetric metric); + static void LogOfferNotificationBubbleOfferMetric(bool is_reshow); + static void LogOfferNotificationBubbleResultMetric( + OfferNotificationBubbleResultMetric metric, + bool is_reshow); + static void LogOfferNotificationInfoBarDeepLinkClicked(); + static void LogOfferNotificationInfoBarResultMetric( + OfferNotificationInfoBarResultMetric metric); + static void LogOfferNotificationInfoBarShown(); // Should be called when credit card scan is finished. |duration| should be // the time elapsed between launching the credit card scanner and getting back @@ -1252,7 +1194,7 @@ class AutofillMetrics { static void LogUserHappinessMetric( UserHappinessMetric metric, - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, security_state::SecurityLevel security_level, uint32_t profile_form_bitmask); @@ -1374,7 +1316,7 @@ class AutofillMetrics { // time elapsed between the initial form interaction and submission. This // metric is sliced by |form_type| and |used_autofill|. static void LogFormFillDurationFromInteraction( - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, bool used_autofill, const base::TimeDelta& duration); @@ -1507,7 +1449,7 @@ class AutofillMetrics { AutofillFormSubmittedState state, bool is_for_credit_card, bool has_upi_vpa_field, - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, const base::TimeTicks& form_parsed_timestamp, FormSignature form_signature, FormInteractionsUkmLogger* form_interactions_ukm_logger); @@ -1570,7 +1512,7 @@ class AutofillMetrics { ukm::SourceId source_id, const GURL& url, bool is_for_credit_card, - std::set<FormType> form_types, + DenseSet<FormType> form_types, int developer_engagement_metrics, FormSignature form_signature); @@ -1579,7 +1521,7 @@ class AutofillMetrics { static void LogHiddenOrPresentationalSelectFieldsFilled(); // Converts form type to bit vector to store in UKM. - static int64_t FormTypesToBitVector(const std::set<FormType>& form_types); + static int64_t FormTypesToBitVector(const DenseSet<FormType>& form_types); // Records the fact that the server card link was clicked with information // about the current sync state. @@ -1632,6 +1574,11 @@ class AutofillMetrics { ukm::SourceId source_id, uint32_t phone_collection_metric_state); + // Logs the number of autofilled fields at submission time. + static void LogNumberOfAutofilledFieldsAtSubmission( + size_t number_of_accepted_fields, + size_t number_of_corrected_fields); + private: static void Log(AutocompleteEvent event); diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc index b9da1edcafb..7a43731c706 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc @@ -32,7 +32,6 @@ #include "components/autofill/core/browser/metrics/credit_card_form_event_logger.h" #include "components/autofill/core/browser/metrics/form_events.h" #include "components/autofill/core/browser/mock_autocomplete_history_manager.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" #include "components/autofill/core/browser/payments/credit_card_access_manager.h" #include "components/autofill/core/browser/payments/test_credit_card_save_manager.h" #include "components/autofill/core/browser/payments/test_payments_client.h" @@ -52,12 +51,14 @@ #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_tick_clock.h" +#include "components/autofill/core/common/dense_set.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/renderer_id.h" #include "components/autofill/core/common/signatures.h" #include "components/prefs/pref_service.h" #include "components/sync/driver/test_sync_service.h" +#include "components/translate/core/common/language_detection_details.h" #include "components/ukm/test_ukm_recorder.h" #include "components/webdata/common/web_data_results.h" #include "services/metrics/public/cpp/ukm_builders.h" @@ -67,6 +68,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect.h" #include "url/gurl.h" +#include "url/url_canon.h" #if !defined(OS_IOS) #include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h" @@ -136,7 +138,7 @@ void VerifyDeveloperEngagementUkm( const ukm::TestUkmRecorder* ukm_recorder, const FormData& form, const bool is_for_credit_card, - const std::set<FormType>& form_types, + const DenseSet<FormType>& form_types, const std::vector<int64_t>& expected_metric_values) { int expected_metric_value = 0; for (const auto it : expected_metric_values) @@ -196,7 +198,7 @@ void VerifySubmitFormUkm(const ukm::TestUkmRecorder* ukm_recorder, AutofillMetrics::AutofillFormSubmittedState state, bool is_for_credit_card, bool has_upi_vpa_field, - const std::set<FormType>& form_types) { + const DenseSet<FormType>& form_types) { VerifyUkm(ukm_recorder, form, UkmFormSubmittedType::kEntryName, {{{UkmFormSubmittedType::kAutofillFormSubmittedStateName, state}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, @@ -381,9 +383,8 @@ class AutofillMetricsTest : public testing::Test { std::unique_ptr<TestAutofillManager> autofill_manager_; std::unique_ptr<TestPersonalDataManager> personal_data_; std::unique_ptr<MockAutocompleteHistoryManager> autocomplete_history_manager_; - std::unique_ptr<AutofillExternalDelegate> external_delegate_; + AutofillExternalDelegate* external_delegate_; base::test::ScopedFeatureList scoped_feature_list_; - TestPatternProvider test_pattern_provider_; private: void CreateTestAutofillProfiles(); @@ -432,9 +433,10 @@ void AutofillMetricsTest::SetUp() { autofill_manager_ = std::make_unique<TestAutofillManager>( autofill_driver_.get(), &autofill_client_, personal_data_.get(), autocomplete_history_manager_.get()); - external_delegate_ = std::make_unique<AutofillExternalDelegate>( + auto external_delegate = std::make_unique<AutofillExternalDelegate>( autofill_manager_.get(), autofill_driver_.get()); - autofill_manager_->SetExternalDelegate(external_delegate_.get()); + external_delegate_ = external_delegate.get(); + autofill_manager_->SetExternalDelegateForTest(std::move(external_delegate)); #if !defined(OS_IOS) autofill_manager_->credit_card_access_manager() @@ -626,6 +628,80 @@ INSTANTIATE_TEST_SUITE_P(AutofillMetricsTest, AutofillMetricsIFrameTest, testing::Bool()); +// Test that we log the right number of autofilled fields at submission time. +TEST_F(AutofillMetricsTest, NumberOfAutofilledFieldsAtSubmission) { + // Set up our form data with two autofilled fields. + FormData form = + test::GetFormData({.description_for_logging = "NumberOfAutofilledFields", + .fields = {{.label = "Autofilled", + .name = "autofilled", + .value = "Elvis Aaron Presley", + .is_autofilled = true}, + {.label = "Autofilled but corrected", + .name = "autofillfailed", + .value = "buddy@gmail.com", + .is_autofilled = true}, + {.label = "Empty", + .name = "empty", + .value = "", + .is_autofilled = false}, + {.label = "Unknown", + .name = "unknown", + .value = "garbage", + .is_autofilled = false}, + {.label = "Select", + .name = "select", + .value = "USA", + .form_control_type = "select-one", + .is_autofilled = false}, + {.role = ServerFieldType::PHONE_HOME_NUMBER, + .value = "2345678901", + .form_control_type = "tel", + .is_autofilled = true}}, + .unique_renderer_id = MakeFormRendererId(), + .main_frame_origin = url::Origin::Create( + autofill_client_.form_origin())}); + + std::vector<ServerFieldType> heuristic_types = { + NAME_FULL, PHONE_HOME_NUMBER, NAME_FULL, + PHONE_HOME_NUMBER, UNKNOWN_TYPE, PHONE_HOME_CITY_AND_NUMBER}; + std::vector<ServerFieldType> server_types = { + NAME_FIRST, EMAIL_ADDRESS, NAME_FIRST, + EMAIL_ADDRESS, NO_SERVER_DATA, PHONE_HOME_CITY_AND_NUMBER}; + + // Simulate having seen this form on page load. + autofill_manager_->AddSeenForm(form, heuristic_types, server_types); + + // Simulate user changing the second field of the form. + autofill_manager_->OnTextFieldDidChange(form, form.fields[1], gfx::RectF(), + TimeTicks()); + form.fields.at(1).is_autofilled = false; + + // Simulate form submission. + base::HistogramTester histogram_tester; + autofill_manager_->OnFormSubmitted(form, false, + SubmissionSource::FORM_SUBMISSION); + + // Test that the correct bucket for the number of filled fields received a + // count while the others remain at zero counts. + const size_t expected_number_of_accepted_fillings = 2; + const size_t expected_number_of_corrected_fillings = 1; + const size_t expected_number_of_total_fillings = + expected_number_of_accepted_fillings + + expected_number_of_corrected_fillings; + for (int i = 0; i < 50; i++) { + histogram_tester.ExpectBucketCount( + "Autofill.NumberOfAutofilledFieldsAtSubmission.Total", i, + i == expected_number_of_total_fillings ? 1 : 0); + histogram_tester.ExpectBucketCount( + "Autofill.NumberOfAutofilledFieldsAtSubmission.Accepted", i, + i == expected_number_of_accepted_fillings ? 1 : 0); + histogram_tester.ExpectBucketCount( + "Autofill.NumberOfAutofilledFieldsAtSubmission.Corrected", i, + i == expected_number_of_corrected_fillings ? 1 : 0); + } +} + // Test that we log quality metrics appropriately. TEST_F(AutofillMetricsTest, QualityMetrics) { // Set up our form data. @@ -1698,7 +1774,7 @@ TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) { {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldSignatureName, field_signature[0].value()}, {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldTypeGroupName, - ADDRESS_HOME}, + static_cast<int64_t>(FieldTypeGroup::kAddressHome)}, {UkmLogHiddenRepresentationalFieldSkipDecisionType:: kFieldOverallTypeName, ADDRESS_HOME_LINE1}, @@ -1717,7 +1793,7 @@ TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) { {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldSignatureName, field_signature[1].value()}, {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldTypeGroupName, - ADDRESS_HOME}, + static_cast<int64_t>(FieldTypeGroup::kAddressHome)}, {UkmLogHiddenRepresentationalFieldSkipDecisionType:: kFieldOverallTypeName, ADDRESS_HOME_CITY}, @@ -1736,7 +1812,7 @@ TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) { {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldSignatureName, field_signature[2].value()}, {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldTypeGroupName, - ADDRESS_HOME}, + static_cast<int64_t>(FieldTypeGroup::kAddressHome)}, {UkmLogHiddenRepresentationalFieldSkipDecisionType:: kFieldOverallTypeName, ADDRESS_HOME_STATE}, @@ -1755,7 +1831,7 @@ TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) { {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldSignatureName, field_signature[3].value()}, {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldTypeGroupName, - ADDRESS_HOME}, + static_cast<int64_t>(FieldTypeGroup::kAddressHome)}, {UkmLogHiddenRepresentationalFieldSkipDecisionType:: kFieldOverallTypeName, ADDRESS_HOME_COUNTRY}, @@ -1837,7 +1913,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) { std::string response_string = SerializeAndEncode(response); FormStructure::ParseApiQueryResponse( response_string, forms, test::GetEncodedSignatures(forms), - autofill_manager_->form_interactions_ukm_logger_for_test()); + autofill_manager_->form_interactions_ukm_logger()); ASSERT_EQ(test_ukm_recorder_ ->GetEntriesByName( @@ -1853,7 +1929,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) { {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, field_signature[0].value()}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, - ADDRESS_HOME}, + static_cast<int64_t>(FieldTypeGroup::kAddressHome)}, {UkmLogRepeatedServerTypePredictionRationalized:: kFieldOldOverallTypeName, ADDRESS_HOME_STREET_ADDRESS}, @@ -1873,7 +1949,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) { {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, field_signature[1].value()}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, - ADDRESS_HOME}, + static_cast<int64_t>(FieldTypeGroup::kAddressHome)}, {UkmLogRepeatedServerTypePredictionRationalized:: kFieldOldOverallTypeName, ADDRESS_HOME_STREET_ADDRESS}, @@ -1955,7 +2031,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { std::string response_string = SerializeAndEncode(response); FormStructure::ParseApiQueryResponse( response_string, forms, test::GetEncodedSignatures(forms), - autofill_manager_->form_interactions_ukm_logger_for_test()); + autofill_manager_->form_interactions_ukm_logger()); ASSERT_EQ(test_ukm_recorder_ ->GetEntriesByName( @@ -1971,7 +2047,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, field_signature[0].value()}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, - ADDRESS_HOME}, + static_cast<int64_t>(FieldTypeGroup::kAddressHome)}, {UkmLogRepeatedServerTypePredictionRationalized:: kFieldOldOverallTypeName, ADDRESS_HOME_COUNTRY}, @@ -1991,7 +2067,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, field_signature[1].value()}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, - ADDRESS_HOME}, + static_cast<int64_t>(FieldTypeGroup::kAddressHome)}, {UkmLogRepeatedServerTypePredictionRationalized:: kFieldOldOverallTypeName, ADDRESS_HOME_COUNTRY}, @@ -2011,7 +2087,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, field_signature[2].value()}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, - ADDRESS_HOME}, + static_cast<int64_t>(FieldTypeGroup::kAddressHome)}, {UkmLogRepeatedServerTypePredictionRationalized:: kFieldOldOverallTypeName, ADDRESS_HOME_COUNTRY}, @@ -2403,8 +2479,9 @@ TEST_P(QualityMetricsTest, Classification) { ServerFieldType predicted_type = GetParam().predicted_field_type; DVLOG(2) << "Test Case = Predicted: " - << AutofillType(predicted_type).ToString() << "; " - << "Actual: " << AutofillType(actual_field_type).ToString(); + << AutofillType::ServerFieldTypeToString(predicted_type) << "; " + << "Actual: " + << AutofillType::ServerFieldTypeToString(actual_field_type); // Set up our form data. FormData form; @@ -2609,7 +2686,7 @@ TEST_F(AutofillMetricsTest, TimingMetrics) { // Simulate a OnFormsSeen() call that should trigger the recording. std::vector<FormData> forms; forms.push_back(form); - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); // Because these metrics are related to timing, it is not possible to know in // advance which bucket the sample will fall into, so we just need to make @@ -2847,7 +2924,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) { std::unique_ptr<TestFormStructure> form_structure = std::make_unique<TestFormStructure>(form); TestFormStructure* form_structure_ptr = form_structure.get(); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); ASSERT_TRUE(autofill_manager_->mutable_form_structures_for_test() ->emplace(form_structure_ptr->unique_renderer_id(), std::move(form_structure)) @@ -3106,7 +3183,7 @@ TEST_F(AutofillMetricsTest, StoredProfileCountAutofillableFormSubmission) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); @@ -3139,7 +3216,7 @@ TEST_F(AutofillMetricsTest, StoredProfileCountNonAutofillableFormSubmission) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); @@ -3415,7 +3492,7 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) { // number of fields enforced). { base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->Reset(); histogram_tester.ExpectTotalCount("Autofill.DeveloperEngagement", 0); } @@ -3427,7 +3504,7 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) { // Expect the "form parsed without hints" metric to be logged. { base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->Reset(); histogram_tester.ExpectUniqueSample( "Autofill.DeveloperEngagement", @@ -3452,7 +3529,7 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) { // Expect the "form parsed with field type hints" metric to be logged. { base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->Reset(); histogram_tester.ExpectBucketCount( "Autofill.DeveloperEngagement", @@ -3472,7 +3549,7 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) { // "author-specified upi-vpa type" metric to be logged. { base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->Reset(); histogram_tester.ExpectBucketCount( "Autofill.DeveloperEngagement", @@ -3505,7 +3582,7 @@ TEST_F(AutofillMetricsTest, // Ensure no entries are logged when loading a non-fillable form. { - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->Reset(); EXPECT_EQ(0ul, test_ukm_recorder_->entries_count()); @@ -3518,12 +3595,12 @@ TEST_F(AutofillMetricsTest, // Expect the "form parsed without field type hints" metric and the // "form loaded" form interaction event to be logged. { - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->Reset(); VerifyDeveloperEngagementUkm( test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false, - {FormType::ADDRESS_FORM}, + {FormType::kAddressForm}, {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS}); } } @@ -3569,12 +3646,12 @@ TEST_F(AutofillMetricsTest, // Expect the "form parsed without field type hints" metric and the // "form loaded" form interaction event to be logged. { - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->Reset(); VerifyDeveloperEngagementUkm( test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false, - {FormType::ADDRESS_FORM}, + {FormType::kAddressForm}, {AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS}); } } @@ -3605,12 +3682,12 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) { { SCOPED_TRACE("VPA and other autocomplete hint present"); - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); VerifyDeveloperEngagementUkm( test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false, /* UPI VPA has Unknown form type.*/ - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE}, + {FormType::kAddressForm, FormType::kUnknownFormType}, {AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT}); PurgeUKM(); @@ -3829,7 +3906,8 @@ TEST_F(AutofillMetricsTest, AutofillProfileIsEnabledAtStartup) { personal_data_->SetAutofillProfileEnabled(true); personal_data_->Init(scoped_refptr<AutofillWebDataService>(nullptr), /*account_database=*/nullptr, - autofill_client_.GetPrefs(), + /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, @@ -3844,7 +3922,8 @@ TEST_F(AutofillMetricsTest, AutofillProfileIsDisabledAtStartup) { personal_data_->SetAutofillProfileEnabled(false); personal_data_->Init(scoped_refptr<AutofillWebDataService>(nullptr), /*account_database=*/nullptr, - autofill_client_.GetPrefs(), + /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, @@ -3859,7 +3938,8 @@ TEST_F(AutofillMetricsTest, AutofillCreditCardIsEnabledAtStartup) { personal_data_->SetAutofillCreditCardEnabled(true); personal_data_->Init(scoped_refptr<AutofillWebDataService>(nullptr), /*account_database=*/nullptr, - autofill_client_.GetPrefs(), + /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, @@ -3874,7 +3954,8 @@ TEST_F(AutofillMetricsTest, AutofillCreditCardIsDisabledAtStartup) { personal_data_->SetAutofillCreditCardEnabled(false); personal_data_->Init(scoped_refptr<AutofillWebDataService>(nullptr), /*account_database=*/nullptr, - autofill_client_.GetPrefs(), + /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, @@ -3889,8 +3970,8 @@ TEST_F(AutofillMetricsTest, AddressSuggestionsCount) { FormData form; form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); - form.url = GURL("http://example.com/form.html"); - form.action = GURL("http://example.com/submit.html"); + form.url = GURL("https://example.com/form.html"); + form.action = GURL("https://example.com/submit.html"); form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin()); FormFieldData field; @@ -3962,8 +4043,8 @@ TEST_F(AutofillMetricsTest, CompanyNameSuggestions) { FormData form; form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); - form.url = GURL("http://example.com/form.html"); - form.action = GURL("http://example.com/submit.html"); + form.url = GURL("https://example.com/form.html"); + form.action = GURL("https://example.com/submit.html"); form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin()); FormFieldData field; @@ -4003,8 +4084,8 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { FormData form; form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); - form.url = GURL("http://example.com/form.html"); - form.action = GURL("http://example.com/submit.html"); + form.url = GURL("https://example.com/form.html"); + form.action = GURL("https://example.com/submit.html"); form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin()); FormFieldData field; @@ -4181,7 +4262,7 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { VerifySubmitFormUkm(test_ukm_recorder_, form, AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/true, /* has_upi_vpa_field=*/false, - {FormType::CREDIT_CARD_FORM}); + {FormType::kCreditCardForm}); } // Test that the UPI Checkout flow form submit is correctly logged @@ -4200,14 +4281,14 @@ TEST_F(AutofillMetricsTest, UpiVpaUkmTest) { std::vector<FormData> forms(1, form); { - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); VerifySubmitFormUkm(test_ukm_recorder_, forms.back(), AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/false, /* has_upi_vpa_field */ true, /* UPI VPA has Unknown form type.*/ - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE}); + {FormType::kAddressForm, FormType::kUnknownFormType}); PurgeUKM(); } } @@ -4221,8 +4302,8 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) { FormData form; form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); - form.url = GURL("http://example.com/form.html"); - form.action = GURL("http://example.com/submit.html"); + form.url = GURL("https://example.com/form.html"); + form.action = GURL("https://example.com/submit.html"); form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin()); FormFieldData field; @@ -4349,7 +4430,7 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) { VerifySubmitFormUkm(test_ukm_recorder_, form, AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/false, - /* has_upi_vpa_field=*/false, {FormType::ADDRESS_FORM}); + /* has_upi_vpa_field=*/false, {FormType::kAddressForm}); } // Tests that the Autofill_PolledCreditCardSuggestions user action is only @@ -4445,6 +4526,15 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) { form.unique_renderer_id = MakeFormRendererId(); form.url = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); + // In order to test that the QueriedCreditCardFormIsSecure is logged as + // false, we need to set the main frame origin, otherwise this fill is + // skipped due to the form being detected as mixed content. + GURL client_form_origin = autofill_client_.form_origin(); + GURL::Replacements replacements; + replacements.SetScheme(url::kHttpScheme, + url::Component(0, strlen(url::kHttpScheme))); + autofill_client_.set_form_origin( + client_form_origin.ReplaceComponents(replacements)); form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin()); autofill_manager_->AddSeenForm(form, field_types, field_types); @@ -4457,6 +4547,8 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) { /*autoselect_first_suggestion=*/false); histogram_tester.ExpectUniqueSample( "Autofill.QueriedCreditCardFormIsSecure", false, 1); + // Reset the main frame origin to secure for other tests + autofill_client_.set_form_origin(client_form_origin); } { @@ -4488,8 +4580,8 @@ TEST_F(AutofillMetricsTest, PolledProfileSuggestions_DebounceLogs) { FormData form; form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); - form.url = GURL("http://example.com/form.html"); - form.action = GURL("http://example.com/submit.html"); + form.url = GURL("https://example.com/form.html"); + form.action = GURL("https://example.com/submit.html"); form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin()); FormFieldData field; @@ -4567,7 +4659,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardParsedFormEvents) { forms.push_back(form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, base::TimeTicks()); + autofill_manager_->OnFormsSeen(forms); histogram_tester.ExpectUniqueSample( "Autofill.FormEvents.CreditCard.WithNoData", FORM_EVENT_DID_PARSE_FORM, 1); @@ -5558,7 +5650,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/true, /* has_upi_vpa_field=*/false, - {FormType::CREDIT_CARD_FORM}); + {FormType::kCreditCardForm}); } // Reset the autofill manager state and purge UKM logs. @@ -5602,7 +5694,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/true, /* has_upi_vpa_field=*/false, - {FormType::CREDIT_CARD_FORM}); + {FormType::kCreditCardForm}); } // Reset the autofill manager state and purge UKM logs. @@ -5650,7 +5742,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/true, /* has_upi_vpa_field=*/false, - {FormType::CREDIT_CARD_FORM}); + {FormType::kCreditCardForm}); } // Reset the autofill manager state and purge UKM logs. @@ -5696,7 +5788,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/true, /* has_upi_vpa_field=*/false, - {FormType::CREDIT_CARD_FORM}); + {FormType::kCreditCardForm}); } // Reset the autofill manager state and purge UKM logs. @@ -5743,7 +5835,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/true, /* has_upi_vpa_field=*/false, - {FormType::CREDIT_CARD_FORM}); + {FormType::kCreditCardForm}); } // Reset the autofill manager state and purge UKM logs. @@ -5789,7 +5881,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/true, /* has_upi_vpa_field=*/false, - {FormType::CREDIT_CARD_FORM}); + {FormType::kCreditCardForm}); } // Reset the autofill manager state and purge UKM logs. @@ -5817,7 +5909,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/true, /* has_upi_vpa_field=*/false, - {FormType::CREDIT_CARD_FORM}); + {FormType::kCreditCardForm}); autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); @@ -5830,7 +5922,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { {UkmFormSubmittedType::kIsForCreditCardName, true}, {UkmFormSubmittedType::kHasUpiVpaFieldName, false}, {UkmFormSubmittedType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::CREDIT_CARD_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kCreditCardForm})}, {UkmFormSubmittedType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}, {{UkmFormSubmittedType::kAutofillFormSubmittedStateName, @@ -5839,7 +5931,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { {UkmFormSubmittedType::kIsForCreditCardName, true}, {UkmFormSubmittedType::kHasUpiVpaFieldName, false}, {UkmFormSubmittedType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::CREDIT_CARD_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kCreditCardForm})}, {UkmFormSubmittedType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}}); @@ -5993,7 +6085,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/true, /* has_upi_vpa_field=*/false, - {FormType::CREDIT_CARD_FORM}); + {FormType::kCreditCardForm}); } } @@ -6324,8 +6416,8 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) { // Set up our form data. FormData form; form.name = ASCIIToUTF16("TestForm"); - form.url = GURL("http://example.com/form.html"); - form.action = GURL("http://example.com/submit.html"); + form.url = GURL("https://example.com/form.html"); + form.action = GURL("https://example.com/submit.html"); form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin()); FormFieldData field; @@ -6606,7 +6698,7 @@ TEST_F(AutofillMetricsTest, MixedParsedFormEvents) { forms.push_back(form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, base::TimeTicks()); + autofill_manager_->OnFormsSeen(forms); histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address.WithNoData", FORM_EVENT_DID_PARSE_FORM, 1); histogram_tester.ExpectUniqueSample( @@ -6641,7 +6733,7 @@ TEST_F(AutofillMetricsTest, AddressParsedFormEvents) { forms.push_back(form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, base::TimeTicks()); + autofill_manager_->OnFormsSeen(forms); histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address.WithNoData", FORM_EVENT_DID_PARSE_FORM, 1); @@ -6653,7 +6745,7 @@ TEST_F(AutofillMetricsTest, AddressParsedFormEvents) { test_ukm_recorder_, form, UkmFormEventType::kEntryName, {{{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_DID_PARSE_FORM}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); } @@ -6700,7 +6792,7 @@ TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) { {{{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_INTERACTED_ONCE}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); } @@ -6727,7 +6819,7 @@ TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) { {{{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_INTERACTED_ONCE}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); } } @@ -6778,12 +6870,12 @@ TEST_F(AutofillMetricsTest, AddressSuppressedFormEvents) { {{{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_POPUP_SUPPRESSED}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, {{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_POPUP_SUPPRESSED_ONCE}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); } @@ -6811,17 +6903,17 @@ TEST_F(AutofillMetricsTest, AddressSuppressedFormEvents) { {{{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_POPUP_SUPPRESSED}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, {{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_POPUP_SUPPRESSED_ONCE}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, {{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_POPUP_SUPPRESSED}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); } } @@ -6871,12 +6963,12 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) { {{{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_SUGGESTIONS_SHOWN}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, {{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_SUGGESTIONS_SHOWN_ONCE}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); } @@ -6903,17 +6995,17 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) { {{{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_SUGGESTIONS_SHOWN}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, {{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_SUGGESTIONS_SHOWN_ONCE}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, {{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_SUGGESTIONS_SHOWN}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); } @@ -6987,12 +7079,12 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) { {{{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_LOCAL_SUGGESTION_FILLED}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, {{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE}, {UkmFormEventType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); } @@ -7103,7 +7195,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { VerifySubmitFormUkm(test_ukm_recorder_, form, AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/false, - /* has_upi_vpa_field=*/false, {FormType::ADDRESS_FORM}); + /* has_upi_vpa_field=*/false, {FormType::kAddressForm}); } // Reset the autofill manager state and purge UKM logs. @@ -7132,7 +7224,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { VerifySubmitFormUkm(test_ukm_recorder_, form, AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/false, - /* has_upi_vpa_field=*/false, {FormType::ADDRESS_FORM}); + /* has_upi_vpa_field=*/false, {FormType::kAddressForm}); } // Reset the autofill manager state and purge UKM logs. @@ -7651,7 +7743,7 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) { TEST_F(AutofillMetricsTest, AutofillProfileIsEnabledAtPageLoad) { base::HistogramTester histogram_tester; autofill_manager_->SetAutofillProfileEnabled(true); - autofill_manager_->OnFormsSeen(std::vector<FormData>(), TimeTicks()); + autofill_manager_->OnFormsSeen(std::vector<FormData>()); histogram_tester.ExpectUniqueSample("Autofill.Address.IsEnabled.PageLoad", true, 1); } @@ -7660,7 +7752,7 @@ TEST_F(AutofillMetricsTest, AutofillProfileIsEnabledAtPageLoad) { TEST_F(AutofillMetricsTest, AutofillProfileIsDisabledAtPageLoad) { base::HistogramTester histogram_tester; autofill_manager_->SetAutofillProfileEnabled(false); - autofill_manager_->OnFormsSeen(std::vector<FormData>(), TimeTicks()); + autofill_manager_->OnFormsSeen(std::vector<FormData>()); histogram_tester.ExpectUniqueSample("Autofill.Address.IsEnabled.PageLoad", false, 1); } @@ -7669,7 +7761,7 @@ TEST_F(AutofillMetricsTest, AutofillProfileIsDisabledAtPageLoad) { TEST_F(AutofillMetricsTest, AutofillCreditCardIsEnabledAtPageLoad) { base::HistogramTester histogram_tester; autofill_manager_->SetAutofillCreditCardEnabled(true); - autofill_manager_->OnFormsSeen(std::vector<FormData>(), TimeTicks()); + autofill_manager_->OnFormsSeen(std::vector<FormData>()); histogram_tester.ExpectUniqueSample("Autofill.CreditCard.IsEnabled.PageLoad", true, 1); } @@ -7678,7 +7770,7 @@ TEST_F(AutofillMetricsTest, AutofillCreditCardIsEnabledAtPageLoad) { TEST_F(AutofillMetricsTest, AutofillCreditCardIsDisabledAtPageLoad) { base::HistogramTester histogram_tester; autofill_manager_->SetAutofillCreditCardEnabled(false); - autofill_manager_->OnFormsSeen(std::vector<FormData>(), TimeTicks()); + autofill_manager_->OnFormsSeen(std::vector<FormData>()); histogram_tester.ExpectUniqueSample("Autofill.CreditCard.IsEnabled.PageLoad", false, 1); } @@ -7728,12 +7820,12 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { // Expect no notifications when the form is first seen. { base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); histogram_tester.ExpectTotalCount("Autofill.FormSubmittedState", 0); VerifyDeveloperEngagementUkm( test_ukm_recorder_, form, /*is_for_credit_card=*/false, - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE}, + {FormType::kAddressForm, FormType::kUnknownFormType}, {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS}); } @@ -7760,7 +7852,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { {UkmFormSubmittedType::kHasUpiVpaFieldName, false}, {UkmFormSubmittedType::kFormTypesName, AutofillMetrics::FormTypesToBitVector( - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE})}, + {FormType::kAddressForm, FormType::kUnknownFormType})}, {UkmFormSubmittedType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}); VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName, @@ -7795,7 +7887,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { {UkmFormSubmittedType::kHasUpiVpaFieldName, false}, {UkmFormSubmittedType::kFormTypesName, AutofillMetrics::FormTypesToBitVector( - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE})}, + {FormType::kAddressForm, FormType::kUnknownFormType})}, {UkmFormSubmittedType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}); VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName, @@ -7834,7 +7926,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { {UkmFormSubmittedType::kHasUpiVpaFieldName, false}, {UkmFormSubmittedType::kFormTypesName, AutofillMetrics::FormTypesToBitVector( - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE})}, + {FormType::kAddressForm, FormType::kUnknownFormType})}, {UkmFormSubmittedType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}); @@ -7880,7 +7972,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { {UkmFormSubmittedType::kHasUpiVpaFieldName, false}, {UkmFormSubmittedType::kFormTypesName, AutofillMetrics::FormTypesToBitVector( - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE})}, + {FormType::kAddressForm, FormType::kUnknownFormType})}, {UkmFormSubmittedType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}); VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName, @@ -7915,7 +8007,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { {UkmFormSubmittedType::kHasUpiVpaFieldName, false}, {UkmFormSubmittedType::kFormTypesName, AutofillMetrics::FormTypesToBitVector( - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE})}, + {FormType::kAddressForm, FormType::kUnknownFormType})}, {UkmFormSubmittedType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}); VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName, @@ -7951,7 +8043,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { {UkmFormSubmittedType::kHasUpiVpaFieldName, false}, {UkmFormSubmittedType::kFormTypesName, AutofillMetrics::FormTypesToBitVector( - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE})}, + {FormType::kAddressForm, FormType::kUnknownFormType})}, {UkmFormSubmittedType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}); VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName, @@ -7997,10 +8089,10 @@ TEST_F( { base::HistogramTester histogram_tester; base::UserActionTester user_action_tester; - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); VerifyDeveloperEngagementUkm( test_ukm_recorder_, form, /*is_for_credit_card=*/false, - {FormType::ADDRESS_FORM}, + {FormType::kAddressForm}, {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS}); histogram_tester.ExpectTotalCount("Autofill.FormSubmittedState", 0); @@ -8029,7 +8121,7 @@ TEST_F( {UkmFormSubmittedType::kIsForCreditCardName, false}, {UkmFormSubmittedType::kHasUpiVpaFieldName, false}, {UkmFormSubmittedType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::ADDRESS_FORM})}, + AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, {UkmFormSubmittedType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}); VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName, @@ -8045,7 +8137,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessMetric_PasswordForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_AUTOFILL, PASSWORD_FIELD, + AutofillMetrics::USER_DID_AUTOFILL, FieldTypeGroup::kPasswordField, security_state::SecurityLevel::SECURITY_LEVEL_COUNT, /*profile_form_bitmask=*/0); histogram_tester.ExpectBucketCount("Autofill.UserHappiness", @@ -8060,7 +8152,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessMetric_PasswordForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_AUTOFILL, USERNAME_FIELD, + AutofillMetrics::USER_DID_AUTOFILL, FieldTypeGroup::kUsernameField, security_state::SecurityLevel::SECURITY_LEVEL_COUNT, /*profile_form_bitmask=*/0); histogram_tester.ExpectBucketCount("Autofill.UserHappiness", @@ -8077,7 +8169,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessMetric_UnknownForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_AUTOFILL, NO_GROUP, + AutofillMetrics::USER_DID_AUTOFILL, FieldTypeGroup::kNoGroup, security_state::SecurityLevel::SECURITY_LEVEL_COUNT, /*profile_form_bitmask=*/0); histogram_tester.ExpectBucketCount("Autofill.UserHappiness", @@ -8092,7 +8184,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessMetric_UnknownForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_AUTOFILL, TRANSACTION, + AutofillMetrics::USER_DID_AUTOFILL, FieldTypeGroup::kTransaction, security_state::SecurityLevel::SECURITY_LEVEL_COUNT, /*profile_form_bitmask=*/0); histogram_tester.ExpectBucketCount("Autofill.UserHappiness", @@ -8120,7 +8212,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_EmptyForm) { // Expect a notification when the form is first seen. { base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); histogram_tester.ExpectTotalCount("Autofill.UserHappiness", 0); histogram_tester.ExpectTotalCount("Autofill.UserHappiness.CreditCard", 0); histogram_tester.ExpectTotalCount("Autofill.UserHappiness.Address", 0); @@ -8161,7 +8253,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { { SCOPED_TRACE("First seen"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness", AutofillMetrics::FORMS_LOADED, 1); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.CreditCard", @@ -8325,7 +8417,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { // Expect a notification when the form is first seen. { base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness", AutofillMetrics::FORMS_LOADED, 1); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.Address", @@ -8360,7 +8452,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { } autofill_manager_->Reset(); - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); // Simulate suggestions shown twice for a single edit (i.e. multiple // keystrokes in a single field). { @@ -8514,7 +8606,8 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { Collapse(CalculateFormSignature(form)).value()}}}); VerifyUkm( test_ukm_recorder_, form, UkmTextFieldDidChangeType::kEntryName, - {{{UkmTextFieldDidChangeType::kFieldTypeGroupName, NAME}, + {{{UkmTextFieldDidChangeType::kFieldTypeGroupName, + static_cast<int64_t>(FieldTypeGroup::kName)}, {UkmTextFieldDidChangeType::kHeuristicTypeName, NAME_FULL}, {UkmTextFieldDidChangeType::kServerTypeName, NO_SERVER_DATA}, {UkmTextFieldDidChangeType::kHtmlFieldTypeName, HTML_TYPE_UNSPECIFIED}, @@ -8526,7 +8619,8 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { Collapse(CalculateFieldSignatureForField(form.fields[0])).value()}, {UkmTextFieldDidChangeType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}, - {{UkmTextFieldDidChangeType::kFieldTypeGroupName, NAME}, + {{UkmTextFieldDidChangeType::kFieldTypeGroupName, + static_cast<int64_t>(FieldTypeGroup::kName)}, {UkmTextFieldDidChangeType::kHeuristicTypeName, NAME_FULL}, {UkmTextFieldDidChangeType::kServerTypeName, NO_SERVER_DATA}, {UkmTextFieldDidChangeType::kHtmlFieldTypeName, HTML_TYPE_UNSPECIFIED}, @@ -8538,7 +8632,8 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { Collapse(CalculateFieldSignatureForField(form.fields[0])).value()}, {UkmTextFieldDidChangeType::kFormSignatureName, Collapse(CalculateFormSignature(form)).value()}}, - {{UkmTextFieldDidChangeType::kFieldTypeGroupName, EMAIL}, + {{UkmTextFieldDidChangeType::kFieldTypeGroupName, + static_cast<int64_t>(FieldTypeGroup::kEmail)}, {UkmTextFieldDidChangeType::kHeuristicTypeName, EMAIL_ADDRESS}, {UkmTextFieldDidChangeType::kServerTypeName, NO_SERVER_DATA}, {UkmTextFieldDidChangeType::kHtmlFieldTypeName, HTML_TYPE_UNSPECIFIED}, @@ -8601,7 +8696,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { { SCOPED_TRACE("Test 1"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); base::TimeTicks parse_time = autofill_manager_->form_structures() .begin() ->second->form_parsed_timestamp(); @@ -8625,7 +8720,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { { SCOPED_TRACE("Test 2"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); base::TimeTicks parse_time = autofill_manager_->form_structures() .begin() ->second->form_parsed_timestamp(); @@ -8654,7 +8749,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { { SCOPED_TRACE("Test 3"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); base::TimeTicks parse_time = autofill_manager_->form_structures() .begin() ->second->form_parsed_timestamp(); @@ -8684,7 +8779,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { SCOPED_TRACE("Test 4"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); base::TimeTicks parse_time = autofill_manager_->form_structures() .begin() ->second->form_parsed_timestamp(); @@ -8716,11 +8811,11 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { { SCOPED_TRACE("Test 5"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); base::TimeTicks parse_time = autofill_manager_->form_structures() .begin() ->second->form_parsed_timestamp(); - autofill_manager_->OnFormsSeen(second_forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(second_forms); autofill_manager_->OnDidFillAutofillFormData( form, parse_time + base::TimeDelta::FromMicroseconds(5)); autofill_manager_->OnTextFieldDidChange( @@ -8748,8 +8843,8 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { { SCOPED_TRACE("Test 6"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); - autofill_manager_->OnFormsSeen(second_forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); + autofill_manager_->OnFormsSeen(second_forms); base::TimeTicks parse_time{}; for (const auto& kv : autofill_manager_->form_structures()) { if (kv.second->form_parsed_timestamp() > parse_time) @@ -8777,7 +8872,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_CreditCardForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {CREDIT_CARD_FORM}, true /* used_autofill */, + {FormType::kCreditCardForm}, true /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTimeBucketCount( "Autofill.FillDuration.FromInteraction.WithAutofill.CreditCard", @@ -8790,7 +8885,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_CreditCardForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {CREDIT_CARD_FORM}, false /* used_autofill */, + {FormType::kCreditCardForm}, false /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTimeBucketCount( "Autofill.FillDuration.FromInteraction.WithoutAutofill.CreditCard", @@ -8804,7 +8899,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_CreditCardForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {UNKNOWN_FORM_TYPE}, false /* used_autofill */, + {FormType::kUnknownFormType}, false /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTotalCount( "Autofill.FillDuration.FromInteraction.WithAutofill.CreditCard", 0); @@ -8818,7 +8913,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_AddressForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {ADDRESS_FORM}, true /* used_autofill */, + {FormType::kAddressForm}, true /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTimeBucketCount( "Autofill.FillDuration.FromInteraction.WithAutofill.Address", @@ -8831,7 +8926,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_AddressForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {ADDRESS_FORM}, false /* used_autofill */, + {FormType::kAddressForm}, false /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTimeBucketCount( "Autofill.FillDuration.FromInteraction.WithoutAutofill.Address", @@ -8845,7 +8940,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_AddressForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {UNKNOWN_FORM_TYPE}, false /* used_autofill */, + {FormType::kUnknownFormType}, false /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTotalCount( "Autofill.FillDuration.FromInteraction.WithAutofill.Address", 0); @@ -8859,7 +8954,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_PasswordForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {PASSWORD_FORM}, true /* used_autofill */, + {FormType::kPasswordForm}, true /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTimeBucketCount( "Autofill.FillDuration.FromInteraction.WithAutofill.Password", @@ -8872,7 +8967,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_PasswordForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {PASSWORD_FORM}, false /* used_autofill */, + {FormType::kPasswordForm}, false /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTimeBucketCount( "Autofill.FillDuration.FromInteraction.WithoutAutofill.Password", @@ -8886,7 +8981,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_PasswordForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {UNKNOWN_FORM_TYPE}, false /* used_autofill */, + {FormType::kUnknownFormType}, false /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTotalCount( "Autofill.FillDuration.FromInteraction.WithAutofill.Password", 0); @@ -8900,7 +8995,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_UnknownForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {UNKNOWN_FORM_TYPE}, true /* used_autofill */, + {FormType::kUnknownFormType}, true /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTimeBucketCount( "Autofill.FillDuration.FromInteraction.WithAutofill.Unknown", @@ -8913,7 +9008,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_UnknownForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {UNKNOWN_FORM_TYPE}, false /* used_autofill */, + {FormType::kUnknownFormType}, false /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTimeBucketCount( "Autofill.FillDuration.FromInteraction.WithoutAutofill.Unknown", @@ -8927,7 +9022,7 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_UnknownForm) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {ADDRESS_FORM}, false /* used_autofill */, + {FormType::kAddressForm}, false /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTotalCount( "Autofill.FillDuration.FromInteraction.WithAutofill.Unknown", 0); @@ -8941,7 +9036,8 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_MultipleForms) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {CREDIT_CARD_FORM, ADDRESS_FORM, PASSWORD_FORM, UNKNOWN_FORM_TYPE}, + {FormType::kCreditCardForm, FormType::kAddressForm, + FormType::kPasswordForm, FormType::kUnknownFormType}, true /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTimeBucketCount( "Autofill.FillDuration.FromInteraction.WithAutofill.CreditCard", @@ -8961,7 +9057,8 @@ TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_MultipleForms) { { base::HistogramTester histogram_tester; AutofillMetrics::LogFormFillDurationFromInteraction( - {CREDIT_CARD_FORM, ADDRESS_FORM, PASSWORD_FORM, UNKNOWN_FORM_TYPE}, + {FormType::kCreditCardForm, FormType::kAddressForm, + FormType::kPasswordForm, FormType::kUnknownFormType}, false /* used_autofill */, base::TimeDelta::FromMilliseconds(2000)); histogram_tester.ExpectTimeBucketCount( "Autofill.FillDuration.FromInteraction.WithoutAutofill.CreditCard", @@ -9055,7 +9152,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log NEW_PROFILE_CREATED for the metric since a new profile is // submitted. - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted", @@ -9068,7 +9165,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log EXISTING_PROFILE_USED for the metric since the same profile // is submitted. - autofill_manager_->OnFormsSeen(second_forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(second_forms); autofill_manager_->OnFormSubmitted(second_form, false, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted", @@ -9081,7 +9178,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log NEW_PROFILE_CREATED for the metric since a new profile is // submitted. - autofill_manager_->OnFormsSeen(third_forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(third_forms); autofill_manager_->OnFormSubmitted(third_form, false, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted", @@ -9094,7 +9191,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log EXISTING_PROFILE_UPDATED for the metric since the profile was // updated. - autofill_manager_->OnFormsSeen(fourth_forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(fourth_forms); autofill_manager_->OnFormSubmitted(fourth_form, false, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted", @@ -9327,7 +9424,9 @@ TEST_F(AutofillMetricsTest, form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); form.url = GURL("https://example.com/form.html"); - form.action = GURL("http://example.com/submit.html"); + // Form action needs to be secure on secure page, otherwise this triggers + // mixed form warnings and no suggestions are offered. + form.action = GURL("https://example.com/submit.html"); form.main_frame_origin = url::Origin::Create(GURL("http://example_root.com/form.html")); @@ -9402,7 +9501,7 @@ TEST_F(AutofillMetricsTest, RecordDeveloperEngagementMetric) { AutofillMetrics::LogDeveloperEngagementUkm( test_ukm_recorder_, autofill_client_.GetUkmSourceId(), url, true, - {FormType::CREDIT_CARD_FORM}, form_structure_metric, form_signature); + {FormType::kCreditCardForm}, form_structure_metric, form_signature); auto entries = test_ukm_recorder_->GetEntriesByName( UkmDeveloperEngagementType::kEntryName); EXPECT_EQ(1u, entries.size()); @@ -9416,7 +9515,7 @@ TEST_F(AutofillMetricsTest, RecordDeveloperEngagementMetric) { entry, UkmDeveloperEngagementType::kIsForCreditCardName, true); test_ukm_recorder_->ExpectEntryMetric( entry, UkmDeveloperEngagementType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector({FormType::CREDIT_CARD_FORM})); + AutofillMetrics::FormTypesToBitVector({FormType::kCreditCardForm})); test_ukm_recorder_->ExpectEntryMetric( entry, UkmDeveloperEngagementType::kFormSignatureName, form_signature.value()); @@ -9558,7 +9657,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel) { { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessBySecurityLevel( - AutofillMetrics::USER_DID_AUTOFILL, CREDIT_CARD_FORM, + AutofillMetrics::USER_DID_AUTOFILL, FormType::kCreditCardForm, security_state::SecurityLevel::SECURE); histogram_tester.ExpectBucketCount( "Autofill.UserHappiness.CreditCard.SECURE", @@ -9568,7 +9667,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel) { { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessBySecurityLevel( - AutofillMetrics::SUGGESTIONS_SHOWN, ADDRESS_FORM, + AutofillMetrics::SUGGESTIONS_SHOWN, FormType::kAddressForm, security_state::SecurityLevel::DANGEROUS); histogram_tester.ExpectBucketCount( "Autofill.UserHappiness.Address.DANGEROUS", @@ -9578,7 +9677,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel) { { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessBySecurityLevel( - AutofillMetrics::FIELD_WAS_AUTOFILLED, PASSWORD_FORM, + AutofillMetrics::FIELD_WAS_AUTOFILLED, FormType::kPasswordForm, security_state::SecurityLevel::WARNING); histogram_tester.ExpectBucketCount( "Autofill.UserHappiness.Password.WARNING", @@ -9588,7 +9687,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel) { { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessBySecurityLevel( - AutofillMetrics::USER_DID_AUTOFILL_ONCE, UNKNOWN_FORM_TYPE, + AutofillMetrics::USER_DID_AUTOFILL_ONCE, FormType::kUnknownFormType, security_state::SecurityLevel::SECURE); histogram_tester.ExpectBucketCount("Autofill.UserHappiness.Unknown.SECURE", AutofillMetrics::USER_DID_AUTOFILL_ONCE, @@ -9601,7 +9700,8 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessBySecurityLevel( AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME, - CREDIT_CARD_FORM, security_state::SecurityLevel::SECURITY_LEVEL_COUNT); + FormType::kCreditCardForm, + security_state::SecurityLevel::SECURITY_LEVEL_COUNT); histogram_tester.ExpectTotalCount("Autofill.UserHappiness.CreditCard.OTHER", 0); } @@ -9633,7 +9733,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel_FromFormEvents) { base::HistogramTester histogram_tester; autofill_client_.set_security_level( security_state::SecurityLevel::DANGEROUS); - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); histogram_tester.ExpectBucketCount( "Autofill.UserHappiness.Address.DANGEROUS", AutofillMetrics::FORMS_LOADED, 1); @@ -9656,7 +9756,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel_FromFormEvents) { TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_AddressOnly) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_TYPE, {FormType::ADDRESS_FORM}, + AutofillMetrics::USER_DID_TYPE, {FormType::kAddressForm}, security_state::SecurityLevel::NONE, data_util::DetermineGroups({ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, ADDRESS_HOME_DEPENDENT_LOCALITY})); @@ -9685,7 +9785,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_AddressOnly) { TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_ContactOnly) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_TYPE, {FormType::ADDRESS_FORM}, + AutofillMetrics::USER_DID_TYPE, {FormType::kAddressForm}, security_state::SecurityLevel::NONE, data_util::DetermineGroups({NAME_FIRST, NAME_LAST, EMAIL_ADDRESS})); @@ -9714,7 +9814,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_AddressPlusPhone) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_TYPE, {FormType::ADDRESS_FORM}, + AutofillMetrics::USER_DID_TYPE, {FormType::kAddressForm}, security_state::SecurityLevel::NONE, data_util::DetermineGroups( {NAME_FULL, ADDRESS_HOME_ZIP, PHONE_HOME_CITY_AND_NUMBER})); @@ -9747,7 +9847,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_AddressPlusEmail) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_TYPE, {FormType::ADDRESS_FORM}, + AutofillMetrics::USER_DID_TYPE, {FormType::kAddressForm}, security_state::SecurityLevel::NONE, data_util::DetermineGroups({NAME_FULL, ADDRESS_HOME_ZIP, EMAIL_ADDRESS})); @@ -9779,7 +9879,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_AddressPlusEmailPlusPhone) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_TYPE, {FormType::ADDRESS_FORM}, + AutofillMetrics::USER_DID_TYPE, {FormType::kAddressForm}, security_state::SecurityLevel::NONE, data_util::DetermineGroups({NAME_FULL, ADDRESS_HOME_ZIP, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER})); @@ -9810,7 +9910,7 @@ TEST_F(AutofillMetricsTest, TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_Other) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_TYPE, {FormType::ADDRESS_FORM}, + AutofillMetrics::USER_DID_TYPE, {FormType::kAddressForm}, security_state::SecurityLevel::NONE, data_util::DetermineGroups({NAME_FIRST, NAME_MIDDLE, NAME_LAST})); @@ -9838,7 +9938,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_Other) { TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_PhoneOnly) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::USER_DID_TYPE, {FormType::ADDRESS_FORM}, + AutofillMetrics::USER_DID_TYPE, {FormType::kAddressForm}, security_state::SecurityLevel::NONE, data_util::DetermineGroups({PHONE_HOME_NUMBER})); @@ -9866,7 +9966,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_FormsLoadedNotLogged) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric( - AutofillMetrics::FORMS_LOADED, {FormType::ADDRESS_FORM}, + AutofillMetrics::FORMS_LOADED, {FormType::kAddressForm}, security_state::SecurityLevel::NONE, data_util::DetermineGroups({NAME_FIRST, NAME_MIDDLE, NAME_LAST})); @@ -9893,7 +9993,7 @@ TEST_F(AutofillMetricsTest, LogUserHappinessByProfileFormType_NoAddressFormType) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED, - {FormType::CREDIT_CARD_FORM}, + {FormType::kCreditCardForm}, security_state::SecurityLevel::NONE, /*profile_form_bitmask=*/0); @@ -9903,98 +10003,13 @@ TEST_F(AutofillMetricsTest, Not(AnyOf(HasSubstr("Autofill.UserHappiness.Address")))); } -// Tests that the LogSaveCardPromptMetricBySecurityLevel are recorded correctly. -TEST_F(AutofillMetricsTest, LogSaveCardPromptMetricBySecurityLevel) { - { - base::HistogramTester histogram_tester; - AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, - /*is_uploading=*/true, security_state::SecurityLevel::SECURE); - histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Upload.SECURE", - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, 1); - } - - { - base::HistogramTester histogram_tester; - AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( - AutofillMetrics::SAVE_CARD_PROMPT_END_DENIED, /*is_uploading=*/false, - security_state::SecurityLevel::DANGEROUS); - histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Local.DANGEROUS", - AutofillMetrics::SAVE_CARD_PROMPT_END_DENIED, 1); - } - - { - base::HistogramTester histogram_tester; - AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( - AutofillMetrics::SAVE_CARD_PROMPT_END_ACCEPTED, /*is_uploading=*/true, - security_state::SecurityLevel::WARNING); - histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Upload.WARNING", - AutofillMetrics::SAVE_CARD_PROMPT_END_ACCEPTED, 1); - } - - { - base::HistogramTester histogram_tester; - AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( - AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, - /*is_uploading=*/false, security_state::SecurityLevel::SECURE); - histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Local.SECURE", - AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, 1); - } - - { - // No metric should be recorded if the security level is - // SECURITY_LEVEL_COUNT. - base::HistogramTester histogram_tester; - AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( - AutofillMetrics::SAVE_CARD_PROMPT_CVC_FIX_FLOW_SHOWN, - /*is_uploading=*/true, - security_state::SecurityLevel::SECURITY_LEVEL_COUNT); - histogram_tester.ExpectTotalCount( - "Autofill.SaveCreditCardPrompt.Upload.OTHER", 0); - } -} - -// Verify that we correctly log LogSaveCardPromptMetricBySecurityLevel from the -// save card prompt metrics. -TEST_F(AutofillMetricsTest, - LogSaveCardPromptMetricBySecurityLevel_FromSaveCardPromptMetric) { - { - base::HistogramTester histogram_tester; - AutofillMetrics::LogSaveCardPromptMetric( - AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, - /*is_uploading=*/true, /*is_reshow=*/false, - AutofillClient::SaveCreditCardOptions(), - /*previous_save_credit_card_prompt_user_decision=*/1, - security_state::SecurityLevel::SECURE, SyncSigninState::kSignedOut); - histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Upload.SECURE", - AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, 1); - } - - { - base::HistogramTester histogram_tester; - AutofillMetrics::LogSaveCardPromptMetric( - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, - /*is_uploading=*/false, - /*is_reshow=*/true, AutofillClient::SaveCreditCardOptions(), - /*previous_save_credit_card_prompt_user_decision=*/0, - security_state::SecurityLevel::SECURE, SyncSigninState::kSignedOut); - histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Local.SECURE", - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, 1); - } -} - // Verify that we don't log Autofill.WebOTP.OneTimeCode.FromAutocomplete if the // frame has no form. TEST_F(AutofillMetricsTest, FrameHasNoForm) { base::HistogramTester histogram_tester; autofill_manager_.reset(); - histogram_tester.ExpectTotalCount("Autofill.WebOTP.OneTimeCode.FromAutocomplete", 0); + histogram_tester.ExpectTotalCount( + "Autofill.WebOTP.OneTimeCode.FromAutocomplete", 0); } // Verify that we correctly log metrics if a frame has @@ -10018,7 +10033,7 @@ TEST_F(AutofillMetricsTest, FrameHasAutocompleteOneTimeCode) { forms_with_one_time_code.back().fields.push_back(field); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms_with_one_time_code, TimeTicks()); + autofill_manager_->OnFormsSeen(forms_with_one_time_code); autofill_manager_.reset(); // Verifies that autocomplete="one-time-code" in a form is correctly recorded. histogram_tester.ExpectBucketCount( @@ -10046,7 +10061,7 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHaveAutocompleteOneTimeCode) { forms_without_one_time_code.back().fields.push_back(field); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms_without_one_time_code, TimeTicks()); + autofill_manager_->OnFormsSeen(forms_without_one_time_code); autofill_manager_.reset(); histogram_tester.ExpectBucketCount( "Autofill.WebOTP.OneTimeCode.FromAutocomplete", @@ -10080,7 +10095,7 @@ TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithoutAutocomplete) { forms_with_phone_number.back().fields.push_back(field); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms_with_phone_number, TimeTicks()); + autofill_manager_->OnFormsSeen(forms_with_phone_number); autofill_manager_.reset(); histogram_tester.ExpectBucketCount( "Autofill.WebOTP.PhoneNumberCollection.ParseResult", @@ -10109,8 +10124,7 @@ TEST_F(AutofillMetricsTest, FrameHasSinglePhoneNumberFieldWithoutAutocomplete) { forms_with_single_phone_number_field.back().fields.push_back(field); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms_with_single_phone_number_field, - TimeTicks()); + autofill_manager_->OnFormsSeen(forms_with_single_phone_number_field); autofill_manager_.reset(); histogram_tester.ExpectBucketCount( "Autofill.WebOTP.PhoneNumberCollection.ParseResult", @@ -10129,7 +10143,7 @@ TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithAutocomplete) { std::vector<FormData> forms_with_phone_number(1, form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms_with_phone_number, TimeTicks()); + autofill_manager_->OnFormsSeen(forms_with_phone_number); autofill_manager_.reset(); histogram_tester.ExpectBucketCount( "Autofill.WebOTP.PhoneNumberCollection.ParseResult", @@ -10156,7 +10170,7 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHavePhoneNumberField) { forms_without_phone_number.back().fields.push_back(field); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms_without_phone_number, TimeTicks()); + autofill_manager_->OnFormsSeen(forms_without_phone_number); autofill_manager_.reset(); histogram_tester.ExpectBucketCount( "Autofill.WebOTP.PhoneNumberCollection.ParseResult", @@ -10177,7 +10191,7 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateNone) { std::vector<FormData> forms(1, form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_driver_->SetAutofillManager(std::move(autofill_manager_)); static_cast<ContentAutofillDriver*>(autofill_driver_.get()) ->ReportAutofillWebOTPMetrics(false); @@ -10195,7 +10209,7 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateOTC) { std::vector<FormData> forms(1, form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_driver_->SetAutofillManager(std::move(autofill_manager_)); static_cast<ContentAutofillDriver*>(autofill_driver_.get()) ->ReportAutofillWebOTPMetrics(false); @@ -10227,7 +10241,7 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateWebOTPPlusOTC) { std::vector<FormData> forms(1, form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_driver_->SetAutofillManager(std::move(autofill_manager_)); static_cast<ContentAutofillDriver*>(autofill_driver_.get()) ->ReportAutofillWebOTPMetrics(true); @@ -10246,7 +10260,7 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhone) { std::vector<FormData> forms(1, form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_driver_->SetAutofillManager(std::move(autofill_manager_)); static_cast<ContentAutofillDriver*>(autofill_driver_.get()) ->ReportAutofillWebOTPMetrics(false); @@ -10265,7 +10279,7 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhonePlusOTC) { std::vector<FormData> forms(1, form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_driver_->SetAutofillManager(std::move(autofill_manager_)); static_cast<ContentAutofillDriver*>(autofill_driver_.get()) ->ReportAutofillWebOTPMetrics(false); @@ -10284,7 +10298,7 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhonePlusWebOTP) { std::vector<FormData> forms(1, form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_driver_->SetAutofillManager(std::move(autofill_manager_)); static_cast<ContentAutofillDriver*>(autofill_driver_.get()) ->ReportAutofillWebOTPMetrics(true); @@ -10306,7 +10320,7 @@ TEST_F(AutofillMetricsTest, std::vector<FormData> forms(1, form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_driver_->SetAutofillManager(std::move(autofill_manager_)); static_cast<ContentAutofillDriver*>(autofill_driver_.get()) ->ReportAutofillWebOTPMetrics(true); @@ -10332,7 +10346,7 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateLoggedToUKM) { std::vector<FormData> forms(1, form); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_driver_->SetAutofillManager(std::move(autofill_manager_)); static_cast<ContentAutofillDriver*>(autofill_driver_.get()) ->ReportAutofillWebOTPMetrics(/* Document uses WebOTP */ true); @@ -10453,7 +10467,7 @@ TEST_F(AutofillMetricsTest, FormEventMetrics_BySyncState) { FormData form; FormStructure form_structure(form); std::vector<FormData> forms(1, form); - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(forms); autofill_manager_->Reset(); { @@ -10504,38 +10518,6 @@ TEST_F(AutofillMetricsTest, LogIsAutofillEnabledAtPageLoad_BySyncState) { } } -// Verify that we correctly log FormEvent metrics with the appropriate sync -// state. -TEST_F(AutofillMetricsTest, LogSaveCardPromptMetric_BySyncState) { - { - base::HistogramTester histogram_tester; - AutofillMetrics::LogSaveCardPromptMetric( - AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, - /*is_uploading=*/true, /*is_reshow=*/false, - AutofillClient::SaveCreditCardOptions(), - /*previous_save_credit_card_prompt_user_decision=*/1, - security_state::SecurityLevel::SECURE, SyncSigninState::kSignedIn); - histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Upload.FirstShow.SignedIn", - AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, 1); - } - - { - base::HistogramTester histogram_tester; - AutofillMetrics::LogSaveCardPromptMetric( - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, - /*is_uploading=*/false, - /*is_reshow=*/true, AutofillClient::SaveCreditCardOptions(), - /*previous_save_credit_card_prompt_user_decision=*/0, - security_state::SecurityLevel::SECURE, - SyncSigninState::kSignedInAndSyncFeatureEnabled); - histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Local.Reshows." - "SignedInAndSyncFeatureEnabled", - AutofillMetrics::SAVE_CARD_PROMPT_SHOWN_DEPRECATED, 1); - } -} - TEST_F(AutofillMetricsTest, LogServerCardLinkClicked) { { base::HistogramTester histogram_tester; @@ -10606,7 +10588,7 @@ TEST_P(AutofillMetricsFunnelTest, LogFunnelMetrics) { const bool user_submitted_form = GetParam() >= 4; // Simulate that the autofill manager has seen this form on page load. - autofill_manager_->OnFormsSeen({form}, TimeTicks()); + autofill_manager_->OnFormsSeen({form}); if (!user_saw_suggestion) { // Remove the profile to prevent suggestion from being shown. @@ -10763,7 +10745,7 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogEmptyForm) { base::HistogramTester histogram_tester; // Simulate page load. - autofill_manager_->OnFormsSeen({form_}, TimeTicks()); + autofill_manager_->OnFormsSeen({form_}); autofill_manager_->OnQueryFormFieldAutofill( 0, form_, form_.fields[0], gfx::RectF(), /*autoselect_first_suggestion=*/false); @@ -10794,7 +10776,7 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogNoProfile) { // Simulate that no data is available. personal_data_->ClearProfiles(); - autofill_manager_->OnFormsSeen({form_}, TimeTicks()); + autofill_manager_->OnFormsSeen({form_}); autofill_manager_->OnQueryFormFieldAutofill( 0, form_, form_.fields[0], gfx::RectF(), /*autoselect_first_suggestion=*/false); @@ -10829,7 +10811,7 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserDoesNotAcceptSuggestion) { base::HistogramTester histogram_tester; // Simulate that suggestion is shown but user does not accept it. - autofill_manager_->OnFormsSeen({form_}, TimeTicks()); + autofill_manager_->OnFormsSeen({form_}); autofill_manager_->OnQueryFormFieldAutofill( 0, form_, form_.fields[0], gfx::RectF(), /*autoselect_first_suggestion=*/false); @@ -10866,7 +10848,7 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledData) { base::HistogramTester histogram_tester; // Simulate that suggestion is shown and user accepts it. - autofill_manager_->OnFormsSeen({form_}, TimeTicks()); + autofill_manager_->OnFormsSeen({form_}); autofill_manager_->OnQueryFormFieldAutofill( 0, form_, form_.fields[0], gfx::RectF(), /*autoselect_first_suggestion=*/false); @@ -10905,7 +10887,7 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledDataButDoesNotSubmit) { base::HistogramTester histogram_tester; // Simulate that suggestion is shown and user accepts it. - autofill_manager_->OnFormsSeen({form_}, TimeTicks()); + autofill_manager_->OnFormsSeen({form_}); autofill_manager_->OnQueryFormFieldAutofill( 0, form_, form_.fields[0], gfx::RectF(), /*autoselect_first_suggestion=*/false); @@ -10957,6 +10939,9 @@ TEST_F(AutofillMetricsTest, PageLanguageMetricsExpectedCase) { CreateSimpleForm(autofill_client_.form_origin(), form); // Set up language state. + translate::LanguageDetectionDetails language_detection_details; + language_detection_details.adopted_language = "ub"; + autofill_manager_->OnLanguageDetermined(language_detection_details); autofill_client_.GetLanguageState()->SetOriginalLanguage("ub"); autofill_client_.GetLanguageState()->SetCurrentLanguage("ub"); int language_code = 'u' * 256 + 'b'; @@ -10979,7 +10964,10 @@ TEST_F(AutofillMetricsTest, PageLanguageMetricsInvalidLanguage) { CreateSimpleForm(autofill_client_.form_origin(), form); // Set up language state. - autofill_client_.GetLanguageState()->SetOriginalLanguage("!ab"); + translate::LanguageDetectionDetails language_detection_details; + language_detection_details.adopted_language = "en"; + autofill_manager_->OnLanguageDetermined(language_detection_details); + autofill_client_.GetLanguageState()->SetOriginalLanguage("en"); autofill_client_.GetLanguageState()->SetCurrentLanguage("other"); // Simulate form submission. diff --git a/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc b/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc index f82bcdf9778..313cd9d1670 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc @@ -18,7 +18,7 @@ #include "components/autofill/core/browser/geo/country_names.h" #include "components/autofill/core/browser/proto/autofill_sync.pb.h" #include "components/autofill/core/browser/webdata/autofill_table.h" -#include "components/sync/model/entity_data.h" +#include "components/sync/engine/entity_data.h" using autofill::data_util::TruncateUTF8; using base::UTF16ToUTF8; @@ -120,6 +120,8 @@ std::unique_ptr<EntityData> CreateEntityDataFromAutofillProfile( TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(NAME_LAST_CONJUNCTION)))); specifics->add_name_full( TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(NAME_FULL)))); + specifics->add_name_full_with_honorific(TruncateUTF8( + UTF16ToUTF8(entry.GetRawInfo(NAME_FULL_WITH_HONORIFIC_PREFIX)))); // Set address-related statuses. specifics->add_name_honorific_status( @@ -142,6 +144,9 @@ std::unique_ptr<EntityData> CreateEntityDataFromAutofillProfile( entry.GetVerificationStatus(NAME_LAST_SECOND))); specifics->add_name_full_status(ConvertProfileToSpecificsVerificationStatus( entry.GetVerificationStatus(NAME_FULL))); + specifics->add_name_full_with_honorific_status( + ConvertProfileToSpecificsVerificationStatus( + entry.GetVerificationStatus(NAME_FULL_WITH_HONORIFIC_PREFIX))); // Set email, phone and company values. specifics->add_email_address( @@ -176,6 +181,10 @@ std::unique_ptr<EntityData> CreateEntityDataFromAutofillProfile( UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_DEPENDENT_STREET_NAME))); specifics->set_address_home_subpremise_name( UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_SUBPREMISE))); + specifics->set_address_home_apt_num( + UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_APT_NUM))); + specifics->set_address_home_floor( + UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_FLOOR))); specifics->set_address_home_premise_name( UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_PREMISE_NAME))); specifics->set_address_home_thoroughfare_number( @@ -212,6 +221,12 @@ std::unique_ptr<EntityData> CreateEntityDataFromAutofillProfile( specifics->set_address_home_subpremise_name_status( ConvertProfileToSpecificsVerificationStatus( entry.GetVerificationStatus(ADDRESS_HOME_SUBPREMISE))); + specifics->set_address_home_apt_num_status( + ConvertProfileToSpecificsVerificationStatus( + entry.GetVerificationStatus(ADDRESS_HOME_APT_NUM))); + specifics->set_address_home_floor_status( + ConvertProfileToSpecificsVerificationStatus( + entry.GetVerificationStatus(ADDRESS_HOME_FLOOR))); specifics->set_address_home_premise_name_status( ConvertProfileToSpecificsVerificationStatus( entry.GetVerificationStatus(ADDRESS_HOME_PREMISE_NAME))); @@ -250,6 +265,17 @@ std::unique_ptr<AutofillProfile> CreateAutofillProfileFromSpecifics( AutofillProfileSpecifics_VerificationStatus_VERIFICATION_STATUS_UNSPECIFIED)); profile->SetRawInfoWithVerificationStatus( + NAME_FULL_WITH_HONORIFIC_PREFIX, + UTF8ToUTF16(specifics.name_full_with_honorific_size() + ? specifics.name_full_with_honorific(0) + : std::string()), + ConvertSpecificsToProfileVerificationStatus( + specifics.name_full_with_honorific_status_size() + ? specifics.name_full_with_honorific_status(0) + : AutofillProfileSpecifics::VerificationStatus:: + AutofillProfileSpecifics_VerificationStatus_VERIFICATION_STATUS_UNSPECIFIED)); + + profile->SetRawInfoWithVerificationStatus( NAME_FIRST, UTF8ToUTF16(specifics.name_first_size() ? specifics.name_first(0) : std::string()), diff --git a/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc index f6468b412a1..1388ab1f0c7 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc @@ -13,7 +13,7 @@ #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_features.h" -#include "components/sync/model/entity_data.h" +#include "components/sync/engine/entity_data.h" #include "components/sync/protocol/sync.pb.h" #include "testing/gtest/include/gtest/gtest.h" namespace autofill { @@ -43,8 +43,14 @@ AutofillProfile ConstructCompleteProfile() { profile.set_use_date(base::Time::FromTimeT(1423182152)); // Set testing values and statuses for the name. - profile.SetRawInfoWithVerificationStatus( - NAME_HONORIFIC_PREFIX, ASCIIToUTF16(""), VerificationStatus::kNoStatus); + profile.SetRawInfoWithVerificationStatus(NAME_HONORIFIC_PREFIX, + ASCIIToUTF16("Dr."), + VerificationStatus::kObserved); + + profile.SetRawInfoWithVerificationStatus(NAME_FULL_WITH_HONORIFIC_PREFIX, + ASCIIToUTF16("Dr. John K. Doe"), + VerificationStatus::kFormatted); + profile.SetRawInfoWithVerificationStatus(NAME_FULL, ASCIIToUTF16("John K. Doe"), VerificationStatus::kUserVerified); @@ -65,15 +71,17 @@ AutofillProfile ConstructCompleteProfile() { profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("user@example.com")); profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("1.800.555.1234")); profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google, Inc.")); - profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STREET_ADDRESS, - ASCIIToUTF16("123 Fake St.\n" - "Apt. 42"), - VerificationStatus::kObserved); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, + ASCIIToUTF16("123 Fake St. Dep Premise\n" + "Apt. 10 Floor 2"), + VerificationStatus::kObserved); // Set testing values and statuses for the address. - EXPECT_EQ(ASCIIToUTF16("123 Fake St."), + EXPECT_EQ(ASCIIToUTF16("123 Fake St. Dep Premise"), profile.GetRawInfo(ADDRESS_HOME_LINE1)); - EXPECT_EQ(ASCIIToUTF16("Apt. 42"), profile.GetRawInfo(ADDRESS_HOME_LINE2)); + EXPECT_EQ(ASCIIToUTF16("Apt. 10 Floor 2"), + profile.GetRawInfo(ADDRESS_HOME_LINE2)); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_CITY, ASCIIToUTF16("Mountain View"), @@ -98,19 +106,23 @@ AutofillProfile ConstructCompleteProfile() { VerificationStatus::kObserved); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STREET_NAME, - ASCIIToUTF16("Street Name"), + ASCIIToUTF16("Fake St."), + VerificationStatus::kFormatted); + profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_DEPENDENT_STREET_NAME, + ASCIIToUTF16("Dep"), VerificationStatus::kFormatted); - profile.SetRawInfoWithVerificationStatus( - ADDRESS_HOME_DEPENDENT_STREET_NAME, ASCIIToUTF16("Dependent Street Name"), - VerificationStatus::kFormatted); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_HOUSE_NUMBER, - ASCIIToUTF16("House Number"), + ASCIIToUTF16("123"), VerificationStatus::kFormatted); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_SUBPREMISE, - ASCIIToUTF16("Subpremise"), - VerificationStatus::kFormatted); + ASCIIToUTF16("Apt. 10 Floor 2"), + VerificationStatus::kObserved); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_APT_NUM, ASCIIToUTF16("10"), VerificationStatus::kParsed); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_FLOOR, ASCIIToUTF16("2"), VerificationStatus::kParsed); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_PREMISE_NAME, ASCIIToUTF16("Premise"), @@ -133,10 +145,15 @@ AutofillProfileSpecifics ConstructCompleteSpecifics() { specifics.set_use_date(1423182152); // Set values and statuses for the names. - specifics.add_name_honorific(""); + specifics.add_name_honorific("Dr."); specifics.add_name_honorific_status( AutofillProfileSpecifics::VerificationStatus:: - AutofillProfileSpecifics_VerificationStatus_VERIFICATION_STATUS_UNSPECIFIED); + AutofillProfileSpecifics_VerificationStatus_OBSERVED); + + specifics.add_name_full_with_honorific("Dr. John K. Doe"); + specifics.add_name_full_with_honorific_status( + AutofillProfileSpecifics::VerificationStatus:: + AutofillProfileSpecifics_VerificationStatus_FORMATTED); specifics.add_name_first("John"); specifics.add_name_first_status( @@ -181,31 +198,38 @@ AutofillProfileSpecifics ConstructCompleteSpecifics() { // Set values and statuses for the address. // Address lines are derived from the home street address and do not have an // independent status. - specifics.set_address_home_line1("123 Fake St."); - specifics.set_address_home_line2("Apt. 42"); + specifics.set_address_home_line1("123 Fake St. Dep Premise"); + specifics.set_address_home_line2("Apt. 10 Floor 2"); specifics.set_address_home_street_address( - "123 Fake St.\n" - "Apt. 42"); + "123 Fake St. Dep Premise\n" + "Apt. 10 Floor 2"); specifics.set_address_home_street_address_status( sync_pb::AutofillProfileSpecifics_VerificationStatus:: AutofillProfileSpecifics_VerificationStatus_OBSERVED); - specifics.set_address_home_thoroughfare_name("Street Name"); + specifics.set_address_home_thoroughfare_name("Fake St."); specifics.set_address_home_thoroughfare_name_status( sync_pb::AutofillProfileSpecifics_VerificationStatus_FORMATTED); - specifics.set_address_home_dependent_thoroughfare_name( - "Dependent Street Name"); + specifics.set_address_home_dependent_thoroughfare_name("Dep"); specifics.set_address_home_dependent_thoroughfare_name_status( sync_pb::AutofillProfileSpecifics_VerificationStatus_FORMATTED); - specifics.set_address_home_thoroughfare_number("House Number"); + specifics.set_address_home_thoroughfare_number("123"); specifics.set_address_home_thoroughfare_number_status( sync_pb::AutofillProfileSpecifics_VerificationStatus_FORMATTED); - specifics.set_address_home_subpremise_name("Subpremise"); + specifics.set_address_home_subpremise_name("Apt. 10 Floor 2"); specifics.set_address_home_subpremise_name_status( - sync_pb::AutofillProfileSpecifics_VerificationStatus_FORMATTED); + sync_pb::AutofillProfileSpecifics_VerificationStatus_OBSERVED); + + specifics.set_address_home_apt_num("10"); + specifics.set_address_home_apt_num_status( + sync_pb::AutofillProfileSpecifics_VerificationStatus_PARSED); + + specifics.set_address_home_floor("2"); + specifics.set_address_home_floor_status( + sync_pb::AutofillProfileSpecifics_VerificationStatus_PARSED); specifics.set_address_home_premise_name("Premise"); specifics.set_address_home_premise_name_status( @@ -258,14 +282,15 @@ class AutofillProfileSyncUtilTest : public testing::Test { // the server. TEST_F(AutofillProfileSyncUtilTest, CreateEntityDataFromAutofillProfile) { base::test::ScopedFeatureList structured_names_feature; - // With those two features enabled, the AutofillProfile supports all tokens + // With those three features enabled, the AutofillProfile supports all tokens // and statuses assignable in the specifics. If one of those features is // disabled, for some tokens // AutofillProfile::GetRawInfo(AutofillProfile::SetRawInfo()) is not the // identify function. The same is true for the verification status. structured_names_feature.InitWithFeatures( {features::kAutofillEnableSupportForMoreStructureInAddresses, - features::kAutofillEnableSupportForMoreStructureInNames}, + features::kAutofillEnableSupportForMoreStructureInNames, + features::kAutofillEnableSupportForHonorificPrefixes}, {}); AutofillProfile profile = ConstructCompleteProfile(); diff --git a/chromium/components/autofill/core/browser/autofill_profile_validation_util.cc b/chromium/components/autofill/core/browser/autofill_profile_validation_util.cc index 14f084dc981..aabb458d126 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_validation_util.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_validation_util.cc @@ -8,8 +8,8 @@ #include <utility> #include "base/check.h" +#include "base/containers/contains.h" #include "base/i18n/case_conversion.h" -#include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/geo/address_i18n.h" #include "components/autofill/core/browser/geo/country_data.h" diff --git a/chromium/components/autofill/core/browser/autofill_provider.cc b/chromium/components/autofill/core/browser/autofill_provider.cc index 8cea5630a05..0c6c3c5f60e 100644 --- a/chromium/components/autofill/core/browser/autofill_provider.cc +++ b/chromium/components/autofill/core/browser/autofill_provider.cc @@ -7,6 +7,18 @@ #include "components/autofill/core/browser/autofill_handler_proxy.h" namespace autofill { +namespace { +bool g_is_download_manager_disabled_for_testing = false; +} + +// static +bool AutofillProvider::is_download_manager_disabled_for_testing() { + return g_is_download_manager_disabled_for_testing; +} + +void AutofillProvider::set_is_download_manager_disabled_for_testing() { + g_is_download_manager_disabled_for_testing = true; +} AutofillProvider::AutofillProvider() {} diff --git a/chromium/components/autofill/core/browser/autofill_provider.h b/chromium/components/autofill/core/browser/autofill_provider.h index 1d9b93d8535..4ea027b0d67 100644 --- a/chromium/components/autofill/core/browser/autofill_provider.h +++ b/chromium/components/autofill/core/browser/autofill_provider.h @@ -8,6 +8,7 @@ #include "base/time/time.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" +#include "components/autofill/core/common/signatures.h" namespace gfx { class RectF; @@ -24,6 +25,9 @@ class AutofillProvider { AutofillProvider(); virtual ~AutofillProvider(); + static bool is_download_manager_disabled_for_testing(); + static void set_is_download_manager_disabled_for_testing(); + virtual void OnQueryFormFieldAutofill(AutofillHandlerProxy* handler, int32_t id, const FormData& form, @@ -65,11 +69,15 @@ class AutofillProvider { base::TimeTicks timestamp) = 0; virtual void OnFormsSeen(AutofillHandlerProxy* handler, - const std::vector<FormData>& forms, - const base::TimeTicks timestamp) = 0; + const std::vector<FormData>& forms) = 0; virtual void OnHidePopup(AutofillHandlerProxy* handler) = 0; + virtual void OnServerPredictionsAvailable(AutofillHandlerProxy* handler) = 0; + + virtual void OnServerQueryRequestError(AutofillHandlerProxy* handler, + FormSignature form_signature) = 0; + virtual void Reset(AutofillHandlerProxy* handler) = 0; void SendFormDataToRenderer(AutofillHandlerProxy* handler, diff --git a/chromium/components/autofill/core/browser/autofill_provider_unittest.cc b/chromium/components/autofill/core/browser/autofill_provider_unittest.cc new file mode 100644 index 00000000000..567d391e3fb --- /dev/null +++ b/chromium/components/autofill/core/browser/autofill_provider_unittest.cc @@ -0,0 +1,92 @@ +// Copyright 2020 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 "components/autofill/core/browser/autofill_handler_proxy.h" +#include "components/autofill/core/browser/test_autofill_provider.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +class AutofillHandlerProxyTestHelper : public AutofillHandlerProxy { + public: + explicit AutofillHandlerProxyTestHelper(AutofillProvider* autofill_provider) + : AutofillHandlerProxy(nullptr, + nullptr, + autofill_provider, + DISABLE_AUTOFILL_DOWNLOAD_MANAGER) {} + + void SimulatePropagateAutofillPredictions() { + PropagateAutofillPredictions(nullptr, std::vector<FormStructure*>()); + } + + void SimulateOnQueryFormFieldAutofillImpl() { + OnQueryFormFieldAutofillImpl(0, FormData(), FormFieldData(), gfx::RectF(), + /*autoselect_first_suggestion=*/false); + } +}; + +class AutofillProviderTestHelper : public TestAutofillProvider { + public: + bool HasServerPrediction() const { return handler_->has_server_prediction(); } + + private: + // AutofillProvider + void OnQueryFormFieldAutofill(AutofillHandlerProxy* handler, + int32_t id, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + bool autoselect_first_suggestion) override { + handler_ = handler; + } + void OnServerQueryRequestError(AutofillHandlerProxy* handler, + FormSignature form_signature) override {} + + AutofillHandlerProxy* handler_; +}; + +class AutofillProviderTest : public testing::Test { + public: + void SetUp() override { + autofill_provider_test_helper_ = + std::make_unique<AutofillProviderTestHelper>(); + autofill_handler_proxy_test_helper_ = + std::make_unique<AutofillHandlerProxyTestHelper>( + autofill_provider_test_helper_.get()); + } + + AutofillProviderTestHelper* autofill_provider_test_helper() { + return autofill_provider_test_helper_.get(); + } + + AutofillHandlerProxyTestHelper* autofill_handler_proxy_test_helper() { + return autofill_handler_proxy_test_helper_.get(); + } + + private: + std::unique_ptr<AutofillProviderTestHelper> autofill_provider_test_helper_; + std::unique_ptr<AutofillHandlerProxyTestHelper> + autofill_handler_proxy_test_helper_; +}; + +TEST_F(AutofillProviderTest, HasServerPredictionAfterQuery) { + // Simulate the result arrives after starting autofill. + autofill_handler_proxy_test_helper()->SimulateOnQueryFormFieldAutofillImpl(); + EXPECT_FALSE(autofill_provider_test_helper()->HasServerPrediction()); + autofill_handler_proxy_test_helper()->SimulatePropagateAutofillPredictions(); + EXPECT_TRUE(autofill_provider_test_helper()->HasServerPrediction()); + autofill_handler_proxy_test_helper()->Reset(); + EXPECT_FALSE(autofill_provider_test_helper()->HasServerPrediction()); +} + +TEST_F(AutofillProviderTest, HasServerPredictionBeforeQuery) { + // Simulate the result arrives before starting autofill. + autofill_handler_proxy_test_helper()->SimulatePropagateAutofillPredictions(); + autofill_handler_proxy_test_helper()->SimulateOnQueryFormFieldAutofillImpl(); + EXPECT_TRUE(autofill_provider_test_helper()->HasServerPrediction()); + autofill_handler_proxy_test_helper()->Reset(); + EXPECT_FALSE(autofill_provider_test_helper()->HasServerPrediction()); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_regex_constants.cc b/chromium/components/autofill/core/browser/autofill_regex_constants.cc index 60997b8bb58..ae58f14698e 100644 --- a/chromium/components/autofill/core/browser/autofill_regex_constants.cc +++ b/chromium/components/autofill/core/browser/autofill_regex_constants.cc @@ -18,7 +18,11 @@ const char kRegionIgnoredRe[] = "province|region|other" "|provincia" // es "|bairro|suburb"; // pt-BR, pt-PT -const char kAddressNameIgnoredRe[] = "address.*nickname|address.*label"; +const char kAddressNameIgnoredRe[] = + "address.*nickname|address.*label" + "|adres ([İi]sim|başlığı|adı)" // tr + "|identificação do endereço" // pt-BR, pt-PT + "|(label|judul|nama) alamat"; // id const char kCompanyRe[] = "company|business|organization|organisation" "|(?<!con)firma|firmenname" // de-DE @@ -29,44 +33,61 @@ const char kCompanyRe[] = "|название.?компании" // ru "|单位|公司" // zh-CN "|شرکت" // fa - "|회사|직장"; // ko-KR + "|회사|직장" // ko-KR + "|(nama.?)?perusahaan"; // id const char kStreetNameRe[] = - "stra(ss|ß)e" // de - "|street" // en - "|улица|название.?улицы" // ru - "|rua|avenida" // pt-PT, pt-BR - "|((?<!do |de )endereço)"; // pt-BR + "stra(ss|ß)e" // de + "|street" // en + "|улица|название.?улицы" // ru + "|rua|avenida" // pt-PT, pt-BR + "|((?<!do |de )endereço)" // pt-BR + "|calle"; // es-MX const char kHouseNumberRe[] = "(house.?|street.?|^)number" // en "|(haus|^)nummer" // de "|^\\*?.?número(.?\\*?$| da residência)" // pt-BR, pt-PT - "|дом|номер.?дома"; // ru + "|дом|номер.?дома" // ru + "|exterior"; // es-MX +const char kApartmentNumberRe[] = + "apartment" // en + "|interior" // es-MX + "|n(u|ú)mero.*app?art(a|e)ment" // es,fr,it + "|Wohnung" // de + "|квартир"; // ru const char kAddressLine1Re[] = "^address$|address[_-]?line(one)?|address1|addr1|street" "|(?:shipping|billing)address$" - "|strasse|straße|hausnummer|housenumber" // de-DE - "|house.?name" // en-GB - "|direccion|dirección" // es - "|adresse" // fr-FR - "|indirizzo" // it-IT - "|^住所$|住所1" // ja-JP - "|morada|((?<!do |de )endereço)" // pt-BR, pt-PT - "|Адрес" // ru - "|地址" // zh-CN - "|(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)" // tr - "|^주소.?$|주소.?1"; // ko-KR + "|strasse|straße|hausnummer|housenumber" // de-DE + "|house.?name" // en-GB + "|direccion|dirección" // es + "|adresse" // fr-FR + "|indirizzo" // it-IT + "|^住所$|住所1" // ja-JP + "|morada|((?<!do |de )endereço)" // pt-BR, pt-PT + "|Адрес" // ru + "|地址" // zh-CN + "|(\\b|_)adres(?! tarifi)(\\b|_)" // tr + "|^주소.?$|주소.?1" // ko-KR + "|^alamat"; // id const char kAddressLine1LabelRe[] = "(^\\W*address)" "|(address\\W*$)" "|(?:shipping|billing|mailing|pick.?up|drop.?off|delivery|sender|postal|" "recipient|home|work|office|school|business|mail)[\\s\\-]+address" "|address\\s+(of|for|to|from)" - "|adresse" // fr-FR - "|indirizzo" // it-IT - "|住所" // ja-JP - "|地址" // zh-CN - "|(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)" // tr - "|주소"; // ko-KR + "|adresse" // fr-FR + "|indirizzo" // it-IT + "|住所" // ja-JP + "|地址" // zh-CN + "|(\\b|_)adres(?! tarifi)(\\b|_)" // tr + "|주소" // ko-KR + "|^alamat" // id + // Should contain street and any other address component, in any order + "|street.*(house|building|apartment|floor)" // en + "|(house|building|apartment|floor).*street" + "|(sokak|cadde).*(apartman|bina|daire|mahalle)" // tr + "|(apartman|bina|daire|mahalle).*(sokak|cadde)" + "|улиц.*(дом|корпус|квартир|этаж)|(дом|корпус|квартир|этаж).*улиц"; // ru const char kAddressLine2Re[] = "address[_-]?line(2|two)|address2|addr2|street|suite|unit" "|adresszusatz|ergänzende.?angaben" // de-DE @@ -98,7 +119,8 @@ const char kCountryRe[] = "|国家" // zh-CN "|국가|나라" // ko-KR "|(\\b|_)(ülke|ulce|ulke)(\\b|_)" // tr - "|کشور"; // fa + "|کشور" // fa + "|negara"; // id const char kCountryLocationRe[] = "location"; const char kZipCodeRe[] = "zip|postal|post.*code|pcode" @@ -115,10 +137,16 @@ const char kZipCodeRe[] = "|邮政编码|邮编" // zh-CN "|郵遞區號" // zh-TW "|(\\b|_)posta kodu(\\b|_)" // tr - "|우편.?번호"; // ko-KR + "|우편.?번호" // ko-KR + "|kode.?pos"; // id const char kZip4Re[] = "zip|^-$|post2" "|codpos2"; // pt-BR, pt-PT +const char kDependentLocalityRe[] = + "neighbo(u)?rhood" // en + "|bairro" // pt-BR, pt-PT + "|mahalle|köy" // tr + "|kecamatan"; // id const char kCityRe[] = "city|town" "|\\bort\\b|stadt" // de-DE @@ -128,7 +156,7 @@ const char kCityRe[] = "|localita" // it-IT "|市区町村" // ja-JP "|cidade|município" // pt-BR, pt-PT - "|Город|Населённый.?пункт" // ru + "|Город|Насел(е|ё)нный.?пункт" // ru "|市" // zh-CN "|分區" // zh-TW "|شهر" // fa @@ -136,7 +164,8 @@ const char kCityRe[] = "|ग्राम|गाँव" // hi for village "|നഗരം|ഗ്രാമം" // ml for town|village "|((\\b|_|\\*)([İii̇]l[cç]e(miz|niz)?)(\\b|_|\\*))" // tr - "|^시[^도·・]|시[·・]?군[·・]?구"; // ko-KR + "|^시[^도·・]|시[·・]?군[·・]?구" // ko-KR + "|kota|kabupaten"; // id const char kStateRe[] = "(?<!(united|hist|history).?)state|county|region|province" "|county|principality" // en-UK @@ -149,7 +178,8 @@ const char kStateRe[] = "|استان" // fa "|राज्य" // hi "|((\\b|_|\\*)(eyalet|[şs]ehir|[İii̇]l(imiz)?|kent)(\\b|_|\\*))" // tr - "|^시[·・]?도"; // ko-KR + "|^시[·・]?도" // ko-KR + "|provinci"; // id ///////////////////////////////////////////////////////////////////////////// // search_field.cc @@ -188,6 +218,7 @@ const char kNameOnCardRe[] = "|nome.*cart" // it-IT "|名前" // ja-JP "|Имя.*карты" // ru + "|nama.*kartu" // id "|信用卡开户名|开户名|持卡人姓名" // zh-CN "|持卡人姓名"; // zh-TW const char kNameOnCardContextualRe[] = "name"; @@ -196,6 +227,7 @@ const char kCardNumberRe[] = "|(?<!telefon|haus|person|fødsels)nummer" // de-DE, sv-SE, no "|カード番号" // ja-JP "|Номер.*карты" // ru + "|no.*kartu" // id "|信用卡号|信用卡号码" // zh-CN "|信用卡卡號" // zh-TW "|카드" // ko-KR @@ -223,14 +255,15 @@ const char kCardCvcRe[] = // Instead, we match only words beginning with "month". const char kExpirationMonthRe[] = "expir|exp.*mo|exp.*date|ccmonth|cardmonth|addmonth" - "|gueltig|gültig|monat" // de-DE - "|fecha" // es - "|date.*exp" // fr-FR - "|scadenza" // it-IT - "|有効期限" // ja-JP - "|validade" // pt-BR, pt-PT - "|Срок действия карты" // ru - "|月"; // zh-CN + "|gueltig|gültig|monat" // de-DE + "|fecha" // es + "|date.*exp" // fr-FR + "|scadenza" // it-IT + "|有効期限" // ja-JP + "|validade" // pt-BR, pt-PT + "|Срок действия карты" // ru + "|masa berlaku|berlaku hingga" // id + "|月"; // zh-CN const char kExpirationYearRe[] = "exp|^/|(add)?year" "|ablaufdatum|gueltig|gültig|jahr" // de-DE @@ -239,6 +272,7 @@ const char kExpirationYearRe[] = "|有効期限" // ja-JP "|validade" // pt-BR, pt-PT "|Срок действия карты" // ru + "|masa berlaku|berlaku hingga" // id "|年|有效期"; // zh-CN // Used to match a expiration date field with a two digit year. @@ -277,12 +311,12 @@ const char kDayRe[] = "day"; ///////////////////////////////////////////////////////////////////////////// const char kEmailRe[] = "e.?mail" - "|courriel" // fr - "|correo.*electr(o|ó)nico" // es-ES - "|メールアドレス" // ja-JP - "|Электронной.?Почты" // ru - "|邮件|邮箱" // zh-CN - "|電郵地址" // zh-TW + "|courriel" // fr + "|correo.*electr(o|ó)nico" // es-ES + "|メールアドレス" // ja-JP + "|Электронн(ая|ой).?Почт(а|ы)" // ru + "|邮件|邮箱" // zh-CN + "|電郵地址" // zh-TW "|ഇ-മെയില്|ഇലക്ട്രോണിക്.?" "മെയിൽ" // ml "|ایمیل|پست.*الکترونیک" // fa @@ -295,21 +329,22 @@ const char kEmailRe[] = ///////////////////////////////////////////////////////////////////////////// const char kNameIgnoredRe[] = "user.?name|user.?id|nickname|maiden name|title|prefix|suffix" - "|adres başlığınız" // tr "|vollständiger.?name" // de-DE "|用户名" // zh-CN "|(?:사용자.?)?아이디|사용자.?ID"; // ko-KR const char kNameRe[] = "^name|full.?name|your.?name|customer.?name|bill.?name|ship.?name" - "|name.*first.*last|firstandlastname" + "|name.*first.*last|firstandlastname|contact.?(name|person)" "|nombre.*y.*apellidos" // es - "|^nom(?!bre)" // fr-FR + "|^nom(?![a-zA-Z])" // fr-FR "|お名前|氏名" // ja-JP "|^nome" // pt-BR, pt-PT "|نام.*نام.*خانوادگی" // fa "|姓名" // zh-CN + "|контактное.?лицо" // ru "|(\\b|_|\\*)ad[ı]? soyad[ı]?(\\b|_|\\*)" // tr - "|성명"; // ko-KR + "|성명" // ko-KR + "|nama.?(lengkap|penerima|kamu)"; // id const char kNameSpecificRe[] = "^name" "|^nom" // fr-FR @@ -326,14 +361,15 @@ const char kFirstNameRe[] = "|이름" // ko-KR "|പേര്" // ml "|(\\b|_|\\*)(isim|ad|ad(i|ı|iniz|ınız)?)(\\b|_|\\*)" // tr - "|नाम"; // hi + "|नाम" // hi + "|nama depan"; // id const char kMiddleInitialRe[] = "middle.*initial|m\\.i\\.|mi$|\\bmi\\b"; const char kMiddleNameRe[] = "middle.*name|mname|middle$"; const char kLastNameRe[] = "last.*name|lname|surname(?!\\d)|last$|secondname|family.*name" "|nachname" // de-DE "|apellidos?" // es - "|famille|^nom(?!bre)" // fr-FR + "|famille|^nom(?![a-zA-Z])" // fr-FR "|cognome" // it-IT "|姓" // ja-JP "|apelidos|surename|sobrenome" // pt-BR, pt-PT @@ -342,7 +378,8 @@ const char kLastNameRe[] = "|उपनाम" // hi "|മറുപേര്" // ml "|(\\b|_|\\*)(soyisim|soyad(i|ı|iniz|ınız)?)(\\b|_|\\*)" // tr - "|\\b성(?:[^명]|\\b)"; // ko-KR + "|\\b성(?:[^명]|\\b)" // ko-KR + "|nama belakang"; // id const char kNameLastFirstRe[] = "(primer.*apellido)" // es "|(apellido1)" // es @@ -360,7 +397,7 @@ const char kHonorificPrefixRe[] = "|(salutation(?! and given name))" // en "|titolo" // it-IT "|titre" // fr-FR - "|обраще́ние|зва́ние" // ru + "|обращение|звание" // ru "|προσφώνηση" // el "|hitap"; // tr ///////////////////////////////////////////////////////////////////////////// @@ -368,17 +405,18 @@ const char kHonorificPrefixRe[] = ///////////////////////////////////////////////////////////////////////////// const char kPhoneRe[] = "phone|mobile|contact.?number" - "|telefonnummer" // de-DE - "|telefono|teléfono" // es - "|telfixe" // fr-FR - "|電話" // ja-JP - "|telefone|telemovel" // pt-BR, pt-PT - "|телефон" // ru - "|मोबाइल" // hi for mobile - "|(\\b|_|\\*)telefon(\\b|_|\\*)" // tr - "|电话" // zh-CN - "|മൊബൈല്" // ml for mobile - "|(?:전화|핸드폰|휴대폰|휴대전화)(?:.?번호)?"; // ko-KR + "|telefonnummer" // de-DE + "|telefono|teléfono" // es + "|telfixe" // fr-FR + "|電話" // ja-JP + "|telefone|telemovel" // pt-BR, pt-PT + "|телефон" // ru + "|मोबाइल" // hi for mobile + "|(\\b|_|\\*)telefon(\\b|_|\\*)" // tr + "|电话" // zh-CN + "|മൊബൈല്" // ml for mobile + "|(?:전화|핸드폰|휴대폰|휴대전화)(?:.?번호)?" // ko-KR + "|telepon|ponsel|(nomor|no\\.?).?(hp|handphone)"; // id const char kAugmentedPhoneCountryCodeRe[] = "^[^0-9+]*(?:\\+|00)\\s*([1-9]\\d{0,3})\\D*$"; const char kCountryCodeRe[] = diff --git a/chromium/components/autofill/core/browser/autofill_regex_constants.h b/chromium/components/autofill/core/browser/autofill_regex_constants.h index de9ef0ecba2..d755da5d990 100644 --- a/chromium/components/autofill/core/browser/autofill_regex_constants.h +++ b/chromium/components/autofill/core/browser/autofill_regex_constants.h @@ -12,6 +12,7 @@ extern const char kRegionIgnoredRe[]; extern const char kAddressNameIgnoredRe[]; extern const char kCompanyRe[]; extern const char kHouseNumberRe[]; +extern const char kApartmentNumberRe[]; extern const char kStreetNameRe[]; extern const char kAddressLine1Re[]; extern const char kAddressLine1LabelRe[]; @@ -20,9 +21,11 @@ extern const char kAddressLine2LabelRe[]; extern const char kAddressLinesExtraRe[]; extern const char kAddressLookupRe[]; extern const char kCountryRe[]; +extern const char kDependentLocality[]; extern const char kCountryLocationRe[]; extern const char kZipCodeRe[]; extern const char kZip4Re[]; +extern const char kDependentLocalityRe[]; extern const char kCityRe[]; extern const char kStateRe[]; extern const char kNameOnCardRe[]; diff --git a/chromium/components/autofill/core/browser/autofill_regexes.cc b/chromium/components/autofill/core/browser/autofill_regexes.cc index 4875a113f8b..fc387879ecf 100644 --- a/chromium/components/autofill/core/browser/autofill_regexes.cc +++ b/chromium/components/autofill/core/browser/autofill_regexes.cc @@ -4,8 +4,8 @@ #include "components/autofill/core/browser/autofill_regexes.h" +#include <map> #include <memory> -#include <unordered_map> #include <utility> #include "base/check.h" @@ -28,19 +28,20 @@ class AutofillRegexes { AutofillRegexes() = default; // Returns the compiled regex matcher corresponding to |pattern|. - icu::RegexMatcher* GetMatcher(const base::string16& pattern); + icu::RegexMatcher* GetMatcher(const base::StringPiece16& pattern); private: ~AutofillRegexes() = default; // Maps patterns to their corresponding regex matchers. - std::unordered_map<base::string16, std::unique_ptr<icu::RegexMatcher>> + std::map<base::string16, std::unique_ptr<icu::RegexMatcher>, std::less<>> matchers_; DISALLOW_COPY_AND_ASSIGN(AutofillRegexes); }; -icu::RegexMatcher* AutofillRegexes::GetMatcher(const base::string16& pattern) { +icu::RegexMatcher* AutofillRegexes::GetMatcher( + const base::StringPiece16& pattern) { auto it = matchers_.find(pattern); if (it == matchers_.end()) { const icu::UnicodeString icu_pattern(false, pattern.data(), @@ -62,8 +63,8 @@ icu::RegexMatcher* AutofillRegexes::GetMatcher(const base::string16& pattern) { namespace autofill { -bool MatchesPattern(const base::string16& input, - const base::string16& pattern, +bool MatchesPattern(const base::StringPiece16& input, + const base::StringPiece16& pattern, base::string16* match, int32_t group_to_be_captured) { static base::NoDestructor<AutofillRegexes> g_autofill_regexes; diff --git a/chromium/components/autofill/core/browser/autofill_regexes.h b/chromium/components/autofill/core/browser/autofill_regexes.h index 585e88e20b6..4ec3fce0f78 100644 --- a/chromium/components/autofill/core/browser/autofill_regexes.h +++ b/chromium/components/autofill/core/browser/autofill_regexes.h @@ -6,6 +6,7 @@ #define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEXES_H_ #include "base/strings/string16.h" +#include "base/strings/string_piece.h" // Parsing utilities. namespace autofill { @@ -13,8 +14,8 @@ namespace autofill { // Case-insensitive regular expression matching. // Returns true if |pattern| is found in |input|. // The |group_to_be_captured| numbered group is captured into |match|. -bool MatchesPattern(const base::string16& input, - const base::string16& pattern, +bool MatchesPattern(const base::StringPiece16& input, + const base::StringPiece16& pattern, base::string16* match = nullptr, int32_t group_to_be_captured = 0); diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.cc b/chromium/components/autofill/core/browser/autofill_test_utils.cc index 0b711b69c67..4853951bac6 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.cc +++ b/chromium/components/autofill/core/browser/autofill_test_utils.cc @@ -83,6 +83,37 @@ FieldRendererId MakeFieldRendererId() { } // namespace +void SetFormGroupValues(FormGroup& form_group, + const std::vector<FormGroupValue>& values) { + for (const auto& value : values) { + form_group.SetRawInfoWithVerificationStatus( + value.type, base::UTF8ToUTF16(value.value), value.verification_status); + } +} + +void VerifyFormGroupValues(const FormGroup& form_group, + const std::vector<FormGroupValue>& values, + bool ignore_status) { + for (const auto& value : values) { + SCOPED_TRACE(testing::Message() + << "Expected for type " + << AutofillType::ServerFieldTypeToString(value.type) << "\n\t" + << value.value << " with status " + << (ignore_status ? "(ignored)" : "") + << value.verification_status << "\nFound:" + << "\n\t" << form_group.GetRawInfo(value.type) + << " with status " + << form_group.GetVerificationStatus(value.type)); + + EXPECT_EQ(form_group.GetRawInfo(value.type), + base::UTF8ToUTF16(value.value)); + if (!ignore_status) { + EXPECT_EQ(form_group.GetVerificationStatus(value.type), + value.verification_status); + } + } +} + std::unique_ptr<PrefService> PrefServiceForTesting() { scoped_refptr<user_prefs::PrefRegistrySyncable> registry( new user_prefs::PrefRegistrySyncable()); @@ -176,9 +207,9 @@ void CreateTestAddressFormData(FormData* form, form->button_titles = { std::make_pair(ASCIIToUTF16("Submit"), mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; - form->url = GURL("http://myform.com/form.html"); - form->full_url = GURL("http://myform.com/form.html?foo=bar"); - form->action = GURL("http://myform.com/submit.html"); + form->url = GURL("https://myform.com/form.html"); + form->full_url = GURL("https://myform.com/form.html?foo=bar"); + form->action = GURL("https://myform.com/submit.html"); form->is_action_empty = true; form->main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); @@ -216,6 +247,10 @@ void CreateTestAddressFormData(FormData* form, form->fields.push_back(field); type_set.clear(); type_set.insert(ADDRESS_HOME_LINE2); + if (base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForMoreStructureInAddresses)) { + type_set.insert(ADDRESS_HOME_SUBPREMISE); + } types->push_back(type_set); test::CreateTestFormField("City", "city", "", "text", &field); form->fields.push_back(field); @@ -254,9 +289,9 @@ void CreateTestPersonalInformationFormData(FormData* form, form->unique_renderer_id = MakeFormRendererId(); form->name = ASCIIToUTF16("MyForm") + ASCIIToUTF16(unique_id ? unique_id : ""); - form->url = GURL("http://myform.com/form.html"); - form->full_url = GURL("http://myform.com/form.html?foo=bar"); - form->action = GURL("http://myform.com/submit.html"); + form->url = GURL("https://myform.com/form.html"); + form->full_url = GURL("https://myform.com/form.html?foo=bar"); + form->action = GURL("https://myform.com/submit.html"); form->main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); @@ -281,7 +316,7 @@ void CreateTestCreditCardFormData(FormData* form, ASCIIToUTF16("MyForm") + ASCIIToUTF16(unique_id ? unique_id : ""); if (is_https) { form->url = GURL("https://myform.com/form.html"); - form->full_url = GURL("http://myform.com/form.html?foo=bar"); + form->full_url = GURL("https://myform.com/form.html?foo=bar"); form->action = GURL("https://myform.com/submit.html"); form->main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); @@ -324,13 +359,15 @@ void CreateTestCreditCardFormData(FormData* form, form->fields.push_back(field); } -inline void check_and_set(FormGroup* profile, - ServerFieldType type, - const char* value) { +inline void check_and_set( + FormGroup* profile, + ServerFieldType type, + const char* value, + structured_address::VerificationStatus status = + structured_address::VerificationStatus::kObserved) { if (value) { - profile->SetRawInfoWithVerificationStatus( - type, base::UTF8ToUTF16(value), - structured_address::VerificationStatus::kObserved); + profile->SetRawInfoWithVerificationStatus(type, base::UTF8ToUTF16(value), + status); } } @@ -619,20 +656,22 @@ void SetProfileInfo(AutofillProfile* profile, const char* zipcode, const char* country, const char* phone, - bool finalize) { - check_and_set(profile, NAME_FIRST, first_name); - check_and_set(profile, NAME_MIDDLE, middle_name); - check_and_set(profile, NAME_LAST, last_name); - check_and_set(profile, EMAIL_ADDRESS, email); - check_and_set(profile, COMPANY_NAME, company); - check_and_set(profile, ADDRESS_HOME_LINE1, address1); - check_and_set(profile, ADDRESS_HOME_LINE2, address2); - check_and_set(profile, ADDRESS_HOME_DEPENDENT_LOCALITY, dependent_locality); - check_and_set(profile, ADDRESS_HOME_CITY, city); - check_and_set(profile, ADDRESS_HOME_STATE, state); - check_and_set(profile, ADDRESS_HOME_ZIP, zipcode); - check_and_set(profile, ADDRESS_HOME_COUNTRY, country); - check_and_set(profile, PHONE_HOME_WHOLE_NUMBER, phone); + bool finalize, + structured_address::VerificationStatus status) { + check_and_set(profile, NAME_FIRST, first_name, status); + check_and_set(profile, NAME_MIDDLE, middle_name, status); + check_and_set(profile, NAME_LAST, last_name, status); + check_and_set(profile, EMAIL_ADDRESS, email, status); + check_and_set(profile, COMPANY_NAME, company, status); + check_and_set(profile, ADDRESS_HOME_LINE1, address1, status); + check_and_set(profile, ADDRESS_HOME_LINE2, address2, status); + check_and_set(profile, ADDRESS_HOME_DEPENDENT_LOCALITY, dependent_locality, + status); + check_and_set(profile, ADDRESS_HOME_CITY, city, status); + check_and_set(profile, ADDRESS_HOME_STATE, state, status); + check_and_set(profile, ADDRESS_HOME_ZIP, zipcode, status); + check_and_set(profile, ADDRESS_HOME_COUNTRY, country, status); + check_and_set(profile, PHONE_HOME_WHOLE_NUMBER, phone, status); if (finalize) profile->FinalizeAfterImport(); } @@ -650,19 +689,20 @@ void SetProfileInfo(AutofillProfile* profile, const char* zipcode, const char* country, const char* phone, - bool finalize) { - check_and_set(profile, NAME_FIRST, first_name); - check_and_set(profile, NAME_MIDDLE, middle_name); - check_and_set(profile, NAME_LAST, last_name); - check_and_set(profile, EMAIL_ADDRESS, email); - check_and_set(profile, COMPANY_NAME, company); - check_and_set(profile, ADDRESS_HOME_LINE1, address1); - check_and_set(profile, ADDRESS_HOME_LINE2, address2); - check_and_set(profile, ADDRESS_HOME_CITY, city); - check_and_set(profile, ADDRESS_HOME_STATE, state); - check_and_set(profile, ADDRESS_HOME_ZIP, zipcode); - check_and_set(profile, ADDRESS_HOME_COUNTRY, country); - check_and_set(profile, PHONE_HOME_WHOLE_NUMBER, phone); + bool finalize, + structured_address::VerificationStatus status) { + check_and_set(profile, NAME_FIRST, first_name, status); + check_and_set(profile, NAME_MIDDLE, middle_name, status); + check_and_set(profile, NAME_LAST, last_name, status); + check_and_set(profile, EMAIL_ADDRESS, email, status); + check_and_set(profile, COMPANY_NAME, company, status); + check_and_set(profile, ADDRESS_HOME_LINE1, address1, status); + check_and_set(profile, ADDRESS_HOME_LINE2, address2, status); + check_and_set(profile, ADDRESS_HOME_CITY, city, status); + check_and_set(profile, ADDRESS_HOME_STATE, state, status); + check_and_set(profile, ADDRESS_HOME_ZIP, zipcode, status); + check_and_set(profile, ADDRESS_HOME_COUNTRY, country, status); + check_and_set(profile, PHONE_HOME_WHOLE_NUMBER, phone, status); if (finalize) profile->FinalizeAfterImport(); } @@ -681,12 +721,13 @@ void SetProfileInfoWithGuid(AutofillProfile* profile, const char* zipcode, const char* country, const char* phone, - bool finalize) { + bool finalize, + structured_address::VerificationStatus status) { if (guid) profile->set_guid(guid); SetProfileInfo(profile, first_name, middle_name, last_name, email, company, address1, address2, city, state, zipcode, country, phone, - finalize); + finalize, status); } void SetCreditCardInfo(CreditCard* credit_card, @@ -830,28 +871,6 @@ void FillUploadField(AutofillUploadContents::Field* field, type_validities->add_validity(validity_states[i]); } -void FillQueryField(AutofillQueryContents::Form::Field* field, - unsigned signature, - const char* name, - const char* control_type) { - field->set_signature(signature); - if (name) - field->set_name(name); - if (control_type) - field->set_type(control_type); -} - -void FillQueryField(AutofillPageQueryRequest_Form_Field* field, - unsigned signature, - const char* name, - const char* control_type) { - field->set_signature(signature); - if (name) - field->set_name(name); - if (control_type) - field->set_control_type(control_type); -} - void GenerateTestAutofillPopup( AutofillExternalDelegate* autofill_external_delegate) { int query_id = 1; @@ -875,26 +894,22 @@ std::string ObfuscatedCardDigitsAsUTF8(const std::string& str) { std::string NextMonth() { base::Time::Exploded now; - // Using AutofillClock here might cause test flakiness. See crbug/1108232. - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); return base::StringPrintf("%02d", now.month % 12 + 1); } std::string LastYear() { base::Time::Exploded now; - // Using AutofillClock here might cause test flakiness. See crbug/1108232. - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); return base::NumberToString(now.year - 1); } std::string NextYear() { base::Time::Exploded now; - // Using AutofillClock here might cause test flakiness. See crbug/1108232. - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); return base::NumberToString(now.year + 1); } std::string TenYearsFromNow() { base::Time::Exploded now; - // Using AutofillClock here might cause test flakiness. See crbug/1108232. - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); return base::NumberToString(now.year + 10); } @@ -912,5 +927,42 @@ std::vector<FormSignature> GetEncodedSignatures( return all_signatures; } +void AddFieldSuggestionToForm( + const autofill::FormFieldData& field_data, + ServerFieldType field_type, + ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion) { + auto* field_suggestion = form_suggestion->add_field_suggestions(); + field_suggestion->set_field_signature( + CalculateFieldSignatureForField(field_data).value()); + field_suggestion->set_primary_type_prediction(field_type); +} + +void AddFieldPredictionsToForm( + const autofill::FormFieldData& field_data, + const std::vector<int>& field_types, + ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion) { + std::vector<ServerFieldType> types; + for (auto type : field_types) { + types.emplace_back(static_cast<ServerFieldType>(type)); + } + AddFieldPredictionsToForm(field_data, types, form_suggestion); +} + +void AddFieldPredictionsToForm( + const autofill::FormFieldData& field_data, + const std::vector<ServerFieldType>& field_types, + ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion) { + // According to api_v1.proto, the first element is always set to primary type. + auto* field_suggestion = form_suggestion->add_field_suggestions(); + field_suggestion->set_field_signature( + CalculateFieldSignatureForField(field_data).value()); + field_suggestion->set_primary_type_prediction(*field_types.begin()); + for (auto field_type : field_types) { + AutofillQueryResponse_FormSuggestion_FieldSuggestion_FieldPrediction* + prediction = field_suggestion->add_predictions(); + prediction->set_type(field_type); + } +} + } // namespace test } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.h b/chromium/components/autofill/core/browser/autofill_test_utils.h index fc1d97d580a..3944ae1c830 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.h +++ b/chromium/components/autofill/core/browser/autofill_test_utils.h @@ -56,6 +56,29 @@ inline bool operator!=(const FormDataPredictions& a, // Common utilities shared amongst Autofill tests. namespace test { +// A compound data type that contains the type, the value and the verification +// status for a form group entry (an AutofillProfile). +struct FormGroupValue { + ServerFieldType type; + std::string value; + structured_address::VerificationStatus verification_status = + structured_address::VerificationStatus::kNoStatus; +}; + +// Convenience declaration for multiple FormGroup values. +using FormGroupValues = std::vector<FormGroupValue>; + +// Helper function to set values and verification statuses to a form group. +void SetFormGroupValues(FormGroup& form_group, + const std::vector<FormGroupValue>& values); + +// Helper function to verify the expectation of values and verification +// statuses in a form group. If |ignore_status| is set, status checking is +// omitted. +void VerifyFormGroupValues(const FormGroup& form_group, + const std::vector<FormGroupValue>& values, + bool ignore_status = false); + const char kEmptyOrigin[] = ""; // The following methods return a PrefService that can be used for @@ -207,7 +230,9 @@ void SetProfileInfo(AutofillProfile* profile, const char* zipcode, const char* country, const char* phone, - bool finalize = true); + bool finalize = true, + structured_address::VerificationStatus = + structured_address::VerificationStatus::kObserved); // This one doesn't require the |dependent_locality|. void SetProfileInfo(AutofillProfile* profile, @@ -223,23 +248,28 @@ void SetProfileInfo(AutofillProfile* profile, const char* zipcode, const char* country, const char* phone, - bool finalize = true); - -void SetProfileInfoWithGuid(AutofillProfile* profile, - const char* guid, - const char* first_name, - const char* middle_name, - const char* last_name, - const char* email, - const char* company, - const char* address1, - const char* address2, - const char* city, - const char* state, - const char* zipcode, - const char* country, - const char* phone, - bool finalize = true); + bool finalize = true, + structured_address::VerificationStatus = + structured_address::VerificationStatus::kObserved); + +void SetProfileInfoWithGuid( + AutofillProfile* profile, + const char* guid, + const char* first_name, + const char* middle_name, + const char* last_name, + const char* email, + const char* company, + const char* address1, + const char* address2, + const char* city, + const char* state, + const char* zipcode, + const char* country, + const char* phone, + bool finalize = true, + structured_address::VerificationStatus = + structured_address::VerificationStatus::kObserved); // A unit testing utility that is common to a number of the Autofill unit // tests. |SetCreditCardInfo| provides a quick way to populate a credit card @@ -302,18 +332,6 @@ void FillUploadField(AutofillUploadContents::Field* field, unsigned autofill_type, const std::vector<unsigned>& validity_states); -// Fills the query form |field| with the information passed by parameter. If the -// value of a const char* parameter is NULL, the corresponding attribute won't -// be set at all, as opposed to being set to empty string. -void FillQueryField(AutofillQueryContents::Form::Field* field, - unsigned signature, - const char* name, - const char* control_type); -void FillQueryField(AutofillPageQueryRequest_Form_Field* field, - unsigned signature, - const char* name, - const char* control_type); - // Creates the structure of signatures that would be encoded by // FormStructure::EncodeUploadRequest() and FormStructure::EncodeQueryRequest() // and consumed by FormStructure::ParseQueryResponse() and @@ -337,6 +355,23 @@ std::string LastYear(); std::string NextYear(); std::string TenYearsFromNow(); +void AddFieldSuggestionToForm( + const autofill::FormFieldData& field_data, + ServerFieldType field_type, + ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion); + +// Adds |field_types| predictions of |field_data| to |form_suggestion| query +// response. Assumes int type can be cast to ServerFieldType. +void AddFieldPredictionsToForm( + const autofill::FormFieldData& field_data, + const std::vector<int>& field_types, + ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion); + +void AddFieldPredictionsToForm( + const autofill::FormFieldData& field_data, + const std::vector<ServerFieldType>& field_types, + ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion); + } // namespace test } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_type.cc b/chromium/components/autofill/core/browser/autofill_type.cc index 2d0f844fef1..83ceb74943e 100644 --- a/chromium/components/autofill/core/browser/autofill_type.cc +++ b/chromium/components/autofill/core/browser/autofill_type.cc @@ -21,7 +21,8 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case NAME_MIDDLE_INITIAL: case NAME_FULL: case NAME_SUFFIX: - return NAME; + case NAME_FULL_WITH_HONORIFIC_PREFIX: + return FieldTypeGroup::kName; case NAME_BILLING_FIRST: case NAME_BILLING_MIDDLE: @@ -29,11 +30,11 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case NAME_BILLING_MIDDLE_INITIAL: case NAME_BILLING_FULL: case NAME_BILLING_SUFFIX: - return NAME_BILLING; + return FieldTypeGroup::kNameBilling; case EMAIL_ADDRESS: case USERNAME_AND_EMAIL_ADDRESS: - return EMAIL; + return FieldTypeGroup::kEmail; case PHONE_HOME_NUMBER: case PHONE_HOME_CITY_CODE: @@ -41,14 +42,14 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case PHONE_HOME_CITY_AND_NUMBER: case PHONE_HOME_WHOLE_NUMBER: case PHONE_HOME_EXTENSION: - return PHONE_HOME; + return FieldTypeGroup::kPhoneHome; case PHONE_BILLING_NUMBER: case PHONE_BILLING_CITY_CODE: case PHONE_BILLING_COUNTRY_CODE: case PHONE_BILLING_CITY_AND_NUMBER: case PHONE_BILLING_WHOLE_NUMBER: - return PHONE_BILLING; + return FieldTypeGroup::kPhoneBilling; case ADDRESS_HOME_LINE1: case ADDRESS_HOME_LINE2: @@ -71,7 +72,7 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case ADDRESS_HOME_ADDRESS: case ADDRESS_HOME_ADDRESS_WITH_NAME: case ADDRESS_HOME_FLOOR: - return ADDRESS_HOME; + return FieldTypeGroup::kAddressHome; case ADDRESS_BILLING_LINE1: case ADDRESS_BILLING_LINE2: @@ -84,7 +85,7 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case ADDRESS_BILLING_STREET_ADDRESS: case ADDRESS_BILLING_SORTING_CODE: case ADDRESS_BILLING_DEPENDENT_LOCALITY: - return ADDRESS_BILLING; + return FieldTypeGroup::kAddressBilling; case CREDIT_CARD_NAME_FULL: case CREDIT_CARD_NAME_FIRST: @@ -97,10 +98,10 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: case CREDIT_CARD_TYPE: case CREDIT_CARD_VERIFICATION_CODE: - return CREDIT_CARD; + return FieldTypeGroup::kCreditCard; case COMPANY_NAME: - return COMPANY; + return FieldTypeGroup::kCompany; case PASSWORD: case ACCOUNT_CREATION_PASSWORD: @@ -112,7 +113,7 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case NOT_PASSWORD: case SINGLE_USERNAME: case NOT_USERNAME: - return PASSWORD_FIELD; + return FieldTypeGroup::kPasswordField; case NO_SERVER_DATA: case EMPTY_TYPE: @@ -126,24 +127,24 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case MERCHANT_EMAIL_SIGNUP: case MERCHANT_PROMO_CODE: case UPI_VPA: - return NO_GROUP; + return FieldTypeGroup::kNoGroup; case MAX_VALID_FIELD_TYPE: NOTREACHED(); - return NO_GROUP; + return FieldTypeGroup::kNoGroup; case USERNAME: - return USERNAME_FIELD; + return FieldTypeGroup::kUsernameField; case PRICE: case SEARCH_TERM: - return UNFILLABLE; + return FieldTypeGroup::kUnfillable; case UNKNOWN_TYPE: - return NO_GROUP; + return FieldTypeGroup::kNoGroup; } NOTREACHED(); - return NO_GROUP; + return FieldTypeGroup::kNoGroup; } FieldTypeGroup GroupTypeOfHtmlFieldType(HtmlFieldType field_type, @@ -155,10 +156,11 @@ FieldTypeGroup GroupTypeOfHtmlFieldType(HtmlFieldType field_type, case HTML_TYPE_ADDITIONAL_NAME: case HTML_TYPE_ADDITIONAL_NAME_INITIAL: case HTML_TYPE_FAMILY_NAME: - return field_mode == HTML_MODE_BILLING ? NAME_BILLING : NAME; + return field_mode == HTML_MODE_BILLING ? FieldTypeGroup::kNameBilling + : FieldTypeGroup::kName; case HTML_TYPE_ORGANIZATION: - return COMPANY; + return FieldTypeGroup::kCompany; case HTML_TYPE_STREET_ADDRESS: case HTML_TYPE_ADDRESS_LINE1: @@ -171,7 +173,8 @@ FieldTypeGroup GroupTypeOfHtmlFieldType(HtmlFieldType field_type, case HTML_TYPE_COUNTRY_NAME: case HTML_TYPE_POSTAL_CODE: case HTML_TYPE_FULL_ADDRESS: - return field_mode == HTML_MODE_BILLING ? ADDRESS_BILLING : ADDRESS_HOME; + return field_mode == HTML_MODE_BILLING ? FieldTypeGroup::kAddressBilling + : FieldTypeGroup::kAddressHome; case HTML_TYPE_CREDIT_CARD_NAME_FULL: case HTML_TYPE_CREDIT_CARD_NAME_FIRST: @@ -186,11 +189,11 @@ FieldTypeGroup GroupTypeOfHtmlFieldType(HtmlFieldType field_type, case HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR: case HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE: case HTML_TYPE_CREDIT_CARD_TYPE: - return CREDIT_CARD; + return FieldTypeGroup::kCreditCard; case HTML_TYPE_TRANSACTION_AMOUNT: case HTML_TYPE_TRANSACTION_CURRENCY: - return TRANSACTION; + return FieldTypeGroup::kTransaction; case HTML_TYPE_TEL: case HTML_TYPE_TEL_COUNTRY_CODE: @@ -200,24 +203,25 @@ FieldTypeGroup GroupTypeOfHtmlFieldType(HtmlFieldType field_type, case HTML_TYPE_TEL_LOCAL_PREFIX: case HTML_TYPE_TEL_LOCAL_SUFFIX: case HTML_TYPE_TEL_EXTENSION: - return field_mode == HTML_MODE_BILLING ? PHONE_BILLING : PHONE_HOME; + return field_mode == HTML_MODE_BILLING ? FieldTypeGroup::kPhoneBilling + : FieldTypeGroup::kPhoneHome; case HTML_TYPE_EMAIL: - return EMAIL; + return FieldTypeGroup::kEmail; case HTML_TYPE_UPI_VPA: // TODO(crbug/702223): Add support for UPI-VPA. - return NO_GROUP; + return FieldTypeGroup::kNoGroup; case HTML_TYPE_ONE_TIME_CODE: - return NO_GROUP; + return FieldTypeGroup::kNoGroup; case HTML_TYPE_UNSPECIFIED: case HTML_TYPE_UNRECOGNIZED: - return NO_GROUP; + return FieldTypeGroup::kNoGroup; } NOTREACHED(); - return NO_GROUP; + return FieldTypeGroup::kNoGroup; } AutofillType::AutofillType(ServerFieldType field_type) @@ -236,7 +240,7 @@ AutofillType::AutofillType(HtmlFieldType field_type, HtmlFieldMode mode) : server_type_(UNKNOWN_TYPE), html_type_(field_type), html_mode_(mode) {} FieldTypeGroup AutofillType::group() const { - FieldTypeGroup result = NO_GROUP; + FieldTypeGroup result = FieldTypeGroup::kNoGroup; if (server_type_ != UNKNOWN_TYPE) { result = GroupTypeOfServerFieldType(server_type_); } else { diff --git a/chromium/components/autofill/core/browser/autofill_type_unittest.cc b/chromium/components/autofill/core/browser/autofill_type_unittest.cc index 3fc28cc091b..9c4a5bb3a9d 100644 --- a/chromium/components/autofill/core/browser/autofill_type_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_type_unittest.cc @@ -12,79 +12,79 @@ TEST(AutofillTypeTest, ServerFieldTypes) { // No server data. AutofillType none(NO_SERVER_DATA); EXPECT_EQ(NO_SERVER_DATA, none.GetStorableType()); - EXPECT_EQ(NO_GROUP, none.group()); + EXPECT_EQ(FieldTypeGroup::kNoGroup, none.group()); // Unknown type. AutofillType unknown(UNKNOWN_TYPE); EXPECT_EQ(UNKNOWN_TYPE, unknown.GetStorableType()); - EXPECT_EQ(NO_GROUP, unknown.group()); + EXPECT_EQ(FieldTypeGroup::kNoGroup, unknown.group()); // Type with group but no subgroup. AutofillType first(NAME_FIRST); EXPECT_EQ(NAME_FIRST, first.GetStorableType()); - EXPECT_EQ(NAME, first.group()); + EXPECT_EQ(FieldTypeGroup::kName, first.group()); // Type with group and subgroup. AutofillType phone(PHONE_HOME_NUMBER); EXPECT_EQ(PHONE_HOME_NUMBER, phone.GetStorableType()); - EXPECT_EQ(PHONE_HOME, phone.group()); + EXPECT_EQ(FieldTypeGroup::kPhoneHome, phone.group()); // Billing type. AutofillType billing_address(ADDRESS_BILLING_LINE1); EXPECT_EQ(ADDRESS_HOME_LINE1, billing_address.GetStorableType()); - EXPECT_EQ(ADDRESS_BILLING, billing_address.group()); + EXPECT_EQ(FieldTypeGroup::kAddressBilling, billing_address.group()); // Last value, to check any offset errors. AutofillType last(NAME_BILLING_SUFFIX); EXPECT_EQ(NAME_SUFFIX, last.GetStorableType()); - EXPECT_EQ(NAME_BILLING, last.group()); + EXPECT_EQ(FieldTypeGroup::kNameBilling, last.group()); // Boundary (error) condition. AutofillType boundary(MAX_VALID_FIELD_TYPE); EXPECT_EQ(UNKNOWN_TYPE, boundary.GetStorableType()); - EXPECT_EQ(NO_GROUP, boundary.group()); + EXPECT_EQ(FieldTypeGroup::kNoGroup, boundary.group()); // Beyond the boundary (error) condition. AutofillType beyond(static_cast<ServerFieldType>(MAX_VALID_FIELD_TYPE + 10)); EXPECT_EQ(UNKNOWN_TYPE, beyond.GetStorableType()); - EXPECT_EQ(NO_GROUP, beyond.group()); + EXPECT_EQ(FieldTypeGroup::kNoGroup, beyond.group()); // In-between value. Missing from enum but within range. Error condition. AutofillType between(static_cast<ServerFieldType>(16)); EXPECT_EQ(UNKNOWN_TYPE, between.GetStorableType()); - EXPECT_EQ(NO_GROUP, between.group()); + EXPECT_EQ(FieldTypeGroup::kNoGroup, between.group()); } TEST(AutofillTypeTest, HtmlFieldTypes) { // Unknown type. AutofillType unknown(HTML_TYPE_UNSPECIFIED, HTML_MODE_NONE); EXPECT_EQ(UNKNOWN_TYPE, unknown.GetStorableType()); - EXPECT_EQ(NO_GROUP, unknown.group()); + EXPECT_EQ(FieldTypeGroup::kNoGroup, unknown.group()); // Type with group but no subgroup. AutofillType first(HTML_TYPE_GIVEN_NAME, HTML_MODE_NONE); EXPECT_EQ(NAME_FIRST, first.GetStorableType()); - EXPECT_EQ(NAME, first.group()); + EXPECT_EQ(FieldTypeGroup::kName, first.group()); // Type with group and subgroup. AutofillType phone(HTML_TYPE_TEL, HTML_MODE_NONE); EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER, phone.GetStorableType()); - EXPECT_EQ(PHONE_HOME, phone.group()); + EXPECT_EQ(FieldTypeGroup::kPhoneHome, phone.group()); // Last value, to check any offset errors. AutofillType last(HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, HTML_MODE_NONE); EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, last.GetStorableType()); - EXPECT_EQ(CREDIT_CARD, last.group()); + EXPECT_EQ(FieldTypeGroup::kCreditCard, last.group()); // Shipping mode. AutofillType shipping_first(HTML_TYPE_GIVEN_NAME, HTML_MODE_SHIPPING); EXPECT_EQ(NAME_FIRST, shipping_first.GetStorableType()); - EXPECT_EQ(NAME, shipping_first.group()); + EXPECT_EQ(FieldTypeGroup::kName, shipping_first.group()); // Billing mode. AutofillType billing_first(HTML_TYPE_GIVEN_NAME, HTML_MODE_BILLING); EXPECT_EQ(NAME_FIRST, billing_first.GetStorableType()); - EXPECT_EQ(NAME_BILLING, billing_first.group()); + EXPECT_EQ(FieldTypeGroup::kNameBilling, billing_first.group()); } } // namespace diff --git a/chromium/components/autofill/core/browser/data_model/address.cc b/chromium/components/autofill/core/browser/data_model/address.cc index 284283981ee..9fe03c9a1c1 100644 --- a/chromium/components/autofill/core/browser/data_model/address.cc +++ b/chromium/components/autofill/core/browser/data_model/address.cc @@ -8,10 +8,10 @@ #include <algorithm> #include "base/check_op.h" +#include "base/containers/contains.h" #include "base/feature_list.h" #include "base/i18n/case_conversion.h" #include "base/notreached.h" -#include "base/stl_util.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -45,14 +45,30 @@ bool Address::operator==(const Address& other) const { // TODO(crbug.com/1130194): Clean legacy implementation once structured // addresses are fully launched. if (structured_address::StructuredAddressesEnabled()) { - return structured_address_ == other.structured_address_; + return structured_address_.SameAs(other.structured_address_); + } + + bool are_states_equal = (state_ == other.state_); + if (base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap) && + !are_states_equal) { + // If the canonical state name exists for |state_| and |other.state_|, they + // are compared otherwise. + base::Optional<AlternativeStateNameMap::CanonicalStateName> + canonical_state_name_cur = GetCanonicalizedStateName(); + base::Optional<AlternativeStateNameMap::CanonicalStateName> + canonical_state_name_other = other.GetCanonicalizedStateName(); + if (canonical_state_name_cur && canonical_state_name_other) { + are_states_equal = + (canonical_state_name_cur == canonical_state_name_other); + } } return street_address_ == other.street_address_ && dependent_locality_ == other.dependent_locality_ && - city_ == other.city_ && state_ == other.state_ && - zip_code_ == other.zip_code_ && sorting_code_ == other.sorting_code_ && - country_code_ == other.country_code_ && + city_ == other.city_ && zip_code_ == other.zip_code_ && + sorting_code_ == other.sorting_code_ && + country_code_ == other.country_code_ && are_states_equal && street_name_ == other.street_name_ && dependent_street_name_ == other.dependent_street_name_ && house_number_ == other.house_number_ && @@ -86,6 +102,13 @@ bool Address::MergeStructuredAddress(const Address& newer, newer_was_more_recently_used); } +base::Optional<AlternativeStateNameMap::CanonicalStateName> +Address::GetCanonicalizedStateName() const { + return AlternativeStateNameMap::GetCanonicalStateName( + base::UTF16ToUTF8(GetRawInfo(ADDRESS_HOME_COUNTRY)), + GetRawInfo(ADDRESS_HOME_STATE)); +} + bool Address::IsStructuredAddressMergeable(const Address& newer) const { return structured_address_.IsMergeableWithComponent( newer.GetStructuredAddress()); @@ -96,7 +119,7 @@ const structured_address::Address& Address::GetStructuredAddress() const { } base::string16 Address::GetRawInfo(ServerFieldType type) const { - DCHECK_EQ(ADDRESS_HOME, AutofillType(type).group()); + DCHECK_EQ(FieldTypeGroup::kAddressHome, AutofillType(type).group()); // For structured addresses, the value can be directly retrieved. if (structured_address::StructuredAddressesEnabled()) @@ -136,6 +159,9 @@ base::string16 Address::GetRawInfo(ServerFieldType type) const { case ADDRESS_HOME_APT_NUM: return base::string16(); + case ADDRESS_HOME_FLOOR: + return base::string16(); + // The following tokens are used for creating new type votes but should not // be filled into fields. case ADDRESS_HOME_STREET_NAME: @@ -153,6 +179,10 @@ base::string16 Address::GetRawInfo(ServerFieldType type) const { case ADDRESS_HOME_SUBPREMISE: return subpremise_; + case ADDRESS_HOME_ADDRESS: + case ADDRESS_HOME_ADDRESS_WITH_NAME: + return base::string16(); + default: NOTREACHED() << "Unrecognized type: " << type; return base::string16(); @@ -162,7 +192,7 @@ base::string16 Address::GetRawInfo(ServerFieldType type) const { void Address::SetRawInfoWithVerificationStatus(ServerFieldType type, const base::string16& value, VerificationStatus status) { - DCHECK_EQ(ADDRESS_HOME, AutofillType(type).group()); + DCHECK_EQ(FieldTypeGroup::kAddressHome, AutofillType(type).group()); // For structured addresses, the value can directly be set. // TODO(crbug.com/1130194): Clean legacy implementation once structured @@ -278,6 +308,17 @@ void Address::SetRawInfoWithVerificationStatus(ServerFieldType type, subpremise_ = value; break; + // Not implemented for unstructured addresses. + case ADDRESS_HOME_APT_NUM: + break; + + // Not implemented for unstructured addresses. + case ADDRESS_HOME_FLOOR: + break; + + case ADDRESS_HOME_ADDRESS: + break; + default: NOTREACHED(); } @@ -313,6 +354,7 @@ void Address::GetMatchingTypes(const base::string16& text, if (!entered_country_code.empty() && country_code == entered_country_code) matching_types->insert(ADDRESS_HOME_COUNTRY); + l10n::CaseInsensitiveCompare compare; AutofillProfileComparator comparator(app_locale); // Check to see if the |text| could be the full name or abbreviation of a // state. @@ -321,8 +363,8 @@ void Address::GetMatchingTypes(const base::string16& text, base::string16 state_abbreviation; state_names::GetNameAndAbbreviation(canon_text, &state_name, &state_abbreviation); + if (!state_name.empty() || !state_abbreviation.empty()) { - l10n::CaseInsensitiveCompare compare; base::string16 canon_profile_state = comparator.NormalizeForComparison( GetInfo(AutofillType(ADDRESS_HOME_STATE), app_locale)); if ((!state_name.empty() && @@ -434,7 +476,7 @@ bool Address::SetInfoWithVerificationStatusImpl(const AutofillType& type, } else { country_code_ = country_code; } - return !country_code_.empty(); + return !GetRawInfo(ADDRESS_HOME_COUNTRY).empty(); } SetRawInfoWithVerificationStatus(storable_type, value, status); diff --git a/chromium/components/autofill/core/browser/data_model/address.h b/chromium/components/autofill/core/browser/data_model/address.h index 348ef2af02a..beb2f9d1600 100644 --- a/chromium/components/autofill/core/browser/data_model/address.h +++ b/chromium/components/autofill/core/browser/data_model/address.h @@ -12,6 +12,7 @@ #include "base/strings/string16.h" #include "components/autofill/core/browser/data_model/autofill_structured_address.h" #include "components/autofill/core/browser/data_model/form_group.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map.h" namespace autofill { @@ -53,6 +54,11 @@ class Address : public FormGroup { bool MergeStructuredAddress(const Address& newer, bool newer_was_more_recently_used); + // Fetches the canonical state name for the current address object if + // possible. + base::Optional<AlternativeStateNameMap::CanonicalStateName> + GetCanonicalizedStateName() const; + // For structured addresses, returns true if |this| is mergeable with |newer|. bool IsStructuredAddressMergeable(const Address& newer) const; diff --git a/chromium/components/autofill/core/browser/data_model/address_unittest.cc b/chromium/components/autofill/core/browser/data_model/address_unittest.cc index ee3aed0d76c..248ccdbe097 100644 --- a/chromium/components/autofill/core/browser/data_model/address_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/address_unittest.cc @@ -12,8 +12,10 @@ #include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/data_model/address.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h" #include "components/autofill/core/browser/geo/country_names.h" #include "components/autofill/core/common/autofill_features.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using base::ASCIIToUTF16; @@ -390,6 +392,55 @@ TEST_P(AddressTest, IsCountry) { EXPECT_EQ(0U, matching_types.size()); } +// Test the equality of two |Address()| objects w.r.t. the state. +TEST_P(AddressTest, TestAreStatesEqual) { + base::test::ScopedFeatureList feature; + // The feature + // |features::kAutofillEnableSupportForMoreStructureInAddresses| is disabled + // since it is incompatible with the feature + // |features::kAutofillUseStateMappingCache|. + feature.InitWithFeatures( + {features::kAutofillUseAlternativeStateNameMap}, + {features::kAutofillEnableSupportForMoreStructureInAddresses}); + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting(); + + struct { + const char* const country_code; + const char* const first; + const char* const second; + const bool result; + } test_cases[] = { + {"DE", "Bavaria", "BY", true}, {"DE", "", "", true}, + {"DE", "", "BY", false}, {"DE", "Bavaria", "Bayern", true}, + {"DE", "BY", "Bayern", true}, {"DE", "b.y.", "Bayern", true}}; + + for (const auto& test_case : test_cases) { + SCOPED_TRACE(testing::Message() << "first: " << test_case.first + << " | second: " << test_case.second); + + Address address1; + Address address2; + + // Set the address tokens. + address1.SetRawInfo(ADDRESS_HOME_COUNTRY, + base::ASCIIToUTF16(test_case.country_code)); + address2.SetRawInfo(ADDRESS_HOME_COUNTRY, + base::ASCIIToUTF16(test_case.country_code)); + + address1.SetRawInfo(ADDRESS_HOME_STATE, + base::ASCIIToUTF16(test_case.first)); + address2.SetRawInfo(ADDRESS_HOME_STATE, + base::ASCIIToUTF16(test_case.second)); + + if (test_case.result) { + EXPECT_TRUE(address1 == address2); + } else { + EXPECT_FALSE(address1 == address2); + } + } +} + // Verifies that Address::GetInfo() correctly combines address lines. TEST_P(AddressTest, GetStreetAddress) { const AutofillType type = AutofillType(ADDRESS_HOME_STREET_ADDRESS); diff --git a/chromium/components/autofill/core/browser/data_model/autofill_data_model.cc b/chromium/components/autofill/core/browser/data_model/autofill_data_model.cc index 7da13d15f85..b0ea8bd395f 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_data_model.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_data_model.cc @@ -87,9 +87,4 @@ AutofillDataModel::ValidityState AutofillDataModel::GetValidityState( return AutofillDataModel::UNSUPPORTED; } -bool AutofillDataModel::ShouldSkipFillingOrSuggesting( - ServerFieldType type) const { - return false; -} - } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/autofill_data_model.h b/chromium/components/autofill/core/browser/data_model/autofill_data_model.h index e8738773f25..e67afcda8dc 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_data_model.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_data_model.h @@ -94,10 +94,6 @@ class AutofillDataModel : public FormGroup { virtual ValidityState GetValidityState(ServerFieldType type, ValidationSource source) const; - // Check for the validity of the data. Leave the field empty if the data is - // invalid and the relevant feature is enabled. - virtual bool ShouldSkipFillingOrSuggesting(ServerFieldType type) const; - protected: // Called to update |use_count_| and |use_date_| when this data model is // the subject of user interaction (usually, when it's used to fill a form). diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile.cc index e9668df416b..10eee8a3ffe 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile.cc @@ -17,6 +17,7 @@ #include "base/i18n/char_iterator.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" +#include "base/ranges/algorithm.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -67,10 +68,10 @@ namespace { // similarly. ServerFieldType GetStorableTypeCollapsingGroups(ServerFieldType type) { ServerFieldType storable_type = AutofillType(type).GetStorableType(); - if (AutofillType(storable_type).group() == NAME) + if (AutofillType(storable_type).group() == FieldTypeGroup::kName) return NAME_FULL; - if (AutofillType(storable_type).group() == PHONE_HOME) + if (AutofillType(storable_type).group() == FieldTypeGroup::kPhoneHome) return PHONE_HOME_WHOLE_NUMBER; return storable_type; @@ -167,7 +168,7 @@ void GetFieldsForDistinguishingProfiles( // Keep track of which fields we've seen so that we avoid duplicate entries. // Always ignore fields of unknown type and the excluded field. - std::set<ServerFieldType> seen_fields; + ServerFieldTypeSet seen_fields; seen_fields.insert(UNKNOWN_TYPE); seen_fields.insert(GetStorableTypeCollapsingGroups(excluded_field)); @@ -222,7 +223,8 @@ static_assert(kNumSupportedTypesForValidation * kValidityBitsPerType <= 64, // main stored type for used to mark field validity . ServerFieldType NormalizeTypeForValidityCheck(ServerFieldType type) { auto field_type_group = AutofillType(type).group(); - if (field_type_group == PHONE_HOME || field_type_group == PHONE_BILLING) + if (field_type_group == FieldTypeGroup::kPhoneHome || + field_type_group == FieldTypeGroup::kPhoneBilling) return PHONE_HOME_WHOLE_NUMBER; return type; } @@ -259,9 +261,6 @@ AutofillProfile::AutofillProfile(const AutofillProfile& profile) company_(this), phone_number_(this) { operator=(profile); - // In case the profile is converted from a legacy profile or a wallet profile - // with structured names, try to finalize it. - FinalizeAfterImport(); } AutofillProfile::~AutofillProfile() = default; @@ -328,8 +327,7 @@ void AutofillProfile::GetMatchingTypes( const std::string& app_locale, ServerFieldTypeSet* matching_types) const { ServerFieldTypeSet matching_types_in_this_profile; - FormGroupList info = FormGroups(); - for (const auto* form_group : info) { + for (const auto* form_group : FormGroups()) { form_group->GetMatchingTypes(text, app_locale, &matching_types_in_this_profile); } @@ -348,8 +346,7 @@ void AutofillProfile::GetMatchingTypesAndValidities( return; ServerFieldTypeSet matching_types_in_this_profile; - FormGroupList info = FormGroups(); - for (const auto* form_group : info) { + for (const auto* form_group : FormGroups()) { form_group->GetMatchingTypes(text, app_locale, &matching_types_in_this_profile); } @@ -387,8 +384,7 @@ void AutofillProfile::SetRawInfoWithVerificationStatus( void AutofillProfile::GetSupportedTypes( ServerFieldTypeSet* supported_types) const { - FormGroupList info = FormGroups(); - for (const auto* form_group : info) { + for (const auto* form_group : FormGroups()) { form_group->GetSupportedTypes(supported_types); } } @@ -579,7 +575,7 @@ bool AutofillProfile::IsSubsetOfForFieldSet( // conditions that follow. return false; } - } else if (AutofillType(type).group() == PHONE_HOME) { + } else if (AutofillType(type).group() == FieldTypeGroup::kPhoneHome) { // Phone numbers should be canonicalized before comparing. if (type != PHONE_HOME_WHOLE_NUMBER && type != PHONE_HOME_CITY_AND_NUMBER) { @@ -1015,14 +1011,15 @@ bool AutofillProfile::IsAnInvalidPhoneNumber(ServerFieldType type) const { return true; ServerFieldTypeSet types; - if (GroupTypeOfServerFieldType(type) == PHONE_HOME) { + if (GroupTypeOfServerFieldType(type) == FieldTypeGroup::kPhoneHome) { types = {PHONE_HOME_NUMBER, PHONE_HOME_CITY_CODE, PHONE_HOME_CITY_AND_NUMBER}; if (type == PHONE_HOME_WHOLE_NUMBER) { types.insert(PHONE_HOME_WHOLE_NUMBER); types.insert(PHONE_HOME_COUNTRY_CODE); } - } else if (GroupTypeOfServerFieldType(type) == PHONE_BILLING) { + } else if (GroupTypeOfServerFieldType(type) == + FieldTypeGroup::kPhoneBilling) { types = {PHONE_BILLING_NUMBER, PHONE_BILLING_CITY_CODE, PHONE_BILLING_CITY_AND_NUMBER}; if (type == PHONE_BILLING_WHOLE_NUMBER) { @@ -1144,7 +1141,7 @@ bool AutofillProfile::ShouldSkipFillingOrSuggesting( autofill::features::kAutofillProfileClientValidation) && GetValidityState(type, AutofillProfile::CLIENT) == AutofillProfile::INVALID && - (GroupTypeOfServerFieldType(type) != ADDRESS_HOME || + (GroupTypeOfServerFieldType(type) != FieldTypeGroup::kAddressHome || !GetRawInfo(ADDRESS_HOME_COUNTRY).empty())) { return true; } @@ -1277,16 +1274,6 @@ void AutofillProfile::CreateInferredLabelsHelper( } } -AutofillProfile::FormGroupList AutofillProfile::FormGroups() const { - FormGroupList v(5); - v[0] = &name_; - v[1] = &email_; - v[2] = &company_; - v[3] = &phone_number_; - v[4] = &address_; - return v; -} - const FormGroup* AutofillProfile::FormGroupForType( const AutofillType& type) const { return const_cast<AutofillProfile*>(this)->MutableFormGroupForType(type); @@ -1294,30 +1281,30 @@ const FormGroup* AutofillProfile::FormGroupForType( FormGroup* AutofillProfile::MutableFormGroupForType(const AutofillType& type) { switch (type.group()) { - case NAME: - case NAME_BILLING: + case FieldTypeGroup::kName: + case FieldTypeGroup::kNameBilling: return &name_; - case EMAIL: + case FieldTypeGroup::kEmail: return &email_; - case COMPANY: + case FieldTypeGroup::kCompany: return &company_; - case PHONE_HOME: - case PHONE_BILLING: + case FieldTypeGroup::kPhoneHome: + case FieldTypeGroup::kPhoneBilling: return &phone_number_; - case ADDRESS_HOME: - case ADDRESS_BILLING: + case FieldTypeGroup::kAddressHome: + case FieldTypeGroup::kAddressBilling: return &address_; - case NO_GROUP: - case CREDIT_CARD: - case PASSWORD_FIELD: - case USERNAME_FIELD: - case TRANSACTION: - case UNFILLABLE: + case FieldTypeGroup::kNoGroup: + case FieldTypeGroup::kCreditCard: + case FieldTypeGroup::kPasswordField: + case FieldTypeGroup::kUsernameField: + case FieldTypeGroup::kTransaction: + case FieldTypeGroup::kUnfillable: return nullptr; } @@ -1331,68 +1318,57 @@ bool AutofillProfile::EqualsSansGuid(const AutofillProfile& profile) const { } std::ostream& operator<<(std::ostream& os, const AutofillProfile& profile) { - return os - << (profile.record_type() == AutofillProfile::LOCAL_PROFILE - ? profile.guid() - : base::HexEncode(profile.server_id().data(), - profile.server_id().size())) - << " " << profile.origin() << " " - << UTF16ToUTF8(profile.GetRawInfo(NAME_FULL)) << " " - << "(" - << base::NumberToString(profile.GetVerificationStatusInt(NAME_FULL)) - << ") " << UTF16ToUTF8(profile.GetRawInfo(NAME_FIRST)) << " " - << "(" - << base::NumberToString(profile.GetVerificationStatusInt(NAME_FIRST)) - << ") " << UTF16ToUTF8(profile.GetRawInfo(NAME_MIDDLE)) << " " - << "(" - << base::NumberToString(profile.GetVerificationStatusInt(NAME_MIDDLE)) - << ") " << UTF16ToUTF8(profile.GetRawInfo(NAME_LAST)) << " " - << "(" - << base::NumberToString(profile.GetVerificationStatusInt(NAME_LAST)) - << ") " << UTF16ToUTF8(profile.GetRawInfo(NAME_LAST_FIRST)) << " " - << "(" - << base::NumberToString( - profile.GetVerificationStatusInt(NAME_LAST_FIRST)) - << ") " << UTF16ToUTF8(profile.GetRawInfo(NAME_LAST_CONJUNCTION)) - << " " - << "(" - << base::NumberToString( - profile.GetVerificationStatusInt(NAME_LAST_CONJUNCTION)) - << ") " << UTF16ToUTF8(profile.GetRawInfo(NAME_LAST_SECOND)) << " " - << "(" - << base::NumberToString( - profile.GetVerificationStatusInt(NAME_LAST_SECOND)) - << ") " << UTF16ToUTF8(profile.GetRawInfo(EMAIL_ADDRESS)) << " " - << UTF16ToUTF8(profile.GetRawInfo(COMPANY_NAME)) << " " - << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE1)) << " " - << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE2)) << " " - << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE3)) << " " - << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)) << " " - << "(" - << base::NumberToString( - profile.GetVerificationStatusInt(ADDRESS_HOME_STREET_ADDRESS)) - << ") " << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STREET_NAME)) - << " " - << "(" - << base::NumberToString( - profile.GetVerificationStatusInt(ADDRESS_HOME_STREET_NAME)) - << ") " << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_HOUSE_NUMBER)) - << " " - << "(" - << base::NumberToString( - profile.GetVerificationStatusInt(ADDRESS_HOME_HOUSE_NUMBER)) - << ") " - << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY)) - << " " << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_CITY)) << " " - << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STATE)) << " " - << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_ZIP)) << " " - << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE)) << " " - << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY)) << " " - << profile.language_code() << " " - << UTF16ToUTF8(profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)) << " " - << profile.GetClientValidityBitfieldValue() << " " - << profile.has_converted() << " " << profile.use_count() << " " - << profile.use_date(); + os << (profile.record_type() == AutofillProfile::LOCAL_PROFILE + ? profile.guid() + : base::HexEncode(profile.server_id().data(), + profile.server_id().size())) + << " " << profile.origin() << " " + << profile.GetClientValidityBitfieldValue() << " " + << profile.has_converted() << " " << profile.use_count() << " " + << profile.use_date() << " " << profile.language_code() << std::endl; + + // Lambda to print the value and verification status for |type|. + auto print_values_lambda = [&os, &profile](ServerFieldType type) { + os << AutofillType::ServerFieldTypeToString(type) << ": " + << profile.GetRawInfo(type) << "(" << profile.GetVerificationStatus(type) + << ")" << std::endl; + }; + + // Use a helper function to print the values of the stored types. + const ServerFieldType field_types_to_print[] = { + NAME_FULL, + NAME_HONORIFIC_PREFIX, + NAME_FIRST, + NAME_MIDDLE, + NAME_LAST, + NAME_LAST_FIRST, + NAME_LAST_CONJUNCTION, + NAME_LAST_SECOND, + EMAIL_ADDRESS, + COMPANY_NAME, + ADDRESS_HOME_ADDRESS, + ADDRESS_HOME_LINE1, + ADDRESS_HOME_LINE2, + ADDRESS_HOME_LINE3, + ADDRESS_HOME_STREET_ADDRESS, + ADDRESS_HOME_STREET_NAME, + ADDRESS_HOME_DEPENDENT_STREET_NAME, + ADDRESS_HOME_HOUSE_NUMBER, + ADDRESS_HOME_APT_NUM, + ADDRESS_HOME_FLOOR, + ADDRESS_HOME_DEPENDENT_LOCALITY, + ADDRESS_HOME_PREMISE_NAME, + ADDRESS_HOME_SUBPREMISE, + ADDRESS_HOME_CITY, + ADDRESS_HOME_STATE, + ADDRESS_HOME_ZIP, + ADDRESS_HOME_SORTING_CODE, + ADDRESS_HOME_COUNTRY, + PHONE_HOME_WHOLE_NUMBER}; + + base::ranges::for_each(field_types_to_print, print_values_lambda); + + return os; } bool AutofillProfile::FinalizeAfterImport() { diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile.h b/chromium/components/autofill/core/browser/data_model/autofill_profile.h index 9bdb9ea800a..d11ce432c8c 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile.h @@ -7,6 +7,7 @@ #include <stddef.h> +#include <array> #include <iosfwd> #include <list> #include <map> @@ -279,7 +280,7 @@ class AutofillProfile : public AutofillDataModel { // Check for the validity of the data. Leave the field empty if the data is // invalid and the relevant feature is enabled. - bool ShouldSkipFillingOrSuggesting(ServerFieldType type) const override; + bool ShouldSkipFillingOrSuggesting(ServerFieldType type) const; base::WeakPtr<const AutofillProfile> GetWeakPtr() const { return weak_ptr_factory_.GetWeakPtr(); @@ -300,8 +301,6 @@ class AutofillProfile : public AutofillDataModel { const Address& GetAddress() const { return address_; } private: - typedef std::vector<const FormGroup*> FormGroupList; - // FormGroup: base::string16 GetInfoImpl(const AutofillType& type, const std::string& app_locale) const override; @@ -330,7 +329,11 @@ class AutofillProfile : public AutofillDataModel { // Utilities for listing and lookup of the data members that constitute // user-visible profile information. - FormGroupList FormGroups() const; + std::array<const FormGroup*, 5> FormGroups() const { + // Adjust the return type size as necessary. + return {&name_, &email_, &company_, &phone_number_, &address_}; + } + const FormGroup* FormGroupForType(const AutofillType& type) const; FormGroup* MutableFormGroupForType(const AutofillType& type); diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc index e24375a36eb..978ba958b27 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc @@ -10,6 +10,7 @@ #include "base/i18n/case_conversion.h" #include "base/i18n/char_iterator.h" #include "base/i18n/unicodestring.h" +#include "base/optional.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversion_utils.h" @@ -701,13 +702,48 @@ bool AutofillProfileComparator::MergeAddresses(const AutofillProfile& p1, const AutofillType kState(ADDRESS_HOME_STATE); const base::string16& state1 = p1.GetInfo(kState, app_locale_); const base::string16& state2 = p2.GetInfo(kState, app_locale_); - if (state1.empty()) { - address->SetInfo(kState, state2, app_locale_); - } else if (state2.empty()) { - address->SetInfo(kState, state1, app_locale_); + + if (base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap)) { + // Holds information about the state string that is going to be used as the + // state value in the merged profile. + base::string16 candidate_state = state1; + + // Cases where the |state2| is used as the state value in the merged + // profile: + // 1. |state1| is empty. + // 2. |state2| has the canonical state name present in + // AlternativeStateNameMap and |state1| does not. + // 3. |state2.size()| < |state1.size()| and either both or none of them + // have canonical state name present in the AlternativeStateNameMap. + if (state1.empty()) { + candidate_state = state2; + } else if (!state2.empty()) { + bool state1_has_canonical_name_present = + p1.GetAddress().GetCanonicalizedStateName().has_value(); + bool state2_has_canonical_name_present = + p2.GetAddress().GetCanonicalizedStateName().has_value(); + + if ((state2_has_canonical_name_present && + !state1_has_canonical_name_present) || + (state2_has_canonical_name_present == + state1_has_canonical_name_present && + state2.size() < state1.size())) { + candidate_state = state2; + } + } + + address->SetInfo(kState, candidate_state, app_locale_); } else { - address->SetInfo(kState, (state2.size() < state1.size() ? state2 : state1), - app_locale_); + if (state1.empty()) { + address->SetInfo(kState, state2, app_locale_); + } else if (state2.empty()) { + address->SetInfo(kState, state1, app_locale_); + } else { + address->SetInfo(kState, + (state2.size() < state1.size() ? state2 : state1), + app_locale_); + } } AddressRewriter rewriter = AddressRewriter::ForCountryCode(country_code); @@ -1149,7 +1185,14 @@ bool AutofillProfileComparator::HaveMergeableAddresses( // State // ------ - // Heuristic: States are mergeable if one is a (possibly empty) bag of words + // When |kAutofillUseAlternativeStateNameMap| is disabled: States are + // mergeable if one is a (possibly empty) bag of words subset of the other. + // + // When |kAutofillUseAlternativeStateNameMap| is enabled: The profiles + // w.r.t the state are mergeable if their canonical state names in + // AlternativeStateNameMap matches. + // In case one of the profile does not have a canonical state name present in + // the AlternativeStateNameMap, states are mergeable if one is a bag of words // subset of the other. // // TODO(rogerm): If the match is between non-empty zip codes then we can infer @@ -1157,12 +1200,30 @@ bool AutofillProfileComparator::HaveMergeableAddresses( // handles the cases where we have invalid or poorly formed data in one of the // state values (like "Select one", or "CA - California"). const AutofillType kState(ADDRESS_HOME_STATE); - const base::string16& state1 = - rewriter.Rewrite(NormalizeForComparison(p1.GetInfo(kState, app_locale_))); - const base::string16& state2 = - rewriter.Rewrite(NormalizeForComparison(p2.GetInfo(kState, app_locale_))); - if (CompareTokens(state1, state2) == DIFFERENT_TOKENS) { - return false; + bool canonical_state_names_match = false; + bool use_alternative_state_name_map_enabled = base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap); + if (use_alternative_state_name_map_enabled) { + base::Optional<AlternativeStateNameMap::CanonicalStateName> + canonical_name_state1 = p1.GetAddress().GetCanonicalizedStateName(); + base::Optional<AlternativeStateNameMap::CanonicalStateName> + canonical_name_state2 = p2.GetAddress().GetCanonicalizedStateName(); + if (canonical_name_state1 && canonical_name_state2) { + if (canonical_name_state1.value() == canonical_name_state2.value()) + canonical_state_names_match = true; + else + return false; + } + } + + if (!use_alternative_state_name_map_enabled || !canonical_state_names_match) { + base::string16 state1 = rewriter.Rewrite( + NormalizeForComparison(p1.GetInfo(kState, app_locale_))); + base::string16 state2 = rewriter.Rewrite( + NormalizeForComparison(p2.GetInfo(kState, app_locale_))); + if (CompareTokens(state1, state2) == DIFFERENT_TOKENS) { + return false; + } } // City diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc index a3cdf6c2e63..6935d2073eb 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc @@ -15,6 +15,7 @@ #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h" #include "components/autofill/core/browser/data_model/contact_info.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h" #include "components/autofill/core/browser/geo/country_names.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_features.h" @@ -1428,6 +1429,75 @@ TEST_P(AutofillProfileComparatorTest, MergeAddressesAndExpect(p2, p1, expected); } +// Checks for various scenarios for determining mergeability of profiles w.r.t. +// the state. +TEST_P(AutofillProfileComparatorTest, CheckStatesMergeability) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature( + autofill::features::kAutofillUseAlternativeStateNameMap); + + autofill::test::ClearAlternativeStateNameMapForTesting(); + autofill::test::PopulateAlternativeStateNameMapForTesting(); + + AutofillProfile empty = CreateProfileWithAddress("", "", "", "", "", "DE"); + AutofillProfile p1 = CreateProfileWithAddress("", "", "", "Bayern", "", "DE"); + AutofillProfile p2 = CreateProfileWithAddress("", "", "", "Random", "", "DE"); + AutofillProfile p3 = + CreateProfileWithAddress("", "", "", "Bayern - BY - Bavaria", "", "DE"); + AutofillProfile p4 = + CreateProfileWithAddress("", "", "", "Bavaria", "", "DE"); + + EXPECT_TRUE(comparator_.HaveMergeableAddresses(empty, empty)); + EXPECT_TRUE(comparator_.HaveMergeableAddresses(p1, empty)); + EXPECT_TRUE(comparator_.HaveMergeableAddresses(p1, p1)); + EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, p2)); + EXPECT_TRUE(comparator_.HaveMergeableAddresses(p3, p1)); + EXPECT_TRUE(comparator_.HaveMergeableAddresses(p1, p4)); + EXPECT_FALSE(comparator_.HaveMergeableAddresses(p2, p4)); +} + +// Tests that the profiles are merged when they have common states. +TEST_P(AutofillProfileComparatorTest, MergeProfilesBasedOnState) { + base::test::ScopedFeatureList feature; + // The feature + // |autofill::features::kAutofillEnableSupportForMoreStructureInAddresses| is + // disabled since it is incompatible with the feature + // |autofill::features::kAutofillUseStateMappingCache|. + feature.InitWithFeatures( + {autofill::features::kAutofillUseAlternativeStateNameMap}, + {autofill::features::kAutofillEnableSupportForMoreStructureInAddresses}); + + autofill::test::ClearAlternativeStateNameMapForTesting(); + autofill::test::PopulateAlternativeStateNameMapForTesting(); + autofill::test::PopulateAlternativeStateNameMapForTesting( + "IN", "UP", + {{.canonical_name = "Uttar Pradesh", + .abbreviations = {"UP"}, + .alternative_names = {}}}); + + AutofillProfile empty = CreateProfileWithAddress("", "", "", "", "", "DE"); + AutofillProfile p1 = CreateProfileWithAddress("", "", "", "Bayern", "", "DE"); + AutofillProfile p2 = + CreateProfileWithAddress("", "", "", "Bayern - BY - Bavaria", "", "DE"); + + Address expected; + expected.SetRawInfo(ADDRESS_HOME_COUNTRY, UTF8ToUTF16("DE")); + expected.SetRawInfo(ADDRESS_HOME_STATE, UTF8ToUTF16("Bayern")); + MergeAddressesAndExpect(empty, p1, expected); + MergeAddressesAndExpect(p1, empty, expected); + MergeAddressesAndExpect(p1, p2, expected); + MergeAddressesAndExpect(p2, p1, expected); + + AutofillProfile p3 = + CreateProfileWithAddress("", "", "", "Pradesh", "", "IN"); + AutofillProfile p4 = + CreateProfileWithAddress("", "", "", "Uttar Pradesh", "", "IN"); + expected.SetRawInfo(ADDRESS_HOME_COUNTRY, UTF8ToUTF16("IN")); + expected.SetRawInfo(ADDRESS_HOME_STATE, UTF8ToUTF16("Uttar Pradesh")); + MergeAddressesAndExpect(p3, p4, expected); + MergeAddressesAndExpect(p4, p3, expected); +} + INSTANTIATE_TEST_SUITE_P( All, AutofillProfileComparatorTest, diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc index 5029ad6d3ad..2e47d29e0b9 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc @@ -1123,18 +1123,25 @@ TEST_P(AutofillProfileTest, MergeDataFrom_DifferentProfile) { AutofillProfile b = a; b.set_guid(base::GenerateGUID()); b.set_origin(kSettingsOrigin); - b.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Unit 5, area 51")); - b.SetRawInfo(COMPANY_NAME, base::string16()); + b.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_LINE2, ASCIIToUTF16("Unit 5, area 51"), + structured_address::VerificationStatus::kObserved); + b.SetRawInfoWithVerificationStatus( + COMPANY_NAME, base::string16(), + structured_address::VerificationStatus::kObserved); b.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("M.")); b.SetRawInfo(NAME_FULL, ASCIIToUTF16("Marion M. Morrison")); b.set_language_code("en"); + b.FinalizeAfterImport(); + a.FinalizeAfterImport(); EXPECT_TRUE(a.MergeDataFrom(b, "en-US")); // Merge has modified profile a, the validation is not updated. EXPECT_FALSE(a.is_client_validity_states_updated()); EXPECT_EQ(kSettingsOrigin, a.origin()); - EXPECT_EQ(ASCIIToUTF16("Unit 5, area 51"), a.GetRawInfo(ADDRESS_HOME_LINE2)); + EXPECT_EQ("Unit 5, area 51", + base::UTF16ToUTF8(a.GetRawInfo(ADDRESS_HOME_LINE2))); EXPECT_EQ(ASCIIToUTF16("Fox"), a.GetRawInfo(COMPANY_NAME)); base::string16 name = a.GetInfo(NAME_FULL, "en-US"); EXPECT_EQ(ASCIIToUTF16("Marion Mitchell Morrison"), name); diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address.cc b/chromium/components/autofill/core/browser/data_model/autofill_structured_address.cc index b5fa1ea03e3..80aabc5ac36 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address.cc @@ -38,7 +38,6 @@ base::string16 AddressComponentWithRewriter::ValueForComparison() const { StreetName::StreetName(AddressComponent* parent) : AddressComponent(ADDRESS_HOME_STREET_NAME, parent, - {}, MergeMode::kDefault) {} StreetName::~StreetName() = default; @@ -46,7 +45,6 @@ StreetName::~StreetName() = default; DependentStreetName::DependentStreetName(AddressComponent* parent) : AddressComponent(ADDRESS_HOME_DEPENDENT_STREET_NAME, parent, - {}, MergeMode::kDefault) {} DependentStreetName::~DependentStreetName() = default; @@ -55,7 +53,6 @@ StreetAndDependentStreetName::StreetAndDependentStreetName( AddressComponent* parent) : AddressComponent(ADDRESS_HOME_STREET_AND_DEPENDENT_STREET_NAME, parent, - {&thoroughfare_name_, &dependent_thoroughfare_name_}, MergeMode::kDefault) {} StreetAndDependentStreetName::~StreetAndDependentStreetName() = default; @@ -63,7 +60,6 @@ StreetAndDependentStreetName::~StreetAndDependentStreetName() = default; HouseNumber::HouseNumber(AddressComponent* parent) : AddressComponent(ADDRESS_HOME_HOUSE_NUMBER, parent, - {}, MergeMode::kDefault) {} HouseNumber::~HouseNumber() = default; @@ -71,25 +67,23 @@ HouseNumber::~HouseNumber() = default; Premise::Premise(AddressComponent* parent) : AddressComponent(ADDRESS_HOME_PREMISE_NAME, parent, - {}, MergeMode::kDefault) {} Premise::~Premise() = default; Floor::Floor(AddressComponent* parent) - : AddressComponent(ADDRESS_HOME_FLOOR, parent, {}, MergeMode::kDefault) {} + : AddressComponent(ADDRESS_HOME_FLOOR, parent, MergeMode::kDefault) {} Floor::~Floor() = default; Apartment::Apartment(AddressComponent* parent) - : AddressComponent(ADDRESS_HOME_APT_NUM, parent, {}, MergeMode::kDefault) {} + : AddressComponent(ADDRESS_HOME_APT_NUM, parent, MergeMode::kDefault) {} Apartment::~Apartment() = default; SubPremise::SubPremise(AddressComponent* parent) : AddressComponent(ADDRESS_HOME_SUBPREMISE, parent, - {&floor_, &apartment_}, MergeMode::kDefault) {} SubPremise::~SubPremise() = default; @@ -101,7 +95,6 @@ StreetAddress::StreetAddress(AddressComponent* parent) : AddressComponentWithRewriter( ADDRESS_HOME_STREET_ADDRESS, parent, - {&streets_, &number_, &premise_, &sub_premise_}, MergeMode::kReplaceEmpty | MergeMode::kReplaceSubset | MergeMode::kDefault) {} @@ -114,7 +107,9 @@ StreetAddress::GetParseRegularExpressionsByRelevance() const { return {pattern_provider->GetRegEx(RegEx::kParseHouseNumberStreetName), pattern_provider->GetRegEx(RegEx::kParseStreetNameHouseNumber), pattern_provider->GetRegEx( - RegEx::kParseStreetNameHouseNumberSuffixedFloor)}; + RegEx::kParseStreetNameHouseNumberSuffixedFloor), + pattern_provider->GetRegEx( + RegEx::kParseStreetNameHouseNumberSuffixedFloorAndAppartmentRe)}; } void StreetAddress::ParseValueAndAssignSubcomponentsByFallbackMethod() { @@ -167,6 +162,16 @@ base::string16 StreetAddress::GetBestFormatString() const { "${ADDRESS_HOME_FLOOR;, ;. Stock}${ADDRESS_HOME_APT_NUM;, ;. Wohnung}"); } + if (country_code == "MX") { + return base::ASCIIToUTF16( + "${ADDRESS_HOME_STREET_NAME} ${ADDRESS_HOME_HOUSE_NUMBER}" + "${ADDRESS_HOME_FLOOR; - Piso ;}${ADDRESS_HOME_APT_NUM; - ;}"); + } + if (country_code == "ES") { + return base::UTF8ToUTF16( + "${ADDRESS_HOME_STREET_NAME} ${ADDRESS_HOME_HOUSE_NUMBER}" + "${ADDRESS_HOME_FLOOR;, ;º}${ADDRESS_HOME_APT_NUM;, ;ª}"); + } // Use the format for US/UK as the default. return base::ASCIIToUTF16( "${ADDRESS_HOME_HOUSE_NUMBER} ${ADDRESS_HOME_STREET_NAME} " @@ -209,21 +214,21 @@ bool StreetAddress::IsValueValid() const { bool StreetAddress::ConvertAndGetTheValueForAdditionalFieldTypeName( const std::string& type_name, base::string16* value) const { - if (type_name == AutofillType(ADDRESS_HOME_LINE1).ToString()) { + if (type_name == AutofillType::ServerFieldTypeToString(ADDRESS_HOME_LINE1)) { if (value) { *value = address_lines_.size() > 0 ? address_lines_.at(0) : base::string16(); } return true; } - if (type_name == AutofillType(ADDRESS_HOME_LINE2).ToString()) { + if (type_name == AutofillType::ServerFieldTypeToString(ADDRESS_HOME_LINE2)) { if (value) { *value = address_lines_.size() > 1 ? address_lines_.at(1) : base::string16(); } return true; } - if (type_name == AutofillType(ADDRESS_HOME_LINE3).ToString()) { + if (type_name == AutofillType::ServerFieldTypeToString(ADDRESS_HOME_LINE3)) { if (value) { *value = address_lines_.size() > 2 ? address_lines_.at(2) : base::string16(); @@ -240,11 +245,13 @@ bool StreetAddress::ConvertAndSetValueForAdditionalFieldTypeName( const base::string16& value, const VerificationStatus& status) { size_t index = 0; - if (type_name == AutofillType(ADDRESS_HOME_LINE1).ToString()) { + if (type_name == AutofillType::ServerFieldTypeToString(ADDRESS_HOME_LINE1)) { index = 0; - } else if (type_name == AutofillType(ADDRESS_HOME_LINE2).ToString()) { + } else if (type_name == + AutofillType::ServerFieldTypeToString(ADDRESS_HOME_LINE2)) { index = 1; - } else if (type_name == AutofillType(ADDRESS_HOME_LINE3).ToString()) { + } else if (type_name == + AutofillType::ServerFieldTypeToString(ADDRESS_HOME_LINE3)) { index = 2; } else { return false; @@ -287,7 +294,6 @@ void StreetAddress::GetAdditionalSupportedFieldTypes( CountryCode::CountryCode(AddressComponent* parent) : AddressComponent(ADDRESS_HOME_COUNTRY, parent, - {}, MergeMode::kReplaceEmpty | MergeMode::kUseBetterOrNewerForSameValue) {} @@ -298,7 +304,6 @@ CountryCode::~CountryCode() = default; DependentLocality::DependentLocality(AddressComponent* parent) : AddressComponent(ADDRESS_HOME_DEPENDENT_LOCALITY, parent, - {}, MergeMode::kReplaceSubset | MergeMode::kReplaceEmpty) {} DependentLocality::~DependentLocality() = default; @@ -308,7 +313,6 @@ DependentLocality::~DependentLocality() = default; City::City(AddressComponent* parent) : AddressComponent(ADDRESS_HOME_CITY, parent, - {}, MergeMode::kReplaceSubset | MergeMode::kReplaceEmpty) {} City::~City() = default; @@ -319,7 +323,6 @@ State::State(AddressComponent* parent) : AddressComponentWithRewriter( ADDRESS_HOME_STATE, parent, - {}, MergeMode::kPickShorterIfOneContainsTheOther | kReplaceEmpty) {} State::~State() = default; @@ -330,7 +333,6 @@ PostalCode::PostalCode(AddressComponent* parent) : AddressComponentWithRewriter( ADDRESS_HOME_ZIP, parent, - {}, MergeMode::kUseMostRecentSubstring | kReplaceEmpty) {} PostalCode::~PostalCode() = default; @@ -342,35 +344,37 @@ base::string16 PostalCode::NormalizedValue() const { SortingCode::SortingCode(AddressComponent* parent) : AddressComponent(ADDRESS_HOME_SORTING_CODE, parent, - {}, MergeMode::kReplaceEmpty | kUseMostRecentSubstring) {} SortingCode::~SortingCode() = default; -Address::Address() : Address{nullptr} {} +Address::Address() : Address(nullptr) {} Address::Address(const Address& other) : Address() { - *this = other; + CopyFrom(other); } -bool Address::WipeInvalidStructure() { - // For structured addresses, currently it is sufficient to wipe the structure - // of the street address, because this is the only directly assignable value - // that has a substructure. - return street_address_.WipeInvalidStructure(); +Address& Address::operator=(const Address& other) { + CopyFrom(other); + return *this; } // Addresses are mergeable when all of their children are mergeable. -// Reformat the address from their children after merge. +// Reformat the address from the children after merge if it changed. Address::Address(AddressComponent* parent) : AddressComponent(ADDRESS_HOME_ADDRESS, parent, - {&street_address_, &postal_code_, &sorting_code_, - &dependent_locality_, &city_, &state_, &country_code_}, - MergeMode::kMergeChildrenAndReformat) {} + MergeMode::kMergeChildrenAndReformatIfNeeded) {} Address::~Address() = default; +bool Address::WipeInvalidStructure() { + // For structured addresses, currently it is sufficient to wipe the structure + // of the street address, because this is the only directly assignable value + // that has a substructure. + return street_address_.WipeInvalidStructure(); +} + void Address::MigrateLegacyStructure(bool is_verified_profile) { // If this component already has a verification status, no profile is regarded // as already verified. diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address.h b/chromium/components/autofill/core/browser/data_model/autofill_structured_address.h index 7e872bfc114..87ac1fdc8e8 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address.h @@ -221,10 +221,9 @@ class Address : public AddressComponent { Address(const Address& other); explicit Address(AddressComponent* parent); ~Address() override; + Address& operator=(const Address& other); - // Migrates from a legacy structure in which name tokens are imported without - // a status. - void MigrateLegacyStructure(bool is_verified_profile); + void MigrateLegacyStructure(bool is_verified_profile) override; // Checks if the street address contains an invalid structure and wipes it if // necessary. diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component.cc b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component.cc index 50e6e86f2d2..a2a1c831c31 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component.cc @@ -49,15 +49,50 @@ bool IsLessSignificantVerificationStatus(VerificationStatus left, static_cast<std::underlying_type_t<VerificationStatus>>(right); } +VerificationStatus GetMoreSignificantVerificationStatus( + VerificationStatus left, + VerificationStatus right) { + if (IsLessSignificantVerificationStatus(left, right)) + return right; + + return left; +} + +std::ostream& operator<<(std::ostream& os, VerificationStatus status) { + switch (status) { + case VerificationStatus::kNoStatus: + os << "NoStatus"; + break; + case VerificationStatus::kParsed: + os << "Parsed"; + break; + case VerificationStatus::kFormatted: + os << "Formatted"; + break; + case VerificationStatus::kObserved: + os << "Observed"; + break; + case VerificationStatus::kServerParsed: + os << "ServerParsed"; + break; + case VerificationStatus::kUserVerified: + os << "UserVerified"; + break; + } + return os; +} + AddressComponent::AddressComponent(ServerFieldType storage_type, AddressComponent* parent, - std::vector<AddressComponent*> subcomponents, unsigned int merge_mode) : value_verification_status_(VerificationStatus::kNoStatus), storage_type_(storage_type), - subcomponents_(subcomponents), parent_(parent), - merge_mode_(merge_mode) {} + merge_mode_(merge_mode) { + if (parent) { + parent->RegisterChildNode(this); + } +} AddressComponent::~AddressComponent() = default; @@ -66,55 +101,51 @@ ServerFieldType AddressComponent::GetStorageType() const { } std::string AddressComponent::GetStorageTypeName() const { - return AutofillType(storage_type_).ToString(); + return AutofillType::ServerFieldTypeToString(storage_type_); } -AddressComponent& AddressComponent::operator=(const AddressComponent& right) { - DCHECK(GetStorageType() == right.GetStorageType()); - if (this == &right) - return *this; +void AddressComponent::CopyFrom(const AddressComponent& other) { + DCHECK(GetStorageType() == other.GetStorageType()); + if (this == &other) + return; - if (right.IsValueAssigned()) { - value_ = right.value_; - value_verification_status_ = right.value_verification_status_; - sorted_normalized_tokens_ = right.sorted_normalized_tokens_; + if (other.IsValueAssigned()) { + value_ = other.value_; + value_verification_status_ = other.value_verification_status_; + sorted_normalized_tokens_ = other.sorted_normalized_tokens_; } else { UnsetValue(); } - DCHECK(right.subcomponents_.size() == subcomponents_.size()); + CHECK(other.subcomponents_.size() == subcomponents_.size()); - for (size_t i = 0; i < right.subcomponents_.size(); i++) - *subcomponents_[i] = *right.subcomponents_[i]; + for (size_t i = 0; i < other.subcomponents_.size(); i++) + subcomponents_[i]->CopyFrom(*other.subcomponents_[i]); PostAssignSanitization(); - - return *this; } -bool AddressComponent::operator==(const AddressComponent& right) const { - if (this == &right) +bool AddressComponent::SameAs(const AddressComponent& other) const { + if (this == &other) return true; - if (GetStorageType() != right.GetStorageType()) + if (GetStorageType() != other.GetStorageType()) return false; - if (GetValue() != right.GetValue() || - value_verification_status_ != right.value_verification_status_) { + if (GetValue() != other.GetValue() || + value_verification_status_ != other.value_verification_status_) { return false; } - DCHECK(right.subcomponents_.size() == subcomponents_.size()); - for (size_t i = 0; i < right.subcomponents_.size(); i++) - if (!(*subcomponents_[i] == *right.subcomponents_[i])) + DCHECK(other.subcomponents_.size() == subcomponents_.size()); + for (size_t i = 0; i < other.subcomponents_.size(); i++) { + if (!(subcomponents_[i]->SameAs(*other.subcomponents_[i]))) { return false; + } + } return true; } -bool AddressComponent::operator!=(const AddressComponent& right) const { - return !(*this == right); -} - bool AddressComponent::IsAtomic() const { return subcomponents_.empty(); } @@ -134,7 +165,12 @@ bool AddressComponent::IsValueForTypeValid(const std::string& field_type_name, bool AddressComponent::IsValueForTypeValid(ServerFieldType field_type, bool wipe_if_not) { - return IsValueForTypeValid(AutofillType(field_type).ToString(), wipe_if_not); + return IsValueForTypeValid(AutofillType::ServerFieldTypeToString(field_type), + wipe_if_not); +} + +void AddressComponent::RegisterChildNode(AddressComponent* child) { + subcomponents_.push_back(child); } bool AddressComponent::GetIsValueForTypeValidIfPossible( @@ -191,8 +227,9 @@ void AddressComponent::GetSupportedTypes( << storage_type_; supported_types->insert(storage_type_); GetAdditionalSupportedFieldTypes(supported_types); - for (auto* subcomponent : subcomponents_) + for (auto* subcomponent : subcomponents_) { subcomponent->GetSupportedTypes(supported_types); + } } bool AddressComponent::ConvertAndSetValueForAdditionalFieldTypeName( @@ -239,9 +276,9 @@ bool AddressComponent::SetValueForTypeIfPossible( const VerificationStatus& verification_status, bool invalidate_child_nodes, bool invalidate_parent_nodes) { - return SetValueForTypeIfPossible(AutofillType(type).ToString(), value, - verification_status, invalidate_child_nodes, - invalidate_parent_nodes); + return SetValueForTypeIfPossible( + AutofillType::ServerFieldTypeToString(type), value, verification_status, + invalidate_child_nodes, invalidate_parent_nodes); } bool AddressComponent::SetValueForTypeIfPossible( @@ -318,8 +355,8 @@ bool AddressComponent::GetValueAndStatusForTypeIfPossible( const ServerFieldType& type, base::string16* value, VerificationStatus* status) const { - return GetValueAndStatusForTypeIfPossible(AutofillType(type).ToString(), - value, status); + return GetValueAndStatusForTypeIfPossible( + AutofillType::ServerFieldTypeToString(type), value, status); } bool AddressComponent::GetValueAndStatusForTypeIfPossible( @@ -353,22 +390,21 @@ bool AddressComponent::GetValueAndStatusForTypeIfPossible( base::string16 AddressComponent::GetValueForType( const ServerFieldType& type) const { - return GetValueForType(AutofillType(type).ToString()); + return GetValueForType(AutofillType::ServerFieldTypeToString(type)); } base::string16 AddressComponent::GetValueForType( const std::string& type_name) const { base::string16 value; bool success = GetValueAndStatusForTypeIfPossible(type_name, &value, nullptr); - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - DCHECK(success || type_name == AutofillType(NAME_HONORIFIC_PREFIX).ToString()) - << type_name; + DCHECK(success) << type_name; return value; } VerificationStatus AddressComponent::GetVerificationStatusForType( const ServerFieldType& type) const { - return GetVerificationStatusForType(AutofillType(type).ToString()); + return GetVerificationStatusForType( + AutofillType::ServerFieldTypeToString(type)); } VerificationStatus AddressComponent::GetVerificationStatusForType( @@ -376,9 +412,7 @@ VerificationStatus AddressComponent::GetVerificationStatusForType( VerificationStatus status = VerificationStatus::kNoStatus; bool success = GetValueAndStatusForTypeIfPossible(type_name, nullptr, &status); - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - DCHECK(success || - type_name == AutofillType(NAME_HONORIFIC_PREFIX).ToString()); + DCHECK(success) << type_name; return status; } @@ -447,11 +481,9 @@ bool AddressComponent::ParseValueAndAssignSubcomponentsByRegularExpression( const std::string& field_type = result_entry.first; base::string16 field_value = base::UTF8ToUTF16(result_entry.second); // Do not reassign the value of this node. - if (field_type == GetStorageTypeName()) - continue; - // crbug.com(1113617): Honorifics are temporarily disabled. - if (field_type == AutofillType(NAME_HONORIFIC_PREFIX).ToString()) + if (field_type == GetStorageTypeName()) { continue; + } bool success = SetValueForTypeIfPossible(field_type, field_value, VerificationStatus::kParsed); // Setting the value should always work unless the regular expression is @@ -504,28 +536,35 @@ void AddressComponent::ParseValueAndAssignSubcomponentsByFallbackMethod() { DCHECK(success); } -bool AddressComponent::WipeInvalidStructure() { +bool AddressComponent::AllDescendantsAreEmpty() const { + return base::ranges::all_of(Subcomponents(), [](const auto* c) { + return c->GetValue().empty() && c->AllDescendantsAreEmpty(); + }); +} + +bool AddressComponent::IsStructureValid() const { if (IsAtomic()) { - return false; + return true; } - // Test that each structured token is part of the subcomponent. // This is not perfect, because different components can match with an // overlapping portion of the unstructured string, but it guarantees that all // information in the components is contained in the unstructured // representation. - for (const auto* component : Subcomponents()) { - if (GetValue().find(component->GetValue()) == base::string16::npos) { - // If the value of one component could not have been found, wipe the full - // structure. - RecursivelyUnsetSubcomponents(); - return true; - } + return base::ranges::all_of(Subcomponents(), [this](const auto* c) { + return GetValue().find(c->GetValue()) != base::string16::npos; + }); +} + +bool AddressComponent::WipeInvalidStructure() { + if (!IsStructureValid()) { + RecursivelyUnsetSubcomponents(); + return true; } return false; } -void AddressComponent::FormatValueFromSubcomponents() { +base::string16 AddressComponent::GetFormattedValueFromSubcomponents() { // Get the most suited format string. base::string16 format_string = GetBestFormatString(); @@ -536,8 +575,13 @@ void AddressComponent::FormatValueFromSubcomponents() { // with an empty value. base::string16 result = ReplacePlaceholderTypesWithValues(format_string); - result = base::CollapseWhitespace(result, /*trim_line_breaks=*/false); - SetValue(result, VerificationStatus::kFormatted); + return base::CollapseWhitespace(result, + /*trim_sequences_with_line_breaks=*/false); +} + +void AddressComponent::FormatValueFromSubcomponents() { + SetValue(GetFormattedValueFromSubcomponents(), + VerificationStatus::kFormatted); } base::string16 AddressComponent::ReplacePlaceholderTypesWithValues( @@ -761,9 +805,14 @@ bool AddressComponent::IsMergeableWithComponent( const base::string16 value_newer = newer_component.ValueForComparison(); // If both components are the same, there is nothing to do. - if (*this == newer_component) + if (SameAs(newer_component)) return true; + if (merge_mode_ & kUseNewerIfDifferent || + merge_mode_ & kUseBetterOrMostRecentIfDifferent) { + return true; + } + if ((merge_mode_ & kReplaceEmpty) && (value.empty() || value_newer.empty())) { return true; } @@ -791,12 +840,15 @@ bool AddressComponent::IsMergeableWithComponent( if ((merge_mode_ & kRecursivelyMergeSingleTokenSubset) && token_comparison_result.IsSingleTokenSuperset()) { - return true; + // This strategy is only applicable if also the unnormalized values have a + // single-token-superset relation. + SortedTokenComparisonResult unnormalized_token_comparison_result = + CompareSortedTokens(GetValue(), newer_component.GetValue()); + if (unnormalized_token_comparison_result.IsSingleTokenSuperset()) { + return true; + } } - if (merge_mode_ == kUseNewerIfDifferent) - return true; - // If the one value is a substring of the other, use the substring of the // corresponding mode is active. if ((merge_mode_ & kUseMostRecentSubstring) && @@ -810,7 +862,8 @@ bool AddressComponent::IsMergeableWithComponent( return true; } - if (merge_mode_ == kMergeChildrenAndReformat) { + // Checks if all child nodes are mergeable. + if (merge_mode_ & kMergeChildrenAndReformatIfNeeded) { bool is_mergeable = true; DCHECK(newer_component.subcomponents_.size() == subcomponents_.size()); for (size_t i = 0; i < newer_component.subcomponents_.size(); i++) { @@ -833,25 +886,28 @@ bool AddressComponent::MergeWithComponent( const base::string16 value = ValueForComparison(); const base::string16 value_newer = newer_component.ValueForComparison(); - - if (*this == newer_component) + if (SameAs(newer_component)) return true; // Now, it is guaranteed that both values are not identical. // Use the non empty one if the corresponding mode is active. if (merge_mode_ & kReplaceEmpty) { if (value.empty()) { - *this = newer_component; + // Only replace the value if the verification status is not kUserVerified. + if (GetVerificationStatus() != VerificationStatus::kUserVerified) { + CopyFrom(newer_component); + } return true; } - if (value_newer.empty()) + if (value_newer.empty()) { return true; + } } // If the normalized values are the same, optimize the verification status. if ((merge_mode_ & kUseBetterOrNewerForSameValue) && (value == value_newer)) { if (HasNewerValuePrecendenceInMerging(newer_component)) { - *this = newer_component; + CopyFrom(newer_component); } return true; } @@ -869,8 +925,11 @@ bool AddressComponent::MergeWithComponent( // Replace the subset with the superset if the corresponding mode is active. if ((merge_mode_ & kReplaceSubset) && token_comparison_result.OneIsSubset()) { - if (token_comparison_result.status == SUBSET) - *this = newer_component; + if (token_comparison_result.status == SUBSET && + !IsLessSignificantVerificationStatus( + newer_component.GetVerificationStatus(), GetVerificationStatus())) { + CopyFrom(newer_component); + } return true; } @@ -878,15 +937,18 @@ bool AddressComponent::MergeWithComponent( if ((merge_mode_ & kReplaceSuperset) && token_comparison_result.OneIsSubset()) { if (token_comparison_result.status == SUPERSET) - *this = newer_component; + CopyFrom(newer_component); return true; } // If the tokens are already equivalent, use the more recently used one. if ((merge_mode_ & (kReplaceSuperset | kReplaceSubset)) && token_comparison_result.status == MATCH) { - if (newer_was_more_recently_used) - *this = newer_component; + if (newer_was_more_recently_used && + !IsLessSignificantVerificationStatus( + newer_component.GetVerificationStatus(), GetVerificationStatus())) { + CopyFrom(newer_component); + } return true; } @@ -896,15 +958,20 @@ bool AddressComponent::MergeWithComponent( token_comparison_result.IsSingleTokenSuperset()) { // For the merging of subset token, the tokenization must be done without // prior normalization of the values. - SortedTokenComparisonResult token_comparison_result = + SortedTokenComparisonResult unnormalized_token_comparison_result = CompareSortedTokens(GetValue(), newer_component.GetValue()); - return MergeSubsetComponent(newer_component, token_comparison_result); + // The merging strategy can only be applied when the comparison of the + // unnormalized tokens still yields a single token superset. + if (unnormalized_token_comparison_result.IsSingleTokenSuperset()) { + return MergeSubsetComponent(newer_component, + unnormalized_token_comparison_result); + } } // Replace the older value with the newer one if the corresponding mode is // active. if (merge_mode_ & kUseNewerIfDifferent) { - *this = newer_component; + CopyFrom(newer_component); return true; } @@ -913,21 +980,34 @@ bool AddressComponent::MergeWithComponent( if ((merge_mode_ & kUseMostRecentSubstring) && (value.find(value_newer) != base::string16::npos || value_newer.find(value) != base::string16::npos)) { - if (newer_was_more_recently_used) - *this = newer_component; + if (newer_was_more_recently_used && + !IsLessSignificantVerificationStatus( + newer_component.GetVerificationStatus(), GetVerificationStatus())) + CopyFrom(newer_component); return true; } if ((merge_mode_ & kPickShorterIfOneContainsTheOther) && token_comparison_result.ContainEachOther()) { - if (newer_component.GetValue().size() <= GetValue().size()) - *this = newer_component; + if (newer_component.GetValue().size() <= GetValue().size() && + !IsLessSignificantVerificationStatus( + newer_component.GetVerificationStatus(), GetVerificationStatus())) { + CopyFrom(newer_component); + } + return true; + } + + if (merge_mode_ & kUseBetterOrMostRecentIfDifferent) { + if (HasNewerValuePrecendenceInMerging(newer_component)) { + SetValue(newer_component.GetValue(), + newer_component.GetVerificationStatus()); + } return true; } // If the corresponding mode is active, ignore this mode and pair-wise merge // the child tokens. Reformat this nodes from its children after the merge. - if (merge_mode_ & kMergeChildrenAndReformat) { + if (merge_mode_ & kMergeChildrenAndReformatIfNeeded) { DCHECK(newer_component.subcomponents_.size() == subcomponents_.size()); for (size_t i = 0; i < newer_component.subcomponents_.size(); i++) { bool success = subcomponents_[i]->MergeWithComponent( @@ -935,9 +1015,37 @@ bool AddressComponent::MergeWithComponent( if (!success) return false; } - FormatValueFromSubcomponents(); + // If the two values are already token equivalent, use the value of the + // component with the better verification status, or if both are the same, + // use the newer one. + if (token_comparison_result.TokensMatch()) { + if (HasNewerValuePrecendenceInMerging(newer_component)) { + SetValue(newer_component.GetValue(), + newer_component.GetVerificationStatus()); + } + } else { + // Otherwise do a reformat from the subcomponents. + base::string16 formatted_value = GetFormattedValueFromSubcomponents(); + // If the current value is maintained, keep the more significant + // verification status. + if (formatted_value == GetValue()) { + SetValue(formatted_value, + GetMoreSignificantVerificationStatus( + VerificationStatus::kFormatted, GetVerificationStatus())); + } else if (formatted_value == newer_component.GetValue()) { + // Otherwise test if the value is the same as the one of + // |newer_component|. If yes, maintain the better verification status. + SetValue(formatted_value, GetMoreSignificantVerificationStatus( + VerificationStatus::kFormatted, + newer_component.GetVerificationStatus())); + } else { + // In all other cases, set the formatted_value. + SetValue(formatted_value, VerificationStatus::kFormatted); + } + } return true; } + return false; } @@ -972,11 +1080,30 @@ bool AddressComponent::MergeTokenEquivalentComponent( // this component or the other depending on which substructure is better in // terms of the number of validated tokens. + const std::vector<AddressComponent*> other_subcomponents = + newer_component.Subcomponents(); + DCHECK(subcomponents_.size() == other_subcomponents.size()); + if (HasNewerValuePrecendenceInMerging(newer_component)) { SetValue(newer_component.GetValue(), newer_component.GetVerificationStatus()); } + if (IsAtomic()) + return true; + + // If the other component has subtree, just keep this one. + if (newer_component.AllDescendantsAreEmpty()) { + return true; + } else if (AllDescendantsAreEmpty()) { + // Otherwise, replace this subtree with the other one if this subtree is + // empty. + for (size_t i = 0; i < subcomponents_.size(); ++i) { + subcomponents_[i]->CopyFrom(*other_subcomponents[i]); + } + return true; + } + // Now, the substructure of the node must be merged. There are three cases: // // * All nodes of the substructure are pairwise mergeable. In this case it @@ -1001,11 +1128,6 @@ bool AddressComponent::MergeTokenEquivalentComponent( // components. By favoring the other component in a tie, the most recently // used structure wins. - const std::vector<AddressComponent*> other_subcomponents = - newer_component.Subcomponents(); - - DCHECK(subcomponents_.size() == other_subcomponents.size()); - int this_component_verification_score = 0; int newer_component_verification_score = 0; @@ -1033,10 +1155,10 @@ bool AddressComponent::MergeTokenEquivalentComponent( // component is equal or larger than the score of this component, use its // subcomponents including their substructure for all unmerged components. if (newer_component_verification_score >= this_component_verification_score) { - for (size_t i : unmerged_indices) - *subcomponents_[i] = *other_subcomponents[i]; + for (size_t i : unmerged_indices) { + subcomponents_[i]->CopyFrom(*other_subcomponents[i]); + } } - return true; } @@ -1136,7 +1258,7 @@ bool AddressComponent::MergeSubsetComponent( // subcomponents including their substructure for all unmerged components. if (newer_component_verification_score >= this_component_verification_score) { for (size_t i : unmerged_indices) - *subcomponents_[i] = *subset_subcomponents[i]; + subcomponents_[i]->CopyFrom(*subset_subcomponents[i]); if (!found_subset_component) this->ConsumeAdditionalToken(token_to_consume); diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component.h b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component.h index 7adba19a86e..8b7f5345471 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component.h @@ -41,12 +41,26 @@ enum class VerificationStatus { kServerParsed = 5, }; +// Prints the string representation of |status| to |os|. +std::ostream& operator<<(std::ostream& os, VerificationStatus status); + // Returns true if |left| has a less significant verification status compared to // |right|. bool IsLessSignificantVerificationStatus(VerificationStatus left, VerificationStatus right); +// Returns the more significant verification status according to +// |IsLessSignificantVerificationStatus|. +VerificationStatus GetMoreSignificantVerificationStatus( + VerificationStatus left, + VerificationStatus right); + // The merge mode defines if and how two components are merged. +// The merge operations are applied in the order defined here. +// If one merge operation succeeds, the subsequent ones are not tested. +// Therefore, if |KUseBetterOrMoreRecentIfDifferent| is active, +// |kMergeChildrenAndReformatIfNeeded| will not be applied because +// |kUseBetterOrMostRecentIfDifferent| is always applicable. enum MergeMode { // If one component has an empty value, use the non-empty one. kReplaceEmpty = 1, @@ -65,13 +79,17 @@ enum MergeMode { // If the newer component contains one token more, apply a recursive strategy // to merge the tokens. kRecursivelyMergeSingleTokenSubset = 1 << 6, - // If one is a substring use the most recent one. + // If one is a substring of the other use the most recent one. kUseMostRecentSubstring = 1 << 7, - // Merge the child nodes and reformat the node from its children after merge. - kMergeChildrenAndReformat = 1 << 8, - // If the tokens match or one is a subset of the other, pick the shorter one. - kPickShorterIfOneContainsTheOther = 1 << 9, + // Merge the child nodes and reformat the node from its children after merge + // if the value has changed. + kPickShorterIfOneContainsTheOther = 1 << 8, + // If the normalized values are different, use the better one in terms + // of verification score or the most recent one if both scores are the same. + kUseBetterOrMostRecentIfDifferent = 1 << 9, // Defines the default merging behavior. + kMergeChildrenAndReformatIfNeeded = 1 << 10, + // If the tokens match or one is a subset of the other, pick the shorter one. kDefault = kRecursivelyMergeTokenEquivalentValues }; @@ -119,25 +137,31 @@ class AddressComponent { // Constructor for a compound child node. AddressComponent(ServerFieldType storage_type, AddressComponent* parent, - std::vector<AddressComponent*> subcomponents, unsigned int merge_mode); - // Disallows copies since they are not needed in the current Autofill design. + // Disallows copies and direct assignments since they are not needed in the + // current Autofill design. AddressComponent(const AddressComponent& other) = delete; + AddressComponent& operator=(const AddressComponent& right) = delete; virtual ~AddressComponent(); - // Assignment operator that works recursively down the tree and assigns the - // |value_| and |verification_status_| of every node in right to the - // corresponding nodes in |this|. For an assignment it is required that both - // nodes have the same |storage_type_|. - AddressComponent& operator=(const AddressComponent& right); + // Migrates from a legacy structure in which tokens are imported without + // a status. + virtual void MigrateLegacyStructure(bool is_verified_profile) {} + + // Comparison operators are deleted in favor of and |SameAs()|. + bool operator==(const AddressComponent& right) const = delete; + bool operator!=(const AddressComponent& right) const = delete; - // Comparison operator that works recursively down the tree. - bool operator==(const AddressComponent& right) const; + // Compares the values and verification statuses with |other| recursively down + // the tree. Returns true iff all values and verification statuses of this + // node and its subtree and |other| with its subtree are the same. + bool SameAs(const AddressComponent& other) const; - // Inequality operator that works recursively down the tree. - bool operator!=(const AddressComponent& right) const; + // Copies the values and verification statuses from |other| recursively down + // the tree. + void CopyFrom(const AddressComponent& other); // Returns the autofill storage type stored in |storage_type_|. ServerFieldType GetStorageType() const; @@ -341,11 +365,18 @@ class AddressComponent { bool* validity_status, bool wipe_if_not = false); - // Deletes the stored structure if it contains strings that are not a - // substring of the unstructured representation. - // Return true if a wipe operation was performed. + // Deletes the stored structure and returns true if |IsStructureValid()| + // returns false. virtual bool WipeInvalidStructure(); + // Returns if the structure in the tree below this node is valid. A structure + // becomes invalid when it contains information that is not contained in the + // value of this node. + bool IsStructureValid() const; + + // Returns true if all values of all descendent nodes are empty. + bool AllDescendantsAreEmpty() const; + #ifdef UNIT_TEST // Initiates the formatting of the values from the subcomponents. void FormatValueFromSubcomponentsForTesting() { @@ -381,6 +412,10 @@ class AddressComponent { // Sets the merge mode for testing purposes. void SetMergeModeForTesting(int merge_mode) { merge_mode_ = merge_mode; } + // Returns the value used for comparison for testing purposes. + base::string16 ValueForComparisonForTesting() const { + return ValueForComparison(); + } #endif protected: @@ -480,16 +515,28 @@ class AddressComponent { const base::string16& value, const re2::RE2* parse_expression); + // Determines and sets a formatted value using + // |GetFormattedValueFromSubcomponents|. + void FormatValueFromSubcomponents(); + + // Returns the maximum number of components with assigned values on the path + // from the component to a leaf node. + int MaximumNumberOfAssignedAddressComponentsOnNodeToLeafPaths() const; + private: + // Function to be called by child nodes on construction to register + // themselves as child nodes. + void RegisterChildNode(AddressComponent* child); + // Unsets the node and all of its children. void UnsetAddressComponentAndItsSubcomponents(); // Unsets the children of a node. void UnsetSubcomponents(); - // Determines the |value_| from the values of the subcomponents by using the + // Determines a value from the subcomponents by using the // most suitable format string determined by |GetBestFormatString()|. - void FormatValueFromSubcomponents(); + base::string16 GetFormattedValueFromSubcomponents(); // Replaces placeholder values with the corresponding values. base::string16 ReplacePlaceholderTypesWithValues( @@ -504,10 +551,6 @@ class AddressComponent { // of the subcomponents. Returns true on success and is allowed to fail. bool ParseValueAndAssignSubcomponentsByRegularExpressions(); - // Returns the maximum number of components with assigned values on the path - // from the component to a leaf node. - int MaximumNumberOfAssignedAddressComponentsOnNodeToLeafPaths() const; - // The unstructured value of this component. base::Optional<base::string16> value_; diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc index a9356b83cd8..029c510ea41 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc @@ -30,7 +30,7 @@ class TestAtomicFirstNameAddressComponent : public AddressComponent { TestAtomicFirstNameAddressComponent() : TestAtomicFirstNameAddressComponent(nullptr) {} explicit TestAtomicFirstNameAddressComponent(AddressComponent* parent) - : AddressComponent(NAME_FIRST, parent, {}, MergeMode::kDefault) {} + : AddressComponent(NAME_FIRST, parent, MergeMode::kDefault) {} }; class TestAtomicMiddleNameAddressComponent : public AddressComponent { @@ -38,7 +38,7 @@ class TestAtomicMiddleNameAddressComponent : public AddressComponent { TestAtomicMiddleNameAddressComponent() : TestAtomicMiddleNameAddressComponent(nullptr) {} explicit TestAtomicMiddleNameAddressComponent(AddressComponent* parent) - : AddressComponent(NAME_MIDDLE, parent, {}, MergeMode::kDefault) {} + : AddressComponent(NAME_MIDDLE, parent, MergeMode::kDefault) {} void GetAdditionalSupportedFieldTypes( ServerFieldTypeSet* supported_types) const override { @@ -51,7 +51,8 @@ class TestAtomicMiddleNameAddressComponent : public AddressComponent { const std::string& field_type_name, const base::string16& value, const VerificationStatus& status) override { - if (field_type_name == AutofillType(NAME_MIDDLE_INITIAL).ToString()) { + if (field_type_name == + AutofillType::ServerFieldTypeToString(NAME_MIDDLE_INITIAL)) { SetValue(value, status); return true; } @@ -61,7 +62,8 @@ class TestAtomicMiddleNameAddressComponent : public AddressComponent { bool ConvertAndGetTheValueForAdditionalFieldTypeName( const std::string& field_type_name, base::string16* value) const override { - if (field_type_name == AutofillType(NAME_MIDDLE_INITIAL).ToString()) { + if (field_type_name == + AutofillType::ServerFieldTypeToString(NAME_MIDDLE_INITIAL)) { if (value) { *value = GetValue().substr(0, 1); } @@ -76,7 +78,7 @@ class TestAtomicLastNameAddressComponent : public AddressComponent { TestAtomicLastNameAddressComponent() : TestAtomicLastNameAddressComponent(nullptr) {} explicit TestAtomicLastNameAddressComponent(AddressComponent* parent) - : AddressComponent(NAME_LAST, parent, {}, MergeMode::kDefault) {} + : AddressComponent(NAME_LAST, parent, MergeMode::kDefault) {} }; // Creates a compound name for testing purposes. @@ -87,7 +89,6 @@ class TestCompoundNameAddressComponent : public AddressComponent { explicit TestCompoundNameAddressComponent(AddressComponent* parent) : AddressComponent(NAME_FULL, parent, - {&first_name_, &middle_name_, &last_name_}, MergeMode::kDefault) {} AddressComponent* GetFirstNameSubComponentForTesting() { @@ -109,7 +110,6 @@ class TestCompoundNameMethodParsedAddressComponent : public AddressComponent { AddressComponent* parent) : AddressComponent(NAME_FULL, parent, - {&first_name_, &middle_name_, &last_name_}, MergeMode::kDefault) {} bool ParseValueAndAssignSubcomponentsByMethod() override { @@ -137,7 +137,6 @@ class TestCompoundNameRegExParsedAddressComponent : public AddressComponent { explicit TestCompoundNameRegExParsedAddressComponent(AddressComponent* parent) : AddressComponent(NAME_FULL, parent, - {&first_name_, &middle_name_, &last_name_}, MergeMode::kDefault) {} std::vector<const RE2*> GetParseRegularExpressionsByRelevance() @@ -164,7 +163,6 @@ class TestCompoundNameCustomFormatAddressComponent : public AddressComponent { AddressComponent* parent) : AddressComponent(NAME_FULL, parent, - {&first_name, &middle_name, &last_name}, MergeMode::kDefault) {} // Introduces a custom format with a leading last name. @@ -188,7 +186,6 @@ class TestCompoundNameCustomAffixedFormatAddressComponent AddressComponent* parent) : AddressComponent(NAME_FULL, parent, - {&first_name, &middle_name, &last_name}, MergeMode::kDefault) {} // Introduces a custom format with a leading last name. @@ -213,7 +210,6 @@ class TestCompoundNameCustomFormatWithUnsupportedTokenAddressComponent AddressComponent* parent) : AddressComponent(NAME_FULL, parent, - {&first_name, &middle_name, &last_name}, MergeMode::kDefault) {} // Introduce a custom format with a leading last name. @@ -234,7 +230,6 @@ class TestAtomicTitleAddressComponent : public AddressComponent { explicit TestAtomicTitleAddressComponent(AddressComponent* parent) : AddressComponent(NAME_HONORIFIC_PREFIX, parent, - {}, MergeMode::kDefault) {} }; @@ -246,7 +241,6 @@ class TestCompoundNameWithTitleAddressComponent : public AddressComponent { explicit TestCompoundNameWithTitleAddressComponent(AddressComponent* parent) : AddressComponent(CREDIT_CARD_NAME_FULL, parent, - {&title, &full_name}, MergeMode::kDefault) {} private: @@ -263,13 +257,34 @@ class TestNonProperFirstNameAddressComponent : public AddressComponent { explicit TestNonProperFirstNameAddressComponent(AddressComponent* parent) : AddressComponent(NAME_FIRST, parent, - {&second_name_first_node_}, MergeMode::kDefault) {} private: - TestAtomicFirstNameAddressComponent second_name_first_node_; + TestAtomicFirstNameAddressComponent second_name_first_node_{this}; }; +// Tests the merging of two atomic component with |type|, and vales +// |older_values| and |newer_values| respectively, and |merge_modes|. +// If |is_mergeable| it is expected that the two components are mergeable. +// If |newer_was_more_recently_used| the newer component was also more recently +// used which is true by default. +void TestAtomMerging(ServerFieldType type, + AddressComponentTestValues older_values, + AddressComponentTestValues newer_values, + AddressComponentTestValues merge_expectation, + bool is_mergeable, + int merge_modes, + bool newer_was_more_recently_used = true) { + AddressComponent older(type, nullptr, merge_modes); + AddressComponent newer(type, nullptr, merge_modes); + + SetTestValues(&older, older_values); + SetTestValues(&newer, newer_values); + + TestMerging(&older, &newer, merge_expectation, is_mergeable, merge_modes, + newer_was_more_recently_used); +} + void TestCompoundNameMerging(AddressComponentTestValues older_values, AddressComponentTestValues newer_values, AddressComponentTestValues merge_expectation, @@ -289,7 +304,7 @@ void TestCompoundNameMerging(AddressComponentTestValues older_values, // Tests that the destructor does not crash TEST(AutofillStructuredAddressAddressComponent, ConstructAndDestruct) { AddressComponent* component = - new AddressComponent(NAME_FULL, nullptr, {}, MergeMode::kDefault); + new AddressComponent(NAME_FULL, nullptr, MergeMode::kDefault); delete component; EXPECT_TRUE(true); } @@ -383,9 +398,9 @@ TEST(AutofillStructuredAddressAddressComponent, TestGetSupportedTypes) { } // Tests the comparison of thw atoms of the same type. -TEST(AutofillStructuredAddressAddressComponent, TestComparisonOperator_Atom) { - AddressComponent left(NAME_FIRST, nullptr, {}, MergeMode::kReplaceEmpty); - AddressComponent right(NAME_FIRST, nullptr, {}, MergeMode::kReplaceEmpty); +TEST(AutofillStructuredAddressAddressComponent, TestComparison_Atom) { + AddressComponent left(NAME_FIRST, nullptr, MergeMode::kReplaceEmpty); + AddressComponent right(NAME_FIRST, nullptr, MergeMode::kReplaceEmpty); left.SetValue(UTF8ToUTF16("some value"), VerificationStatus::kParsed); right.SetValue(UTF8ToUTF16("some other value"), @@ -393,37 +408,34 @@ TEST(AutofillStructuredAddressAddressComponent, TestComparisonOperator_Atom) { EXPECT_NE(left.GetValue(), right.GetValue()); EXPECT_NE(left.GetVerificationStatus(), right.GetVerificationStatus()); - EXPECT_FALSE(left == right); - EXPECT_TRUE(left != right); + EXPECT_FALSE(left.SameAs(right)); right.SetValue(UTF8ToUTF16("some value"), VerificationStatus::kParsed); - EXPECT_TRUE(left == right); - EXPECT_FALSE(left != right); + EXPECT_TRUE(left.SameAs(right)); } // Tests comparison of two different types. TEST(AutofillStructuredAddressAddressComponent, TestComparisonOperator_DifferentTypes) { - AddressComponent type_a1(NAME_FIRST, nullptr, {}, MergeMode::kReplaceEmpty); - AddressComponent type_a2(NAME_FIRST, nullptr, {}, MergeMode::kReplaceEmpty); - AddressComponent type_b(NAME_LAST, nullptr, {}, MergeMode::kReplaceEmpty); + AddressComponent type_a1(NAME_FIRST, nullptr, MergeMode::kReplaceEmpty); + AddressComponent type_a2(NAME_FIRST, nullptr, MergeMode::kReplaceEmpty); + AddressComponent type_b(NAME_LAST, nullptr, MergeMode::kReplaceEmpty); - EXPECT_TRUE(type_a1 == type_a2); - EXPECT_FALSE(type_a1 == type_b); + EXPECT_TRUE(type_a1.SameAs(type_a2)); + EXPECT_FALSE(type_a1.SameAs(type_b)); } // Tests the comparison with itself. TEST(AutofillStructuredAddressAddressComponent, TestComparisonOperator_SelfComparison) { - AddressComponent type_a(NAME_FIRST, nullptr, {}, MergeMode::kReplaceEmpty); + AddressComponent type_a(NAME_FIRST, nullptr, MergeMode::kReplaceEmpty); - EXPECT_TRUE(type_a == type_a); + EXPECT_TRUE(type_a.SameAs(type_a)); } // Tests the comparison operator. -TEST(AutofillStructuredAddressAddressComponent, - TestComparisonOperator_Compound) { +TEST(AutofillStructuredAddressAddressComponent, TestComparison_Compound) { TestCompoundNameAddressComponent left; TestCompoundNameAddressComponent right; @@ -461,8 +473,7 @@ TEST(AutofillStructuredAddressAddressComponent, EXPECT_EQ(right.GetVerificationStatusForType(NAME_MIDDLE), VerificationStatus::kParsed); - EXPECT_FALSE(left == right); - EXPECT_TRUE(left != right); + EXPECT_FALSE(left.SameAs(right)); // Set left to the same values as right and verify that it is now equal. TestCompoundNameAddressComponent same_right; @@ -471,29 +482,51 @@ TEST(AutofillStructuredAddressAddressComponent, VerificationStatus::kUserVerified); EXPECT_TRUE(same_right.CompleteFullTree()); - EXPECT_TRUE(right == same_right); - EXPECT_FALSE(right != same_right); + EXPECT_TRUE(right.SameAs(same_right)); // Change one subcomponent and verify that it is not equal anymore. same_right.SetValueForTypeIfPossible(NAME_LAST, UTF8ToUTF16("Joker"), VerificationStatus::kParsed); - EXPECT_TRUE(right != same_right); - EXPECT_FALSE(right == same_right); + EXPECT_FALSE(right.SameAs(same_right)); } // Tests the assignment operator. TEST(AutofillStructuredAddressAddressComponent, TestAssignmentOperator_Atom) { - AddressComponent left(NAME_FIRST, nullptr, {}, MergeMode::kReplaceEmpty); - AddressComponent right(NAME_FIRST, nullptr, {}, MergeMode::kReplaceEmpty); + AddressComponent left(NAME_FIRST, nullptr, MergeMode::kReplaceEmpty); + AddressComponent right(NAME_FIRST, nullptr, MergeMode::kReplaceEmpty); left.SetValue(UTF8ToUTF16("some value"), VerificationStatus::kParsed); right.SetValue(UTF8ToUTF16("some other value"), VerificationStatus::kFormatted); - EXPECT_FALSE(left == right); + EXPECT_FALSE(left.SameAs(right)); left.SetValue(UTF8ToUTF16("some other value"), VerificationStatus::kFormatted); - EXPECT_TRUE(left == right); + EXPECT_TRUE(left.SameAs(right)); +} + +// Tests the assignment operator when using the base class type. +TEST(AutofillStructuredAddressAddressComponent, + TestAssignmentOperator_Compound_FromBase) { + TestCompoundNameAddressComponent left; + TestCompoundNameAddressComponent right; + + left.SetValueForTypeIfPossible(NAME_FULL, UTF8ToUTF16("First Middle Last"), + VerificationStatus::kObserved); + left.RecursivelyCompleteTree(); + + right.SetValueForTypeIfPossible(NAME_FULL, UTF8ToUTF16("The Dark Knight"), + VerificationStatus::kParsed); + right.RecursivelyCompleteTree(); + + AddressComponent* left_base = &left; + AddressComponent* right_base = &right; + EXPECT_FALSE(left_base->SameAs(*right_base)); + + // Use the assignment operators defined in the base. + left_base->CopyFrom(*right_base); + // But verify that the higher level classes are assigned correctly. + EXPECT_TRUE(left.SameAs(right)); } // Tests the assignment operator on a compound node. @@ -510,10 +543,10 @@ TEST(AutofillStructuredAddressAddressComponent, VerificationStatus::kParsed); right.RecursivelyCompleteTree(); - EXPECT_FALSE(left == right); + EXPECT_FALSE(left.SameAs(right)); - left = right; - EXPECT_TRUE(left == right); + left.CopyFrom(right); + EXPECT_TRUE(left.SameAs(right)); } // Tests that self-assignment does not break things. @@ -522,7 +555,7 @@ TEST(AutofillStructuredAddressAddressComponent, SelfAssignment) { left.SetValueForTypeIfPossible(NAME_FULL, UTF8ToUTF16("First Middle Last"), VerificationStatus::kObserved); - left = *(&left); + left.CopyFrom(*(&left)); EXPECT_EQ(left.GetValueForType(NAME_FULL), UTF8ToUTF16("First Middle Last")); } @@ -1398,7 +1431,7 @@ TEST(AutofillStructuredAddressAddressComponent, MergePermutatedComponent) { VerificationStatus::kObserved)); TestCompoundNameAddressComponent copy_of_one; - copy_of_one = one; + copy_of_one.CopyFrom(one); EXPECT_TRUE(one.MergeWithComponent(two)); // As a result of the merging, the unstructured representation should be @@ -1577,7 +1610,7 @@ TEST(AutofillStructuredAddressAddressComponent, MergeChildsAndReformatRoot) { // Set the root node to merging mode which only merges the children and gets // reformatted afterwards. - older.SetMergeModeForTesting(MergeMode::kMergeChildrenAndReformat); + older.SetMergeModeForTesting(MergeMode::kMergeChildrenAndReformatIfNeeded); // Set the merge modes of the children to replace empty values and use // supersets. for (auto* subcomponent : older.Subcomponents()) { @@ -1661,7 +1694,7 @@ TEST(AutofillStructuredAddressAddressComponent, MergeChildsAndReformatRoot) { VerifyTestValues(&older, older_values); } -// Test the comparison of different Verification statuses. +// Tests the comparison of different Verification statuses. TEST(AutofillStructuredAddressAddressComponent, TestIsLessSignificantVerificationStatus) { EXPECT_TRUE(IsLessSignificantVerificationStatus( @@ -1682,5 +1715,64 @@ TEST(AutofillStructuredAddressAddressComponent, VerificationStatus::kUserVerified, VerificationStatus::kServerParsed)); } +// Tests gettings the more significant VerificationStatus. +TEST(AutofillStructuredAddressAddressComponent, + GetMoreSignificantVerificationStatus) { + EXPECT_EQ(VerificationStatus::kFormatted, + GetMoreSignificantVerificationStatus(VerificationStatus::kFormatted, + VerificationStatus::kParsed)); + EXPECT_EQ(VerificationStatus::kObserved, + GetMoreSignificantVerificationStatus( + VerificationStatus::kFormatted, VerificationStatus::kObserved)); + EXPECT_EQ( + VerificationStatus::kUserVerified, + GetMoreSignificantVerificationStatus(VerificationStatus::kUserVerified, + VerificationStatus::kUserVerified)); +} + +// Tests merging using the Mermode::KUseBetterOrMoreRecentIfDifferent| +TEST(AutofillStructuredAddressAddressComponent, + TestUseBetterOfMoreRecentIfDifferentMergeStrategy) { + AddressComponentTestValues old_values = { + {.type = NAME_FIRST, + .value = "first value", + .status = VerificationStatus::kObserved}}; + AddressComponentTestValues newer_values = { + {.type = NAME_FIRST, + .value = "second value", + .status = VerificationStatus::kObserved}}; + AddressComponentTestValues better_values = { + {.type = NAME_FIRST, + .value = "second value", + .status = VerificationStatus::kUserVerified}}; + AddressComponentTestValues not_better_values = { + {.type = NAME_FIRST, + .value = "second value", + .status = VerificationStatus::kParsed}}; + + // Test that the newer values are used. + TestAtomMerging(NAME_FIRST, old_values, newer_values, newer_values, + /*is_mergable=*/true, + MergeMode::kUseBetterOrMostRecentIfDifferent); + + // Test that the better values are used. + TestAtomMerging(NAME_FIRST, old_values, better_values, better_values, + /*is_mergable=*/true, + MergeMode::kUseBetterOrMostRecentIfDifferent); + // Should work equally in both directions. + TestAtomMerging(NAME_FIRST, better_values, old_values, better_values, + /*is_mergable=*/true, + MergeMode::kUseBetterOrMostRecentIfDifferent); + + // Test that the not better values are not used. + TestAtomMerging(NAME_FIRST, old_values, not_better_values, old_values, + /*is_mergable=*/true, + MergeMode::kUseBetterOrMostRecentIfDifferent); + // Should work equally in both directions. + TestAtomMerging(NAME_FIRST, not_better_values, old_values, old_values, + /*is_mergable=*/true, + MergeMode::kUseBetterOrMostRecentIfDifferent); +} + } // namespace structured_address } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.cc b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.cc index e544cc54c78..dfc86617acc 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.cc @@ -40,18 +40,17 @@ base::string16 ReduceToInitials(const base::string16& value) { } NameHonorific::NameHonorific(AddressComponent* parent) - : AddressComponent(NAME_HONORIFIC_PREFIX, parent, {}, MergeMode::kDefault) { -} + : AddressComponent(NAME_HONORIFIC_PREFIX, parent, MergeMode::kDefault) {} NameHonorific::~NameHonorific() = default; NameFirst::NameFirst(AddressComponent* parent) - : AddressComponent(NAME_FIRST, parent, {}, MergeMode::kDefault) {} + : AddressComponent(NAME_FIRST, parent, MergeMode::kDefault) {} NameFirst::~NameFirst() = default; NameMiddle::NameMiddle(AddressComponent* parent) - : AddressComponent(NAME_MIDDLE, parent, {}, MergeMode::kDefault) {} + : AddressComponent(NAME_MIDDLE, parent, MergeMode::kDefault) {} NameMiddle::~NameMiddle() = default; @@ -63,7 +62,7 @@ void NameMiddle::GetAdditionalSupportedFieldTypes( bool NameMiddle::ConvertAndGetTheValueForAdditionalFieldTypeName( const std::string& type_name, base::string16* value) const { - if (type_name == AutofillType(NAME_MIDDLE_INITIAL).ToString()) { + if (type_name == AutofillType::ServerFieldTypeToString(NAME_MIDDLE_INITIAL)) { if (value) { // If the stored value has the characteristics of containing only // initials, use the value as it is. Otherwise, convert it to a @@ -84,7 +83,7 @@ bool NameMiddle::ConvertAndSetValueForAdditionalFieldTypeName( const std::string& type_name, const base::string16& value, const VerificationStatus& status) { - if (type_name == AutofillType(NAME_MIDDLE_INITIAL).ToString()) { + if (type_name == AutofillType::ServerFieldTypeToString(NAME_MIDDLE_INITIAL)) { SetValue(value, status); return true; } @@ -92,13 +91,12 @@ bool NameMiddle::ConvertAndSetValueForAdditionalFieldTypeName( } NameLastFirst::NameLastFirst(AddressComponent* parent) - : AddressComponent(NAME_LAST_FIRST, parent, {}, MergeMode::kDefault) {} + : AddressComponent(NAME_LAST_FIRST, parent, MergeMode::kDefault) {} NameLastFirst::~NameLastFirst() = default; NameLastConjunction::NameLastConjunction(AddressComponent* parent) - : AddressComponent(NAME_LAST_CONJUNCTION, parent, {}, MergeMode::kDefault) { -} + : AddressComponent(NAME_LAST_CONJUNCTION, parent, MergeMode::kDefault) {} NameLastConjunction::~NameLastConjunction() = default; @@ -113,14 +111,13 @@ std::vector<const re2::RE2*> NameLast::GetParseRegularExpressionsByRelevance() } NameLastSecond::NameLastSecond(AddressComponent* parent) - : AddressComponent(NAME_LAST_SECOND, parent, {}, MergeMode::kDefault) {} + : AddressComponent(NAME_LAST_SECOND, parent, MergeMode::kDefault) {} NameLastSecond::~NameLastSecond() = default; NameLast::NameLast(AddressComponent* parent) : AddressComponent(NAME_LAST, parent, - {&first_, &conjunction_, &second_}, MergeMode::kDefault) {} NameLast::~NameLast() = default; @@ -137,16 +134,24 @@ NameFull::NameFull(AddressComponent* parent) : AddressComponent( NAME_FULL, parent, - {/*&name_honorific_,*/ &name_first_, &name_middle_, &name_last_}, MergeMode::kDefault) {} NameFull::NameFull(const NameFull& other) : NameFull() { // The purpose of the copy operator is to copy the values and verification // statuses of all nodes in |other| to |this|. This exact functionality is - // already implemented in the assignment operator. - *this = other; + // already implemented as a recursive operation in the base class. + this->CopyFrom(other); } +NameHonorificPrefix::NameHonorificPrefix(AddressComponent* parent) + : AddressComponent(NAME_HONORIFIC_PREFIX, + parent, + MergeMode::kUseBetterOrNewerForSameValue | + MergeMode::kReplaceEmpty | + MergeMode::kUseBetterOrMostRecentIfDifferent) {} + +NameHonorificPrefix::~NameHonorificPrefix() = default; + void NameFull::MigrateLegacyStructure(bool is_verified_profile) { // Only if the name was imported from a legacy structure, the component has no if (GetVerificationStatus() != VerificationStatus::kNoStatus) @@ -235,14 +240,52 @@ base::string16 NameFull::GetBestFormatString() const { HasCjkNameCharacteristics(base::UTF16ToUTF8(name_last_.GetValue()))) { return base::ASCIIToUTF16("${NAME_LAST}${NAME_FIRST}"); } - // TODO(crbug.com/1113617): Honorifics are temporally disabled. return - // base::ASCIIToUTF16( "${NAME_HONORIFIC_PREFIX} ${NAME_FIRST} - // ${NAME_MIDDLE} ${NAME_LAST}"); base::ASCIIToUTF16("${NAME_FIRST} ${NAME_MIDDLE} ${NAME_LAST}"); } NameFull::~NameFull() = default; +NameFullWithPrefix::NameFullWithPrefix() : NameFullWithPrefix(nullptr) {} + +NameFullWithPrefix::NameFullWithPrefix(AddressComponent* parent) + : AddressComponent(NAME_FULL_WITH_HONORIFIC_PREFIX, + parent, + MergeMode::kMergeChildrenAndReformatIfNeeded) {} + +NameFullWithPrefix::NameFullWithPrefix(const NameFullWithPrefix& other) + : NameFullWithPrefix() { + // The purpose of the copy operator is to copy the values and verification + // statuses of all nodes in |other| to |this|. This exact functionality is + // already implemented as a recursive operation in the base class. + this->CopyFrom(other); +} + +NameFullWithPrefix::~NameFullWithPrefix() = default; + +std::vector<const re2::RE2*> +NameFullWithPrefix::GetParseRegularExpressionsByRelevance() const { + auto* pattern_provider = StructuredAddressesRegExProvider::Instance(); + return {pattern_provider->GetRegEx(RegEx::kParsePrefixedName)}; +} + +void NameFullWithPrefix::MigrateLegacyStructure(bool is_verified_profile) { + // If a verification status is set, the structure is already migrated. + if (GetVerificationStatus() != VerificationStatus::kNoStatus) { + return; + } + + // If it is not migrated, continue with migrating the full name. + name_full_.MigrateLegacyStructure(is_verified_profile); + + // Check if the tree is already in a completed state. + // If yes, build the root node from the subcomponents. + // Otherwise, this step is not necessary and will be taken care of in a later + // stage of the import process. + if (MaximumNumberOfAssignedAddressComponentsOnNodeToLeafPaths() > 1) { + FormatValueFromSubcomponents(); + } +} + } // namespace structured_address } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.h b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.h index 67dd210ada0..db4e70137d6 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name.h @@ -114,23 +114,22 @@ class NameLast : public AddressComponent { // Compound that represents a full name. It contains a honorific, a first // name, a middle name and a last name. The last name is a compound itself. // -// +----------+ -// | NAME_FULL| -// +----------+ -// / | | \ -// / | | \ -// / | | \ -// / | | \ -// +------------+ +--------+ +---------+ +-------+ -// | _HONORIFIC | | _FIRST | | _MIDDLE | | _LAST | -// +------------+ +--------+ +---------+ +-------+ -// / | \ -// / | \ +// +------------+ +// | NAME_FULL | +// +------------+ +// / | \ +// / | \ +// / | \ +// +------------+ +-------------+ +-----------+ +// | NAME_FIRST | | NAME_MIDDLE | | NAME_LAST | +// +------------+ +-------------+ +-----------+ +// / | \ // / | \ +// / | \ // / | \ -// +--------+ +-----------+ +---------+ -// | _FIRST | | _CONJUNC. | | _SECOND | -// +--------+ +-----------+ +---------+ +// +--------+ +--------------+ +---------+ +// | _FIRST | | _CONJUNCTION | | _SECOND | +// +--------+ +--------------+ +---------+ // class NameFull : public AddressComponent { public: @@ -139,9 +138,7 @@ class NameFull : public AddressComponent { NameFull(const NameFull& other); ~NameFull() override; - // Migrates from a legacy structure in which name tokens are imported without - // a status. - void MigrateLegacyStructure(bool is_verified_profile); + void MigrateLegacyStructure(bool is_verified_profile) override; protected: std::vector<const re2::RE2*> GetParseRegularExpressionsByRelevance() @@ -151,13 +148,61 @@ class NameFull : public AddressComponent { base::string16 GetBestFormatString() const override; private: - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - // NameHonorific name_honorific_; NameFirst name_first_{this}; NameMiddle name_middle_{this}; NameLast name_last_{this}; }; +// Atomic component that represents a honorific prefix. +class NameHonorificPrefix : public AddressComponent { + public: + explicit NameHonorificPrefix(AddressComponent* parent); + ~NameHonorificPrefix() override; +}; + +// Compound that represent a full name and a honorific prefix. +// +// +-----------------------+ +// | NAME_FULL_WITH_PREFIX | +// +-----------------------+ +// / \ +// / \ +// / \ +// / \ +// +-------------------+ +------------+ +// | HONORIFIC_PREFIX | | NAME_FULL | +// +-------------------+ +------------+ +// / | \ +// / | \ +// / | \ +// +------------+ +-------------+ +-----------+ +// | NAME_FIRST | | NAME_MIDDLE | | NAME_LAST | +// +------------+ +-------------+ +-----------+ +// / | \ +// / | \ +// / | \ +// / | \ +// +--------+ +--------------+ +---------+ +// | _FIRST | | _CONJUNCTION | | _SECOND | +// +--------+ +--------------+ +---------+ +// +class NameFullWithPrefix : public AddressComponent { + public: + NameFullWithPrefix(); + explicit NameFullWithPrefix(AddressComponent* parent); + NameFullWithPrefix(const NameFullWithPrefix& other); + ~NameFullWithPrefix() override; + + void MigrateLegacyStructure(bool is_verified_profile) override; + + protected: + std::vector<const re2::RE2*> GetParseRegularExpressionsByRelevance() + const override; + + NameHonorificPrefix honorific_prefix_{this}; + NameFull name_full_{this}; +}; + } // namespace structured_address } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name_unittest.cc index ea5ba89c845..7be3e48495e 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_name_unittest.cc @@ -48,7 +48,7 @@ struct LastNameParserTestRecord { // Function to test the parsing of a name from the full (unstructured) // representation into its subcomponents. -void TestNameParsing(const base::string16& full, +void TestNameParsing(const base::string16& full_with_prefix, const base::string16& honorific, const base::string16& first, const base::string16& middle, @@ -56,15 +56,25 @@ void TestNameParsing(const base::string16& full, const base::string16& last_first, const base::string16& last_conjunction, const base::string16& last_second) { - SCOPED_TRACE(full); - NameFull name; - name.SetValueForTypeIfPossible(NAME_FULL, full, + SCOPED_TRACE(full_with_prefix); + NameFullWithPrefix name; + name.SetValueForTypeIfPossible(NAME_FULL_WITH_HONORIFIC_PREFIX, + full_with_prefix, VerificationStatus::kObserved); name.CompleteFullTree(); - EXPECT_EQ(name.GetValueForType(NAME_FULL), full); + EXPECT_EQ(name.GetValueForType(NAME_FULL_WITH_HONORIFIC_PREFIX), + full_with_prefix); // TODO(crbug.com/1113617): Honorifics are temporally disabled. - // EXPECT_EQ(name.GetValueForType(NAME_HONORIFIC_PREFIX), honorific); + if (structured_address::HonorificPrefixEnabled()) { + EXPECT_EQ(name.GetValueForType(NAME_HONORIFIC_PREFIX), honorific); + } + + SCOPED_TRACE(testing::Message() + << "first name: " << name.GetValueForType(NAME_FIRST) << "\n" + << "middle name: " << name.GetValueForType(NAME_MIDDLE) << "\n" + << "last name: " << name.GetValueForType(NAME_LAST)); + EXPECT_EQ(name.GetValueForType(NAME_FIRST), first); EXPECT_EQ(name.GetValueForType(NAME_MIDDLE), middle); EXPECT_EQ(name.GetValueForType(NAME_LAST), last); @@ -346,28 +356,39 @@ TEST(AutofillStructuredName, GetNameMiddleInitial) { base::ASCIIToUTF16("G.-W.")); } -TEST(AutofillStructuredName, TestGetSupportedTypes) { +TEST(AutofillStructuredName, TestGetSupportedTypes_FullNameWithPrefix) { + NameFullWithPrefix full_name_with_prefix; + ServerFieldTypeSet supported_types; + full_name_with_prefix.GetSupportedTypes(&supported_types); + EXPECT_EQ(ServerFieldTypeSet({NAME_FULL_WITH_HONORIFIC_PREFIX, NAME_FULL, + NAME_HONORIFIC_PREFIX, NAME_FIRST, NAME_MIDDLE, + NAME_MIDDLE_INITIAL, NAME_LAST, NAME_LAST_FIRST, + NAME_LAST_CONJUNCTION, NAME_LAST_SECOND}), + supported_types); +} + +TEST(AutofillStructuredName, TestGetSupportedTypes_FullName) { NameFull full_name; ServerFieldTypeSet supported_types; full_name.GetSupportedTypes(&supported_types); - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - EXPECT_EQ(ServerFieldTypeSet({NAME_FULL, /*NAME_HONORIFIC_PREFIX*/ NAME_FIRST, - NAME_MIDDLE, NAME_MIDDLE_INITIAL, NAME_LAST, - NAME_LAST_FIRST, NAME_LAST_CONJUNCTION, - NAME_LAST_SECOND}), + EXPECT_EQ(ServerFieldTypeSet({NAME_FULL, NAME_FIRST, NAME_MIDDLE, + NAME_MIDDLE_INITIAL, NAME_LAST, NAME_LAST_FIRST, + NAME_LAST_CONJUNCTION, NAME_LAST_SECOND}), supported_types); } TEST(AutofillStructuredName, TestSettingMiddleNameInitial) { - NameFull full_name; - EXPECT_EQ(full_name.GetValueForType(NAME_MIDDLE), base::string16()); + NameFullWithPrefix full_name_with_prefix; + EXPECT_EQ(full_name_with_prefix.GetValueForType(NAME_MIDDLE), + base::string16()); - EXPECT_TRUE(full_name.SetValueForTypeIfPossible( + EXPECT_TRUE(full_name_with_prefix.SetValueForTypeIfPossible( NAME_MIDDLE_INITIAL, base::UTF8ToUTF16("M"), VerificationStatus::kObserved)); - EXPECT_EQ(full_name.GetValueForType(NAME_MIDDLE_INITIAL), + EXPECT_EQ(full_name_with_prefix.GetValueForType(NAME_MIDDLE_INITIAL), + base::UTF8ToUTF16("M")); + EXPECT_EQ(full_name_with_prefix.GetValueForType(NAME_MIDDLE), base::UTF8ToUTF16("M")); - EXPECT_EQ(full_name.GetValueForType(NAME_MIDDLE), base::UTF8ToUTF16("M")); } TEST(AutofillStructuredName, MergePermutatedNames) { @@ -415,126 +436,376 @@ TEST(AutofillStructuredName, MergePermutatedNames) { VerificationStatus::kObserved); } -TEST(AutofillStructuredName, MergeNamesByCombiningSubstructureObservations) { +TEST(AutofillStructuredName, + MergeNamesByCombiningSubstructureObservations_WithAdditionalPrefix) { + NameFullWithPrefix one; + NameFullWithPrefix two; + + // The first name has an incorrect componentization of the last name, but a + // correctly observed structure of first, middle, last and a verified full + // name. + AddressComponentTestValues name_one_values = { + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FULL_WITH_HONORIFIC_PREFIX, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kFormatted}, + {.type = NAME_FIRST, + .value = "Pablo Diego", + .status = VerificationStatus::kObserved}, + {.type = NAME_MIDDLE, + .value = "", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kParsed}}; + + // The second name has a correct componentization of the last name, but an + // correctly parsed structure of first, middle, last and an additional + // title. + AddressComponentTestValues name_two_values = { + {.type = NAME_FULL_WITH_HONORIFIC_PREFIX, + .value = "Mr Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kParsed}, + {.type = NAME_HONORIFIC_PREFIX, + .value = "Mr", + .status = VerificationStatus::kParsed}, + {.type = NAME_FIRST, + .value = "Pablo", + .status = VerificationStatus::kParsed}, + {.type = NAME_MIDDLE, + .value = "Diego", + .status = VerificationStatus::kParsed}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kParsed}, + {.type = NAME_LAST_FIRST, + .value = "Ruiz", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_CONJUNCTION, + .value = "y", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Picasso", + .status = VerificationStatus::kObserved}, + }; + + AddressComponentTestValues merge_expectation = { + {.type = NAME_FULL_WITH_HONORIFIC_PREFIX, + .value = "Mr Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_HONORIFIC_PREFIX, + .value = "Mr", + .status = VerificationStatus::kParsed}, + {.type = NAME_FIRST, + .value = "Pablo Diego", + .status = VerificationStatus::kObserved}, + {.type = NAME_MIDDLE, + .value = "", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_FIRST, + .value = "Ruiz", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_CONJUNCTION, + .value = "y", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Picasso", + .status = VerificationStatus::kObserved}, + }; + + SetTestValues(&one, name_one_values); + SetTestValues(&two, name_two_values); + + NameFullWithPrefix copy_of_one(one); + ASSERT_TRUE(one.IsMergeableWithComponent(two)); + EXPECT_TRUE(one.MergeWithComponent(two)); + + VerifyTestValues(&one, merge_expectation); + + // The merging should work in both directions equally. + EXPECT_TRUE(two.MergeWithComponent(copy_of_one)); + + VerifyTestValues(&two, merge_expectation); +} + +// Tests that the root node of NameFullWithPrefix is correctly populated after a +// migration from a NameFull structure. +TEST(AutofillStructuredName, TestPopulationOfNameFullWithPrefix) { + NameFullWithPrefix name_full_with_prefix; + + // The first name has an incorrect componentization of the last name, but a + // correctly observed structure of title, first, middle, last. + AddressComponentTestValues name_full_with_prefix_values = { + {.type = NAME_FULL_WITH_HONORIFIC_PREFIX, + .value = "", + .status = VerificationStatus::kNoStatus}, + {.type = NAME_HONORIFIC_PREFIX, + .value = "", + .status = VerificationStatus::kNoStatus}, + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FIRST, + .value = "Pablo Diego", + .status = VerificationStatus::kObserved}, + {.type = NAME_MIDDLE, + .value = "", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kParsed}}; + + SetTestValues(&name_full_with_prefix, name_full_with_prefix_values); + + AddressComponentTestValues expectation = { + // Expect the honorific prefix to be derived from the full name. + {.type = NAME_FULL_WITH_HONORIFIC_PREFIX, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kFormatted}, + {.type = NAME_HONORIFIC_PREFIX, + .value = "", + .status = VerificationStatus::kNoStatus}, + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FIRST, + .value = "Pablo Diego", + .status = VerificationStatus::kObserved}, + {.type = NAME_MIDDLE, + .value = "", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kParsed}}; + + name_full_with_prefix.MigrateLegacyStructure(true); + name_full_with_prefix.CompleteFullTree(); + + VerifyTestValues(&name_full_with_prefix, expectation); +} + +TEST(AutofillStructuredName, + MergeNamesByCombiningSubstructureObservations_FullName) { NameFull one; NameFull two; // The first name has an incorrect componentization of the last name, but a + // correctly observed structure of first, middle, last. + AddressComponentTestValues name_one_values = { + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FIRST, + .value = "Pablo Diego", + .status = VerificationStatus::kObserved}, + {.type = NAME_MIDDLE, + .value = "", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kParsed}}; + + // The second name has a correct componentization of the last name, but an + // incorrectly parsed structure of first, middle, last. + AddressComponentTestValues name_two_values = { + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kParsed}, + {.type = NAME_FIRST, + .value = "Pablo", + .status = VerificationStatus::kParsed}, + {.type = NAME_MIDDLE, + .value = "Diego", + .status = VerificationStatus::kParsed}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kParsed}, + {.type = NAME_LAST_FIRST, + .value = "Ruiz", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_CONJUNCTION, + .value = "y", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Picasso", + .status = VerificationStatus::kObserved}, + }; + + AddressComponentTestValues merge_expectation = { + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FIRST, + .value = "Pablo Diego", + .status = VerificationStatus::kObserved}, + {.type = NAME_MIDDLE, + .value = "", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_FIRST, + .value = "Ruiz", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_CONJUNCTION, + .value = "y", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Picasso", + .status = VerificationStatus::kObserved}, + }; + + SetTestValues(&one, name_one_values); + SetTestValues(&two, name_two_values); + + // By merging both, it is expected that the first, middle, last + // structure of |one| is maintained, while the substructure of the last name + // is taken from two. + NameFull copy_of_one(one); + EXPECT_TRUE(one.MergeWithComponent(two)); + + VerifyTestValues(&one, merge_expectation); + + // The merging should work in both directions equally. + EXPECT_TRUE(two.MergeWithComponent(copy_of_one)); + + VerifyTestValues(&two, merge_expectation); +} + +TEST(AutofillStructuredName, + MergeNamesByCombiningSubstructureObservations_FullNameWithPrefix) { + NameFullWithPrefix one; + NameFullWithPrefix two; + + // The first name has an incorrect componentization of the last name, but a // correctly observed structure of title, first, middle, last. - one.SetValueForTypeIfPossible( - NAME_FULL, base::ASCIIToUTF16("Mr Pablo Diego Ruiz y Picasso"), - VerificationStatus::kUserVerified); - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - // one.SetValueForTypeIfPossible(NAME_HONORIFIC_PREFIX, - // base::ASCIIToUTF16("Mr"), - // VerificationStatus::kObserved); - one.SetValueForTypeIfPossible(NAME_FIRST, base::ASCIIToUTF16("Pablo Diego"), - VerificationStatus::kObserved); - one.SetValueForTypeIfPossible(NAME_MIDDLE, base::ASCIIToUTF16(""), - VerificationStatus::kObserved); - one.SetValueForTypeIfPossible(NAME_LAST, base::ASCIIToUTF16("Ruiz y Picasso"), - VerificationStatus::kObserved); - one.SetValueForTypeIfPossible(NAME_LAST_SECOND, - base::ASCIIToUTF16("Ruiz y Picasso"), - VerificationStatus::kParsed); + AddressComponentTestValues name_one_values = { + + {.type = NAME_FULL_WITH_HONORIFIC_PREFIX, + .value = "Mr Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_HONORIFIC_PREFIX, + .value = "Mr", + .status = VerificationStatus::kObserved}, + {.type = NAME_FIRST, + .value = "Pablo Diego", + .status = VerificationStatus::kObserved}, + {.type = NAME_MIDDLE, + .value = "", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kParsed}}; // The second name has a correct componentization of the last name, but an - // incorrectly parsed structure of title,first,middle,last. - two.SetValueForTypeIfPossible( - NAME_FULL, base::ASCIIToUTF16("Mr Pablo Diego Ruiz y Picasso"), - VerificationStatus::kUserVerified); - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - // two.SetValueForTypeIfPossible(NAME_HONORIFIC_PREFIX, - // base::ASCIIToUTF16(""), - // VerificationStatus::kParsed); - two.SetValueForTypeIfPossible(NAME_FIRST, base::ASCIIToUTF16("Pablo"), - VerificationStatus::kParsed); - two.SetValueForTypeIfPossible(NAME_MIDDLE, base::ASCIIToUTF16("Diego"), - VerificationStatus::kParsed); - two.SetValueForTypeIfPossible(NAME_LAST, base::ASCIIToUTF16("Ruiz y Picasso"), - VerificationStatus::kParsed); - two.SetValueForTypeIfPossible(NAME_LAST_FIRST, base::ASCIIToUTF16("Ruiz"), - VerificationStatus::kObserved); - two.SetValueForTypeIfPossible(NAME_LAST_CONJUNCTION, base::ASCIIToUTF16("y"), - VerificationStatus::kObserved); - two.SetValueForTypeIfPossible(NAME_LAST_SECOND, base::ASCIIToUTF16("Picasso"), - VerificationStatus::kObserved); + // incorrectly parsed structure of first, middle, last. + AddressComponentTestValues name_two_values = { + {.type = NAME_FULL_WITH_HONORIFIC_PREFIX, + .value = "Mr Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kParsed}, + {.type = NAME_HONORIFIC_PREFIX, + .value = "Mr", + .status = VerificationStatus::kParsed}, + {.type = NAME_FIRST, + .value = "Pablo", + .status = VerificationStatus::kParsed}, + {.type = NAME_MIDDLE, + .value = "Diego", + .status = VerificationStatus::kParsed}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kParsed}, + {.type = NAME_LAST_FIRST, + .value = "Ruiz", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_CONJUNCTION, + .value = "y", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Picasso", + .status = VerificationStatus::kObserved}, + }; + + AddressComponentTestValues merge_expectation = { + {.type = NAME_FULL_WITH_HONORIFIC_PREFIX, + .value = "Mr Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FULL, + .value = "Pablo Diego Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_HONORIFIC_PREFIX, + .value = "Mr", + .status = VerificationStatus::kObserved}, + {.type = NAME_FIRST, + .value = "Pablo Diego", + .status = VerificationStatus::kObserved}, + {.type = NAME_MIDDLE, + .value = "", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_FIRST, + .value = "Ruiz", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_CONJUNCTION, + .value = "y", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST_SECOND, + .value = "Picasso", + .status = VerificationStatus::kObserved}, + }; + + SetTestValues(&one, name_one_values); + SetTestValues(&two, name_two_values); // By merging both, it is expected that the title, first, middle, last // structure of |one| is maintained, while the substructure of the last name // is taken from two. - NameFull copy_of_one; - copy_of_one = one; + NameFullWithPrefix copy_of_one(one); EXPECT_TRUE(one.MergeWithComponent(two)); - EXPECT_EQ(one.GetValueForType(NAME_FULL), - base::ASCIIToUTF16("Mr Pablo Diego Ruiz y Picasso")); - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - // EXPECT_EQ(one.GetValueForType(NAME_HONORIFIC_PREFIX), - // base::ASCIIToUTF16("Mr")); - EXPECT_EQ(one.GetValueForType(NAME_FIRST), base::ASCIIToUTF16("Pablo Diego")); - EXPECT_EQ(one.GetValueForType(NAME_MIDDLE), base::ASCIIToUTF16("")); - EXPECT_EQ(one.GetValueForType(NAME_LAST), - base::ASCIIToUTF16("Ruiz y Picasso")); - EXPECT_EQ(one.GetValueForType(NAME_LAST_FIRST), base::ASCIIToUTF16("Ruiz")); - EXPECT_EQ(one.GetValueForType(NAME_LAST_CONJUNCTION), - base::ASCIIToUTF16("y")); - EXPECT_EQ(one.GetValueForType(NAME_LAST_SECOND), - base::ASCIIToUTF16("Picasso")); - - EXPECT_EQ(one.GetVerificationStatusForType(NAME_FULL), - VerificationStatus::kUserVerified); - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - // EXPECT_EQ(one.GetVerificationStatusForType(NAME_HONORIFIC_PREFIX), - // VerificationStatus::kObserved); - EXPECT_EQ(one.GetVerificationStatusForType(NAME_FIRST), - VerificationStatus::kObserved); - EXPECT_EQ(one.GetVerificationStatusForType(NAME_MIDDLE), - VerificationStatus::kObserved); - EXPECT_EQ(one.GetVerificationStatusForType(NAME_LAST), - VerificationStatus::kObserved); - EXPECT_EQ(one.GetVerificationStatusForType(NAME_LAST_FIRST), - VerificationStatus::kObserved); - EXPECT_EQ(one.GetVerificationStatusForType(NAME_LAST_CONJUNCTION), - VerificationStatus::kObserved); - EXPECT_EQ(one.GetVerificationStatusForType(NAME_LAST_SECOND), - VerificationStatus::kObserved); + VerifyTestValues(&one, merge_expectation); // The merging should work in both directions equally. EXPECT_TRUE(two.MergeWithComponent(copy_of_one)); - EXPECT_EQ(two.GetValueForType(NAME_FULL), - base::ASCIIToUTF16("Mr Pablo Diego Ruiz y Picasso")); - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - // EXPECT_EQ(two.GetValueForType(NAME_HONORIFIC_PREFIX), - // base::ASCIIToUTF16("Mr")); - EXPECT_EQ(two.GetValueForType(NAME_FIRST), base::ASCIIToUTF16("Pablo Diego")); - EXPECT_EQ(two.GetValueForType(NAME_MIDDLE), base::ASCIIToUTF16("")); - EXPECT_EQ(two.GetValueForType(NAME_LAST), - base::ASCIIToUTF16("Ruiz y Picasso")); - EXPECT_EQ(two.GetValueForType(NAME_LAST_FIRST), base::ASCIIToUTF16("Ruiz")); - EXPECT_EQ(two.GetValueForType(NAME_LAST_CONJUNCTION), - base::ASCIIToUTF16("y")); - EXPECT_EQ(two.GetValueForType(NAME_LAST_SECOND), - base::ASCIIToUTF16("Picasso")); - - EXPECT_EQ(two.GetVerificationStatusForType(NAME_FULL), - VerificationStatus::kUserVerified); - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - // EXPECT_EQ(two.GetVerificationStatusForType(NAME_HONORIFIC_PREFIX), - // VerificationStatus::kObserved); - EXPECT_EQ(two.GetVerificationStatusForType(NAME_FIRST), - VerificationStatus::kObserved); - EXPECT_EQ(two.GetVerificationStatusForType(NAME_MIDDLE), - VerificationStatus::kObserved); - EXPECT_EQ(two.GetVerificationStatusForType(NAME_LAST), - VerificationStatus::kObserved); - EXPECT_EQ(two.GetVerificationStatusForType(NAME_LAST_FIRST), - VerificationStatus::kObserved); - EXPECT_EQ(two.GetVerificationStatusForType(NAME_LAST_CONJUNCTION), - VerificationStatus::kObserved); - EXPECT_EQ(two.GetVerificationStatusForType(NAME_LAST_SECOND), - VerificationStatus::kObserved); + VerifyTestValues(&two, merge_expectation); } TEST(AutofillStructuredName, TestCopyConstructuror) { @@ -560,7 +831,7 @@ TEST(AutofillStructuredName, TestCopyConstructuror) { VerificationStatus::kParsed); NameFull copy = orginal; - EXPECT_EQ(orginal, copy); + EXPECT_TRUE(orginal.SameAs(copy)); } TEST(AutofillStructuredName, @@ -731,13 +1002,83 @@ TEST(AutofillStructuredName, MergeSubsetLastname) { VerifyTestValues(&name, name_values); } -TEST(AutofillStructuredName, MergeSubsetLastname2) { +TEST(AutofillStructuredName, MergeSubsetLastname_WithNonSpaceSeparators) { NameFull name; NameFull subset_name; name.SetMergeModeForTesting(kRecursivelyMergeSingleTokenSubset | kRecursivelyMergeTokenEquivalentValues); AddressComponentTestValues name_values = { + {.type = NAME_FULL, + .value = "Thomas-Neo-Anderson", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FIRST, + .value = "Thomas", + .status = VerificationStatus::kObserved}, + {.type = NAME_MIDDLE, + .value = "Thomas", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Anderson", + .status = VerificationStatus::kObserved}, + }; + + AddressComponentTestValues subset_name_values = { + {.type = NAME_FULL, + .value = "Thomas-Anderson", + .status = VerificationStatus::kObserved}, + {.type = NAME_FIRST, + .value = "Thomas", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Anderson", + .status = VerificationStatus::kObserved}, + }; + + AddressComponentTestValues expectation = { + {.type = NAME_FULL, + .value = "Thomas-Neo-Anderson", + .status = VerificationStatus::kUserVerified}, + {.type = NAME_FIRST, + .value = "Thomas", + .status = VerificationStatus::kObserved}, + {.type = NAME_MIDDLE, + .value = "Thomas", + .status = VerificationStatus::kObserved}, + {.type = NAME_LAST, + .value = "Anderson", + .status = VerificationStatus::kObserved}, + }; + + SetTestValues(&name, name_values); + SetTestValues(&subset_name, subset_name_values); + + // After normalization, the two names should have a single-token-superset + // relation. + SortedTokenComparisonResult token_comparison_result = + CompareSortedTokens(name.ValueForComparisonForTesting(), + subset_name.ValueForComparisonForTesting()); + EXPECT_TRUE(token_comparison_result.IsSingleTokenSuperset()); + + // Without normalization, the two names should be considered distinct. + token_comparison_result = + CompareSortedTokens(name.GetValue(), subset_name.GetValue()); + EXPECT_TRUE(token_comparison_result.status == DISTINCT); + + // Verify that those two names are not considered mergeable. + EXPECT_FALSE(name.IsMergeableWithComponent(subset_name)); + EXPECT_FALSE(name.MergeWithComponent(subset_name)); + + VerifyTestValues(&name, expectation); +} + +TEST(AutofillStructuredName, MergeSubsetLastname2) { + NameFullWithPrefix name; + NameFullWithPrefix subset_name; + name.SetMergeModeForTesting(kRecursivelyMergeSingleTokenSubset | + kRecursivelyMergeTokenEquivalentValues); + + AddressComponentTestValues name_values = { {.type = NAME_FIRST, .value = "Thomas", .status = VerificationStatus::kObserved}, diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.cc b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.cc index ba400f32f08..82aab62d170 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.cc @@ -160,12 +160,16 @@ const char kOptionalLastNamePrefixRe[] = // Regular expression to match the affixes that indicate the floor an // apartment is located in. const char kFloorAffixRe[] = - "((°|º|\\.|\\s)*" - "(floor|flur|fl|og|obergeschoss|ug|untergeschoss|geschoss|andar)" + "((°|º|\\.|\\s|-)*" + "(floor|flur|fl|og|obergeschoss|ug|untergeschoss|geschoss|andar|piso|º)" "(\\.|\\s|-)*)"; +// Prefix that indicates an apartment number. const char kApartmentNumberPrefix[] = - "((apt|apartment|wohnung|apto)(\\.|\\s|-)*)"; + "((apt|apartment|wohnung|apto|-)(\\.|\\s|-)*)"; + +// Suffix that inficates an apartment number. +const char kApartmentNumberSuffix[] = "(\\.|\\s|-)*(ª)"; // Regular expression to match the prefixes that indicate a house number. const char kHouseNumberOptionalPrefixRe[] = "(((no|°|º|number)(\\.|-|\\s)*)?)"; @@ -257,8 +261,8 @@ std::string ParseOnlyLastNameExpression() { // Returns an expression to parse a name that consists of a first, middle and // last name with an optional honorific prefix. The full name is parsed into -// |NAME_FULL|. The name can start with an honorific prefix that is parsed -// into |NAME_HONORIFIC_PREFIX|. The last token is parsed into |NAME_LAST|. +// |NAME_FULL|. The name can start with an honorific prefix that is ignored. +// The last token is parsed into |NAME_LAST|. // This token may be preceded by a last name prefix like "Mac" or // "von" that is included in |NAME_LAST|. If the strings contains any // remaining tokens, the first token is parsed into @@ -266,8 +270,8 @@ std::string ParseOnlyLastNameExpression() { std::string ParseFirstMiddleLastNameExpression() { return CaptureTypeWithPattern( NAME_FULL, - {CaptureTypeWithPattern(NAME_HONORIFIC_PREFIX, kHonorificPrefixRe, - CaptureOptions{.quantifier = MATCH_OPTIONAL}), + {NoCapturePattern(kHonorificPrefixRe, + CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPattern(NAME_FIRST, kSingleWordRe, CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPattern( @@ -281,15 +285,15 @@ std::string ParseFirstMiddleLastNameExpression() { // Returns an expression to parse a name that starts with the last name, // followed by a comma, and than the first and middle names. // The full name is parsed into |NAME_FULL|. The name can start with an optional -// honorific prefix that is parsed into |HONORIFIC_PREFIX|, follow by a single +// honorific prefix that is ignored, followed by a single // token that is parsed into |LAST_NAME|. The |LAST_NAME| must be preceded by a // comma with optional spaces. The next token is parsed into |NAME_FIRST| and // all remaining tokens are parsed into |NAME_MIDDLE|. std::string ParseLastCommaFirstMiddleExpression() { return CaptureTypeWithPattern( NAME_FULL, - {CaptureTypeWithPattern(NAME_HONORIFIC_PREFIX, kHonorificPrefixRe, - CaptureOptions{.quantifier = MATCH_OPTIONAL}), + {NoCapturePattern(kHonorificPrefixRe, + CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPattern(NAME_LAST, {kOptionalLastNamePrefixRe, kSingleWordRe}, {.separator = "\\s*,\\s*"}), @@ -321,13 +325,13 @@ std::string ParseHispanicLastNameExpression() { } // Returns an expression to parse a full Hispanic/Latinx name that -// contains an optional honorific prefix, a first name, and a last name as -// specified by |ParseHispanicLastNameExpression()|. +// contains an optional honorific prefix which is ignored, a first name, and a +// last name as specified by |ParseHispanicLastNameExpression()|. std::string ParseHispanicFullNameExpression() { return CaptureTypeWithPattern( NAME_FULL, - {CaptureTypeWithPattern(NAME_HONORIFIC_PREFIX, kHonorificPrefixRe, - CaptureOptions{.quantifier = MATCH_OPTIONAL}), + {NoCapturePattern(kHonorificPrefixRe, + CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPattern( NAME_FIRST, kMultipleLazyWordsRe, CaptureOptions{.quantifier = MATCH_LAZY_OPTIONAL}), @@ -354,19 +358,50 @@ std::string ParseStreetNameHouseNumberExpression() { {CaptureTypeWithPattern(ADDRESS_HOME_STREET_AND_DEPENDENT_STREET_NAME, CaptureTypeWithPattern(ADDRESS_HOME_STREET_NAME, kMultipleLazyWordsRe), - {.separator = ""}), - CaptureTypeWithPrefixedPattern(ADDRESS_HOME_HOUSE_NUMBER, - kHouseNumberOptionalPrefixRe, - "(?:\\d+\\w?)"), + CaptureOptions{.separator = ""}), + CaptureTypeWithAffixedPattern(ADDRESS_HOME_HOUSE_NUMBER, + kHouseNumberOptionalPrefixRe, + "(?:\\d+\\w?)", "(th\\.|\\.)?"), CaptureTypeWithPattern( ADDRESS_HOME_SUBPREMISE, { CaptureTypeWithPrefixedPattern( - ADDRESS_HOME_FLOOR, kFloorAffixRe, "(?:(\\d{0,3}\\w?))", + ADDRESS_HOME_FLOOR, kFloorAffixRe, "(?:(\\d{1,3}\\w?|\\w))", CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPrefixedPattern( ADDRESS_HOME_APT_NUM, kApartmentNumberPrefix, - "(?:(\\d{0,3}\\w?))", + "(?:(\\d{1,3}\\w?|\\w))", + CaptureOptions{.quantifier = MATCH_OPTIONAL}), + }, + CaptureOptions{.quantifier = MATCH_OPTIONAL})}); +} + +// Returns an expression to parse a street address into the street name, the +// house number and the subpremise. The latter is parsed into the floor and +// apartment number. The expression is applicable, if the street name comes +// before the house number, followed by the floor and the apartment. +// Both the floor and the apartment must be indicated by a suffix. +// Example: Calla 1, 2º, 3ª +// Where 2 is the floor and 3 the apartment number. +std::string ParseStreetNameHouseNumberSuffixedFloorAndAppartmentExpression() { + return CaptureTypeWithPattern( + ADDRESS_HOME_STREET_ADDRESS, + {CaptureTypeWithPattern(ADDRESS_HOME_STREET_AND_DEPENDENT_STREET_NAME, + CaptureTypeWithPattern(ADDRESS_HOME_STREET_NAME, + kMultipleLazyWordsRe), + CaptureOptions{.separator = ""}), + CaptureTypeWithAffixedPattern(ADDRESS_HOME_HOUSE_NUMBER, + kHouseNumberOptionalPrefixRe, + "(?:\\d+\\w?)", "(th\\.|\\.)?"), + CaptureTypeWithPattern( + ADDRESS_HOME_SUBPREMISE, + { + CaptureTypeWithSuffixedPattern( + ADDRESS_HOME_FLOOR, "(?:(\\d{1,3}\\w?|\\w))", kFloorAffixRe, + CaptureOptions{.quantifier = MATCH_OPTIONAL}), + CaptureTypeWithAffixedPattern( + ADDRESS_HOME_APT_NUM, "(-\\s*)?", "(?:(\\d{1,3}\\w?|\\w))", + kApartmentNumberSuffix, CaptureOptions{.quantifier = MATCH_OPTIONAL}), }, CaptureOptions{.quantifier = MATCH_OPTIONAL})}); @@ -388,9 +423,9 @@ std::string ParseStreetNameHouseNumberExpressionSuffixedFloor() { CaptureTypeWithPattern(ADDRESS_HOME_STREET_NAME, kMultipleLazyWordsRe), {.separator = ""}), - CaptureTypeWithPrefixedPattern(ADDRESS_HOME_HOUSE_NUMBER, - kHouseNumberOptionalPrefixRe, - "(?:\\d+\\w?)"), + CaptureTypeWithAffixedPattern(ADDRESS_HOME_HOUSE_NUMBER, + kHouseNumberOptionalPrefixRe, + "(?:\\d+\\w?)", "(th\\.|\\.)?"), CaptureTypeWithPattern( ADDRESS_HOME_SUBPREMISE, { @@ -414,7 +449,9 @@ std::string ParseStreetNameHouseNumberExpressionSuffixedFloor() { std::string ParseHouseNumberStreetNameExpression() { return CaptureTypeWithPattern( ADDRESS_HOME_STREET_ADDRESS, - {CaptureTypeWithPattern(ADDRESS_HOME_HOUSE_NUMBER, "(?:\\d+\\w{0,3})"), + {CaptureTypeWithAffixedPattern(ADDRESS_HOME_HOUSE_NUMBER, + kHouseNumberOptionalPrefixRe, + "(?:\\d+\\w?)", "(th\\.|\\.)?"), CaptureTypeWithPattern(ADDRESS_HOME_STREET_AND_DEPENDENT_STREET_NAME, CaptureTypeWithPattern(ADDRESS_HOME_STREET_NAME, kMultipleLazyWordsRe), @@ -433,6 +470,17 @@ std::string ParseHouseNumberStreetNameExpression() { CaptureOptions{.quantifier = MATCH_OPTIONAL})}); } +// Returns a regular expression to parse a name with a honorific into the prefix +// and the full name. +std::string ParsePrefixedName() { + return CaptureTypeWithPattern( + NAME_FULL_WITH_HONORIFIC_PREFIX, + {CaptureTypeWithPattern(NAME_HONORIFIC_PREFIX, kHonorificPrefixRe, + CaptureOptions{.quantifier = MATCH_OPTIONAL}), + CaptureTypeWithPattern(NAME_FULL, ".+", + CaptureOptions{.quantifier = MATCH_REQUIRED})}); +} + } // namespace StructuredAddressesRegExProvider::StructuredAddressesRegExProvider() = default; @@ -481,8 +529,12 @@ std::string StructuredAddressesRegExProvider::GetPattern( return ParseHouseNumberStreetNameExpression(); case RegEx::kParseStreetNameHouseNumberSuffixedFloor: return ParseStreetNameHouseNumberExpressionSuffixedFloor(); + case RegEx::kParseStreetNameHouseNumberSuffixedFloorAndAppartmentRe: + return ParseStreetNameHouseNumberSuffixedFloorAndAppartmentExpression(); case RegEx::kParseStreetNameHouseNumber: return ParseStreetNameHouseNumberExpression(); + case RegEx::kParsePrefixedName: + return ParsePrefixedName(); } NOTREACHED(); } diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.h b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.h index ce94af3c5ff..a983be897af 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.h @@ -36,7 +36,9 @@ enum class RegEx { kMatchMiddleNameInitialsCharacteristics, kParseStreetNameHouseNumber, kParseStreetNameHouseNumberSuffixedFloor, + kParseStreetNameHouseNumberSuffixedFloorAndAppartmentRe, kParseHouseNumberStreetName, + kParsePrefixedName, kLastRegEx = kParseLastNameIntoSecondLastName, }; diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_test_utils.cc b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_test_utils.cc index a2aa93e787d..3d8f0a1b848 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_test_utils.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_test_utils.cc @@ -66,8 +66,8 @@ void VerifyTestValues(AddressComponent* component, AutofillType(test_value.type).ToString().c_str(), test_value.value.c_str(), static_cast<int>(test_value.status))); - EXPECT_EQ(component->GetValueForType(test_value.type), - base::UTF8ToUTF16(test_value.value)); + EXPECT_EQ(base::UTF16ToUTF8(component->GetValueForType(test_value.type)), + test_value.value); // Omit testing the status if the value is empty. if (!test_value.value.empty()) { diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_unittest.cc index c00710a40d4..4b1a2f33a66 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_unittest.cc @@ -168,7 +168,40 @@ TEST(AutofillStructuredAddress, ParseStreetAddress) { .street_name = "Av. Paulista", .house_number = "1098", .floor = "1", - .apartment = "101"}}; + .apartment = "101"}, + // Examples for Mexico. + {.street_address = "Street Name 12 - Piso 13 - 14", + .street_name = "Street Name", + .house_number = "12", + .floor = "13", + .apartment = "14"}, + {.street_address = "Street Name 12 - 14", + .street_name = "Street Name", + .house_number = "12", + .floor = "", + .apartment = "14"}, + {.street_address = "Street Name 12 - Piso 13", + .street_name = "Street Name", + .house_number = "12", + .floor = "13", + .apartment = ""}, + // Examples for Spain. + {.street_address = "Street Name 1, 2º, 3ª", + .street_name = "Street Name", + .house_number = "1", + .floor = "2", + .apartment = "3"}, + {.street_address = "Street Name 1, 2º", + .street_name = "Street Name", + .house_number = "1", + .floor = "2", + .apartment = ""}, + {.street_address = "Street Name 1, 3ª", + .street_name = "Street Name", + .house_number = "1", + .floor = "", + .apartment = "3"}, + }; for (const auto& test_case : test_cases) TestAddressLineParsing(test_case); @@ -228,7 +261,46 @@ TEST(AutofillStructuredAddress, TestStreetAddressFormatting) { .street_name = "Amphitheatre Parkway", .house_number = "1600", .floor = "6", - .apartment = "12"}}; + .apartment = "12"}, + // Examples for Mexico. + {.country_code = "MX", + .street_address = "StreetName 12 - Piso 13 - 14", + .street_name = "StreetName", + .house_number = "12", + .floor = "13", + .apartment = "14"}, + {.country_code = "MX", + .street_address = "StreetName 12 - 14", + .street_name = "StreetName", + .house_number = "12", + .floor = "", + .apartment = "14"}, + {.country_code = "MX", + .street_address = "StreetName 12 - Piso 13", + .street_name = "StreetName", + .house_number = "12", + .floor = "13", + .apartment = ""}, + // Examples for Spain. + {.country_code = "ES", + .street_address = "Street Name 1, 3ª", + .street_name = "Street Name", + .house_number = "1", + .floor = "", + .apartment = "3"}, + {.country_code = "ES", + .street_address = "Street Name 1, 2º", + .street_name = "Street Name", + .house_number = "1", + .floor = "2", + .apartment = ""}, + {.country_code = "ES", + .street_address = "Street Name 1, 2º, 3ª", + .street_name = "Street Name", + .house_number = "1", + .floor = "2", + .apartment = "3"}, + }; for (const auto& test_case : test_cases) TestAddressLineFormatting(test_case); diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils.cc b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils.cc index 62bf64b2722..0dc7769247a 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils.cc @@ -55,6 +55,10 @@ bool SortedTokenComparisonResult::ContainEachOther() const { return status != DISTINCT; } +bool SortedTokenComparisonResult::TokensMatch() const { + return status == MATCH; +} + bool StructuredNamesEnabled() { return base::FeatureList::IsEnabled( features::kAutofillEnableSupportForMoreStructureInNames); @@ -65,6 +69,13 @@ bool StructuredAddressesEnabled() { features::kAutofillEnableSupportForMoreStructureInAddresses); } +bool HonorificPrefixEnabled() { + return base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForHonorificPrefixes) && + base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForMoreStructureInNames); +} + Re2RegExCache::Re2RegExCache() = default; // static @@ -269,6 +280,29 @@ std::string CaptureTypeWithPattern( options); } +std::string NoCapturePattern(const std::string& pattern, + const CaptureOptions& options) { + std::string quantifier; + switch (options.quantifier) { + // Makes the match optional. + case MATCH_OPTIONAL: + quantifier = "?"; + break; + // Makes the match lazy meaning that it is avoided if possible. + case MATCH_LAZY_OPTIONAL: + quantifier = "??"; + break; + // Makes the match required. + case MATCH_REQUIRED: + quantifier = ""; + } + + // By adding an "i" in the first group, the capturing is case insensitive. + // Allow multiple separators to support the ", " case. + return base::StrCat( + {"(?i:", pattern, "(?:", options.separator, ")+)", quantifier}); +} + std::string CaptureTypeWithAffixedPattern(const ServerFieldType& type, const std::string& prefix, const std::string& pattern, @@ -291,9 +325,9 @@ std::string CaptureTypeWithAffixedPattern(const ServerFieldType& type, // By adding an "i" in the first group, the capturing is case insensitive. // Allow multiple separators to support the ", " case. - return base::StrCat({"(?i:", prefix, "(?P<", AutofillType(type).ToString(), - ">", pattern, ")", suffix, "(?:", options.separator, - ")+)", quantifier}); + return base::StrCat( + {"(?i:", prefix, "(?P<", AutofillType::ServerFieldTypeToString(type), ">", + pattern, ")", suffix, "(?:", options.separator, ")+)", quantifier}); } std::string CaptureTypeWithSuffixedPattern(const ServerFieldType& type, diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils.h b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils.h index 7098f754ae3..f01c89aa50c 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils.h @@ -77,6 +77,8 @@ struct SortedTokenComparisonResult { bool OneIsSubset() const; // Returns true if one contains the other. bool ContainEachOther() const; + // Returns true if both contain the same tokens. + bool TokensMatch() const; }; // Options for capturing a named group using the @@ -97,6 +99,9 @@ bool StructuredNamesEnabled(); // Returns true if the structured address feature is enabled. bool StructuredAddressesEnabled(); +// Returns true if honorific prefixes are enabled. +bool HonorificPrefixEnabled(); + // A cache for compiled RE2 regular expressions. class Re2RegExCache { public: @@ -240,6 +245,11 @@ std::string CaptureTypeWithPattern( const ServerFieldType& type, std::initializer_list<base::StringPiece> pattern_span_initializer_list); +// A pattern that is used to capture tokens that are not supposed to be +// associated into a type. +std::string NoCapturePattern(const std::string& pattern, + const CaptureOptions& options = CaptureOptions()); + // Returns a capture group named by the string representation of |type| that // matches |pattern| with an additional uncaptured |prefix_pattern| and // |suffix_pattern|. diff --git a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils_unittest.cc index d93c0bb9c63..01fd13bc3ad 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_structured_address_utils_unittest.cc @@ -217,6 +217,17 @@ TEST(AutofillStructuredAddressUtils, CaptureTypeWithPattern) { CaptureTypeWithPattern(NAME_FULL, "abs\\w", {.separator = "_"})); } +TEST(AutofillStructuredAddressUtils, NoCaptureTypeWithPattern) { + EXPECT_EQ("(?i:abs\\w(?:,|\\s+|$)+)?", + NoCapturePattern("abs\\w", {.quantifier = MATCH_OPTIONAL})); + EXPECT_EQ("(?i:abs\\w(?:,|\\s+|$)+)", NoCapturePattern("abs\\w")); + EXPECT_EQ("(?i:abs\\w(?:,|\\s+|$)+)??", + NoCapturePattern("abs\\w", {.quantifier = MATCH_LAZY_OPTIONAL})); + EXPECT_EQ("(?i:abs\\w(?:,|\\s+|$)+)", NoCapturePattern("abs\\w")); + EXPECT_EQ("(?i:abs\\w(?:_)+)", + NoCapturePattern("abs\\w", {.separator = "_"})); +} + TEST(AutofillStructuredAddressUtils, TokenizeValue) { std::vector<AddressToken> expected_tokens = { {base::ASCIIToUTF16("AnD"), base::ASCIIToUTF16("and"), 1}, diff --git a/chromium/components/autofill/core/browser/data_model/contact_info.cc b/chromium/components/autofill/core/browser/data_model/contact_info.cc index bd31bdcaa3a..a42f4a78f59 100644 --- a/chromium/components/autofill/core/browser/data_model/contact_info.cc +++ b/chromium/components/autofill/core/browser/data_model/contact_info.cc @@ -23,21 +23,23 @@ namespace autofill { -using structured_address::VerificationStatus; - namespace { -// TODO(crbug.com/1103421): Clean legacy implementation once structured names -// are fully launched. -bool StructuredAddressesEnabled() { - return base::FeatureList::IsEnabled( - features::kAutofillEnableSupportForMoreStructureInNames); +// Factory for the structured tree to be used in NameInfo. +std::unique_ptr<AddressComponent> CreateStructuredNameTree() { + if (structured_address::HonorificPrefixEnabled()) { + return std::make_unique<structured_address::NameFullWithPrefix>(); + } + return std::make_unique<structured_address::NameFull>(); } + } // namespace -NameInfo::NameInfo() = default; +using structured_address::VerificationStatus; + +NameInfo::NameInfo() : name_(CreateStructuredNameTree()) {} -NameInfo::NameInfo(const NameInfo& info) { +NameInfo::NameInfo(const NameInfo& info) : NameInfo() { *this = info; } @@ -49,8 +51,8 @@ NameInfo& NameInfo::operator=(const NameInfo& info) { // TODO(crbug.com/1103421): Clean legacy implementation once structured names // are fully launched. - if (StructuredAddressesEnabled()) { - name_ = info.name_; + if (structured_address::StructuredNamesEnabled()) { + name_->CopyFrom(*info.name_); } else { given_ = info.given_; middle_ = info.middle_; @@ -61,24 +63,24 @@ NameInfo& NameInfo::operator=(const NameInfo& info) { } bool NameInfo::MergeStructuredName(const NameInfo& newer) { - return name_.MergeWithComponent(newer.GetStructuredName()); + return name_->MergeWithComponent(newer.GetStructuredName()); } void NameInfo::MergeStructuredNameValidationStatuses(const NameInfo& newer) { - name_.MergeVerificationStatuses(newer.GetStructuredName()); + name_->MergeVerificationStatuses(newer.GetStructuredName()); } bool NameInfo::IsStructuredNameMergeable(const NameInfo& newer) const { - if (!StructuredAddressesEnabled()) + if (!structured_address::StructuredNamesEnabled()) NOTREACHED(); - return name_.IsMergeableWithComponent(newer.GetStructuredName()); + return name_->IsMergeableWithComponent(newer.GetStructuredName()); } bool NameInfo::FinalizeAfterImport(bool profile_is_verified) { - if (StructuredAddressesEnabled()) { - name_.MigrateLegacyStructure(profile_is_verified); - return name_.CompleteFullTree(); + if (structured_address::StructuredNamesEnabled()) { + name_->MigrateLegacyStructure(profile_is_verified); + return name_->CompleteFullTree(); } return true; } @@ -89,23 +91,27 @@ bool NameInfo::operator==(const NameInfo& other) const { // TODO(crbug.com/1103421): Clean legacy implementation once structured names // are fully launched. - if (StructuredAddressesEnabled()) - return name_ == other.name_; + if (structured_address::StructuredNamesEnabled()) + return name_->SameAs(*other.name_); return given_ == other.given_ && middle_ == other.middle_ && family_ == other.family_ && full_ == other.full_; } base::string16 NameInfo::GetRawInfo(ServerFieldType type) const { - DCHECK_EQ(NAME, AutofillType(type).group()); + DCHECK_EQ(FieldTypeGroup::kName, AutofillType(type).group()); // TODO(crbug.com/1103421): Clean legacy implementation once structured names // are fully launched. - if (StructuredAddressesEnabled()) { - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - if (type == NAME_HONORIFIC_PREFIX) + if (structured_address::StructuredNamesEnabled()) { + // Without the second generation of the structured name tree, honorific + // prefixes and the name including the prefix are unsupported types. + if ((type == NAME_HONORIFIC_PREFIX || + type == NAME_FULL_WITH_HONORIFIC_PREFIX) && + !structured_address::HonorificPrefixEnabled()) { return base::string16(); - return name_.GetValueForType(type); + } + return name_->GetValueForType(type); } switch (type) { case NAME_FIRST: @@ -131,15 +137,19 @@ base::string16 NameInfo::GetRawInfo(ServerFieldType type) const { void NameInfo::SetRawInfoWithVerificationStatus(ServerFieldType type, const base::string16& value, VerificationStatus status) { - DCHECK_EQ(NAME, AutofillType(type).group()); + DCHECK_EQ(FieldTypeGroup::kName, AutofillType(type).group()); // TODO(crbug.com/1103421): Clean legacy implementation once structured names // are fully launched. - if (StructuredAddressesEnabled()) { - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - if (type == NAME_HONORIFIC_PREFIX) + if (structured_address::StructuredNamesEnabled()) { + // Without the second generation of the structured name tree, honorific + // prefixes and the name including the prefix are unsupported types. + if ((type == NAME_HONORIFIC_PREFIX || + type == NAME_FULL_WITH_HONORIFIC_PREFIX) && + !structured_address::HonorificPrefixEnabled()) { return; - bool success = name_.SetValueForTypeIfPossible(type, value, status); - DCHECK(success); + } + bool success = name_->SetValueForTypeIfPossible(type, value, status); + DCHECK(success) << AutofillType::ServerFieldTypeToString(type); return; } switch (type) { @@ -164,6 +174,7 @@ void NameInfo::SetRawInfoWithVerificationStatus(ServerFieldType type, case NAME_LAST_SECOND: case NAME_LAST_CONJUNCTION: case NAME_HONORIFIC_PREFIX: + case NAME_FULL_WITH_HONORIFIC_PREFIX: break; default: @@ -174,8 +185,8 @@ void NameInfo::SetRawInfoWithVerificationStatus(ServerFieldType type, void NameInfo::GetSupportedTypes(ServerFieldTypeSet* supported_types) const { // TODO(crbug.com/1103421): Clean legacy implementation once structured names // are fully launched. - if (StructuredAddressesEnabled()) { - name_.GetSupportedTypes(supported_types); + if (structured_address::StructuredNamesEnabled()) { + name_->GetSupportedTypes(supported_types); } else { supported_types->insert(NAME_FIRST); supported_types->insert(NAME_MIDDLE); @@ -189,7 +200,7 @@ base::string16 NameInfo::GetInfoImpl(const AutofillType& type, const std::string& app_locale) const { // TODO(crbug.com/1103421): Clean legacy implementation once structured names // are fully launched. - if (!StructuredAddressesEnabled()) { + if (!structured_address::StructuredNamesEnabled()) { if (type.GetStorableType() == NAME_FULL) return FullName(); } @@ -202,14 +213,14 @@ bool NameInfo::SetInfoWithVerificationStatusImpl(const AutofillType& type, VerificationStatus status) { // TODO(crbug.com/1103421): Clean legacy implementation once structured names // are fully launched. - if (StructuredAddressesEnabled()) { + if (structured_address::StructuredNamesEnabled()) { if (type.GetStorableType() == NAME_FULL) { // If the set string is token equivalent to the old one, the value can // just be updated, otherwise create a new name record and complete it in // the end. bool token_equivalent = structured_address::AreStringTokenEquivalent( - value, name_.GetValueForType(NAME_FULL)); - name_.SetValueForTypeIfPossible( + value, name_->GetValueForType(NAME_FULL)); + name_->SetValueForTypeIfPossible( type.GetStorableType(), value, status, /*invalidate_child_nodes=*/!token_equivalent); return true; @@ -229,19 +240,38 @@ bool NameInfo::SetInfoWithVerificationStatusImpl(const AutofillType& type, status); } +void NameInfo::GetMatchingTypes(const base::string16& text, + const std::string& app_locale, + ServerFieldTypeSet* matching_types) const { + FormGroup::GetMatchingTypes(text, app_locale, matching_types); + // Replace type matches for |NAME_FULL_WITH_HONORIFIC_PREFIX| with |NAME_FULL| + // to always vote for a full name field even if the user decides to add an + // additional honorific prefix to their name. + if (matching_types->contains(NAME_FULL_WITH_HONORIFIC_PREFIX)) { + matching_types->erase(NAME_FULL_WITH_HONORIFIC_PREFIX); + matching_types->insert(NAME_FULL); + } +} + VerificationStatus NameInfo::GetVerificationStatusImpl( ServerFieldType type) const { // TODO(crbug.com/1103421): Clean legacy implementation once structured // names are fully launched. - if (StructuredAddressesEnabled()) - return name_.GetVerificationStatusForType(type); + // Without the second generation of the structured name tree, honorific + // prefixes and the name including the prefix are unsupported types. + if (structured_address::StructuredNamesEnabled() && + !((type == NAME_HONORIFIC_PREFIX || + type == NAME_FULL_WITH_HONORIFIC_PREFIX) && + !structured_address::HonorificPrefixEnabled())) { + return name_->GetVerificationStatusForType(type); + } return VerificationStatus::kNoStatus; } base::string16 NameInfo::FullName() const { // TODO(crbug.com/1103421): Clean legacy implementation once structured // names are fully launched. - if (StructuredAddressesEnabled()) + if (structured_address::StructuredNamesEnabled()) NOTREACHED(); if (!full_.empty()) return full_; @@ -252,7 +282,7 @@ base::string16 NameInfo::FullName() const { base::string16 NameInfo::MiddleInitial() const { // TODO(crbug.com/1103421): Clean legacy implementation once structured // names are fully launched. - if (StructuredAddressesEnabled()) + if (structured_address::StructuredNamesEnabled()) NOTREACHED(); return middle_.empty() ? base::string16() : middle_.substr(0U, 1U); } @@ -260,7 +290,7 @@ base::string16 NameInfo::MiddleInitial() const { void NameInfo::SetFullName(const base::string16& full) { // TODO(crbug.com/1103421): Clean legacy implementation once structured // names are fully launched. - if (StructuredAddressesEnabled()) + if (structured_address::StructuredNamesEnabled()) NOTREACHED(); full_ = full; data_util::NameParts parts = data_util::SplitName(full); diff --git a/chromium/components/autofill/core/browser/data_model/contact_info.h b/chromium/components/autofill/core/browser/data_model/contact_info.h index 868595ac103..818dbed3b7e 100644 --- a/chromium/components/autofill/core/browser/data_model/contact_info.h +++ b/chromium/components/autofill/core/browser/data_model/contact_info.h @@ -5,6 +5,7 @@ #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_CONTACT_INFO_H_ #define COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_CONTACT_INFO_H_ +#include <memory> #include <string> #include <vector> @@ -31,6 +32,10 @@ class NameInfo : public FormGroup { // FormGroup: base::string16 GetRawInfo(ServerFieldType type) const override; + void GetMatchingTypes(const base::string16& text, + const std::string& app_locale, + ServerFieldTypeSet* matching_types) const override; + void SetRawInfoWithVerificationStatus( ServerFieldType type, const base::string16& value, @@ -65,8 +70,8 @@ class NameInfo : public FormGroup { void MergeStructuredNameValidationStatuses(const NameInfo& newer); // Returns a constant reference to the structured name tree. - const structured_address::NameFull& GetStructuredName() const { - return name_; + const structured_address::AddressComponent& GetStructuredName() const { + return *name_; } private: @@ -105,7 +110,7 @@ class NameInfo : public FormGroup { // This data structure stores the more-structured representation of the name // when |features::kAutofillEnableSupportForMoreStructureInNames| is enabled. - structured_address::NameFull name_; + const std::unique_ptr<structured_address::AddressComponent> name_; }; class EmailInfo : public FormGroup { diff --git a/chromium/components/autofill/core/browser/data_model/contact_info_unittest.cc b/chromium/components/autofill/core/browser/data_model/contact_info_unittest.cc index b600471f24d..65385838a4f 100644 --- a/chromium/components/autofill/core/browser/data_model/contact_info_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/contact_info_unittest.cc @@ -12,6 +12,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" +#include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h" @@ -53,46 +54,110 @@ TEST_P(SetFullNameTest, SetFullName) { name.GetInfo(AutofillType(NAME_FULL), "en-US")); } +TEST(NameInfoTest, GetMatchingTypesForStructuredNameWithPrefix) { + base::test::ScopedFeatureList structured_name_feature; + structured_name_feature.InitWithFeatures( + {features::kAutofillEnableSupportForMoreStructureInNames, + features::kAutofillEnableSupportForHonorificPrefixes}, + {}); + + NameInfo name; + test::FormGroupValues name_values = { + {.type = NAME_FULL_WITH_HONORIFIC_PREFIX, + .value = "Mr. Pablo Diego Ruiz y Picasso", + .verification_status = VerificationStatus::kObserved}}; + test::SetFormGroupValues(name, name_values); + name.FinalizeAfterImport(); + + test::FormGroupValues expectation = { + {.type = NAME_FULL_WITH_HONORIFIC_PREFIX, + .value = "Mr. Pablo Diego Ruiz y Picasso", + .verification_status = VerificationStatus::kObserved}, + {.type = NAME_HONORIFIC_PREFIX, + .value = "Mr.", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_FIRST, + .value = "Pablo Diego", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_MIDDLE, + .value = "", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_LAST_FIRST, + .value = "Ruiz", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_LAST_SECOND, + .value = "Picasso", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_LAST_CONJUNCTION, + .value = "y", + .verification_status = VerificationStatus::kParsed}}; + + test::VerifyFormGroupValues(name, expectation); + + ServerFieldTypeSet matching_types; + name.GetMatchingTypes(base::ASCIIToUTF16("Ruiz"), "US", &matching_types); + EXPECT_EQ(matching_types, ServerFieldTypeSet({NAME_LAST_FIRST})); + + name.GetMatchingTypes(base::ASCIIToUTF16("Mr."), "US", &matching_types); + EXPECT_EQ(matching_types, + ServerFieldTypeSet({NAME_LAST_FIRST, NAME_HONORIFIC_PREFIX})); + + // Verify that a field filled with |NAME_FULL_WITH_HONORIFIC_PREFIX| creates a + // |NAME_FULL| vote. + name.GetMatchingTypes(base::ASCIIToUTF16("Mr. Pablo Diego Ruiz y Picasso"), + "US", &matching_types); + EXPECT_EQ(matching_types, ServerFieldTypeSet({NAME_FULL, NAME_LAST_FIRST, + NAME_HONORIFIC_PREFIX})); +} + TEST(NameInfoTest, GetMatchingTypesForStructuredName) { base::test::ScopedFeatureList structured_name_feature; - structured_name_feature.InitAndEnableFeature( - features::kAutofillEnableSupportForMoreStructureInNames); + structured_name_feature.InitWithFeatures( + {features::kAutofillEnableSupportForMoreStructureInNames}, + {features::kAutofillEnableSupportForHonorificPrefixes}); NameInfo name; - name.SetRawInfoWithVerificationStatus( - NAME_FULL, base::ASCIIToUTF16("Mr. Pablo Diego Ruiz y Picasso"), - VerificationStatus::kObserved); + + test::FormGroupValues name_values = { + {.type = NAME_FULL, + .value = "Mr. Pablo Diego Ruiz y Picasso", + .verification_status = VerificationStatus::kObserved}}; + test::SetFormGroupValues(name, name_values); name.FinalizeAfterImport(); - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - // EXPECT_EQ(name.GetRawInfo(NAME_HONORIFIC_PREFIX), - // base::ASCIIToUTF16("Mr.")); - EXPECT_EQ(name.GetRawInfo(NAME_FIRST), base::ASCIIToUTF16("Pablo Diego")); - EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), base::ASCIIToUTF16("")); - EXPECT_EQ(name.GetRawInfo(NAME_LAST), base::ASCIIToUTF16("Ruiz y Picasso")); - EXPECT_EQ(name.GetRawInfo(NAME_LAST_FIRST), base::ASCIIToUTF16("Ruiz")); - EXPECT_EQ(name.GetRawInfo(NAME_LAST_SECOND), base::ASCIIToUTF16("Picasso")); - EXPECT_EQ(name.GetRawInfo(NAME_LAST_CONJUNCTION), base::ASCIIToUTF16("y")); - - // TODO(crbug.com/1113617): Honorifics are temporally disabled. - // EXPECT_EQ(name.GetVerificationStatus(NAME_HONORIFIC_PREFIX), - // VerificationStatus::kParsed); - EXPECT_EQ(name.GetVerificationStatus(NAME_FIRST), - VerificationStatus::kParsed); - EXPECT_EQ(name.GetVerificationStatus(NAME_MIDDLE), - VerificationStatus::kParsed); - EXPECT_EQ(name.GetVerificationStatus(NAME_LAST), VerificationStatus::kParsed); - EXPECT_EQ(name.GetVerificationStatus(NAME_LAST_FIRST), - VerificationStatus::kParsed); - EXPECT_EQ(name.GetVerificationStatus(NAME_LAST_SECOND), - VerificationStatus::kParsed); - EXPECT_EQ(name.GetVerificationStatus(NAME_LAST_CONJUNCTION), - VerificationStatus::kParsed); + test::FormGroupValues expectation = { + {.type = NAME_HONORIFIC_PREFIX, + .value = "", + .verification_status = VerificationStatus::kNoStatus}, + {.type = NAME_FIRST, + .value = "Pablo Diego", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_MIDDLE, + .value = "", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_LAST, + .value = "Ruiz y Picasso", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_LAST_FIRST, + .value = "Ruiz", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_LAST_SECOND, + .value = "Picasso", + .verification_status = VerificationStatus::kParsed}, + {.type = NAME_LAST_CONJUNCTION, + .value = "y", + .verification_status = VerificationStatus::kParsed}}; + + test::VerifyFormGroupValues(name, expectation); ServerFieldTypeSet matching_types; name.GetMatchingTypes(base::ASCIIToUTF16("Ruiz"), "US", &matching_types); EXPECT_EQ(matching_types, ServerFieldTypeSet({NAME_LAST_FIRST})); + // The honorific prefix is ignored. name.GetMatchingTypes(base::ASCIIToUTF16("Mr."), "US", &matching_types); EXPECT_EQ(matching_types, ServerFieldTypeSet({NAME_LAST_FIRST})); } @@ -145,10 +210,6 @@ TEST(NameInfoTest, GetFullName) { EXPECT_EQ(ASCIIToUTF16("First"), name.GetRawInfo(NAME_FIRST)); EXPECT_EQ(base::string16(), name.GetRawInfo(NAME_MIDDLE)); EXPECT_EQ(base::string16(), name.GetRawInfo(NAME_LAST)); - // For structured names, the full must contain all of its subcomponents. - EXPECT_EQ(structured_address::StructuredNamesEnabled() ? ASCIIToUTF16("First") - : base::string16(), - name.GetRawInfo(NAME_FULL)); EXPECT_EQ(ASCIIToUTF16("First"), name.GetInfo(AutofillType(NAME_FULL), "en-US")); diff --git a/chromium/components/autofill/core/browser/data_model/credit_card.cc b/chromium/components/autofill/core/browser/data_model/credit_card.cc index 484b13711c5..c775eb2c686 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card.cc +++ b/chromium/components/autofill/core/browser/data_model/credit_card.cc @@ -356,7 +356,7 @@ bool CreditCard::IsDeletable() const { } base::string16 CreditCard::GetRawInfo(ServerFieldType type) const { - DCHECK_EQ(CREDIT_CARD, AutofillType(type).group()); + DCHECK_EQ(FieldTypeGroup::kCreditCard, AutofillType(type).group()); switch (type) { case CREDIT_CARD_NAME_FULL: return name_on_card_; @@ -411,7 +411,7 @@ base::string16 CreditCard::GetRawInfo(ServerFieldType type) const { void CreditCard::SetRawInfoWithVerificationStatus(ServerFieldType type, const base::string16& value, VerificationStatus status) { - DCHECK_EQ(CREDIT_CARD, AutofillType(type).group()); + DCHECK_EQ(FieldTypeGroup::kCreditCard, AutofillType(type).group()); switch (type) { case CREDIT_CARD_NAME_FULL: name_on_card_ = value; @@ -799,11 +799,9 @@ const std::pair<base::string16, base::string16> CreditCard::LabelPieces() if (number().empty()) { // No CC number, if valid nickname is present, return nickname only. // Otherwise, return cardholder name only. - if (base::FeatureList::IsEnabled( - features::kAutofillEnableCardNicknameManagement) && - HasNonEmptyValidNickname()) { + if (HasNonEmptyValidNickname()) return std::make_pair(nickname_, base::string16()); - } + return std::make_pair(name_on_card_, base::string16()); } @@ -865,12 +863,15 @@ base::string16 CreditCard::CardIdentifierStringForAutofillDisplay( if (HasNonEmptyValidNickname() || !customized_nickname.empty()) { return NicknameAndLastFourDigits(customized_nickname); } - // Return a Google-specific string for Google-issued cards. + base::string16 networkAndLastFourDigits = NetworkAndLastFourDigits(); + // Add Plex before the network and last four digits to identify it as a Google + // Plex card. if (base::FeatureList::IsEnabled(features::kAutofillEnableGoogleIssuedCard) && IsGoogleIssuedCard()) { - return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_GOOGLE_ISSUED); + return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_GOOGLE_ISSUED) + + ASCIIToUTF16(" ") + networkAndLastFourDigits; } - return NetworkAndLastFourDigits(); + return networkAndLastFourDigits; } base::string16 CreditCard::CardIdentifierStringAndDescriptiveExpiration( diff --git a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc index 2990a9a4360..21923678ee9 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc @@ -100,11 +100,7 @@ TEST(CreditCardTest, GetObfuscatedStringForCardDigits) { // of different possible summary strings. Variations occur based on the // existence of credit card number, month, and year fields. TEST(CreditCardTest, PreviewSummaryAndNetworkAndLastFourDigitsStrings) { - base::test::ScopedFeatureList scoped_feature_list; base::string16 valid_nickname = ASCIIToUTF16("My Visa Card"); - // Enable the flag. - scoped_feature_list.InitAndEnableFeature( - features::kAutofillEnableCardNicknameManagement); // Case 0: empty credit card. CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com/"); @@ -205,20 +201,6 @@ TEST(CreditCardTest, PreviewSummaryAndNetworkAndLastFourDigitsStrings) { UTF8ToUTF16(std::string(" ") + test::ObfuscatedCardDigitsAsUTF8("5100") + ", 01/2010"), summary6); - - // Case 7: No credit card number, has valid nickname, but flag is off. - // Reset and disable the nickname feature flags. - scoped_feature_list.Reset(); - scoped_feature_list.InitAndDisableFeature( - features::kAutofillEnableCardNicknameManagement); - CreditCard credit_card7(base::GenerateGUID(), "https://www.example.com/"); - test::SetCreditCardInfo(&credit_card7, "John Dillinger", "", "01", "2010", - "1"); - credit_card7.SetNickname(valid_nickname); - base::string16 summary7 = credit_card11.Label(); - EXPECT_EQ(base::string16(ASCIIToUTF16("John Dillinger")), summary7); - base::string16 obfuscated7 = credit_card7.NetworkAndLastFourDigits(); - EXPECT_EQ(ASCIIToUTF16(std::string("Card")), obfuscated7); } TEST(CreditCardTest, NicknameAndLastFourDigitsStrings) { @@ -283,7 +265,9 @@ TEST(CreditCardTest, CardIdentifierStringForIssuedCard) { test::SetCreditCardInfo(&credit_card1, "John Dillinger", "5105 1051 0510 5100" /* Mastercard */, "01", "2020", "1"); - EXPECT_EQ(l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_GOOGLE_ISSUED), + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_GOOGLE_ISSUED) + + UTF8ToUTF16(std::string(" Mastercard ") + + test::ObfuscatedCardDigitsAsUTF8("5100")), credit_card1.CardIdentifierStringForAutofillDisplay()); // Case 2: Card Issuer set to GOOGLE with nickname. diff --git a/chromium/components/autofill/core/browser/data_model/phone_number.cc b/chromium/components/autofill/core/browser/data_model/phone_number.cc index c9923332beb..3eb07f06293 100644 --- a/chromium/components/autofill/core/browser/data_model/phone_number.cc +++ b/chromium/components/autofill/core/browser/data_model/phone_number.cc @@ -74,7 +74,7 @@ void PhoneNumber::GetSupportedTypes(ServerFieldTypeSet* supported_types) const { } base::string16 PhoneNumber::GetRawInfo(ServerFieldType type) const { - DCHECK_EQ(PHONE_HOME, AutofillType(type).group()); + DCHECK_EQ(FieldTypeGroup::kPhoneHome, AutofillType(type).group()); if (type == PHONE_HOME_WHOLE_NUMBER) return number_; @@ -87,7 +87,7 @@ base::string16 PhoneNumber::GetRawInfo(ServerFieldType type) const { void PhoneNumber::SetRawInfoWithVerificationStatus(ServerFieldType type, const base::string16& value, VerificationStatus status) { - DCHECK_EQ(PHONE_HOME, AutofillType(type).group()); + DCHECK_EQ(FieldTypeGroup::kPhoneHome, AutofillType(type).group()); if (type != PHONE_HOME_CITY_AND_NUMBER && type != PHONE_HOME_WHOLE_NUMBER) { // Only full phone numbers should be set directly. The remaining field // field types are read-only. diff --git a/chromium/components/autofill/core/browser/field_filler.cc b/chromium/components/autofill/core/browser/field_filler.cc index 16ada9dbb6c..0e574dd27f0 100644 --- a/chromium/components/autofill/core/browser/field_filler.cc +++ b/chromium/components/autofill/core/browser/field_filler.cc @@ -19,14 +19,14 @@ #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/data_model/autofill_data_model.h" -#include "components/autofill/core/browser/data_model/autofill_profile.h" -#include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/data_model/data_model_utils.h" #include "components/autofill/core/browser/data_model/phone_number.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map.h" #include "components/autofill/core/browser/geo/autofill_country.h" #include "components/autofill/core/browser/geo/country_names.h" #include "components/autofill/core/browser/geo/state_names.h" +#include "components/autofill/core/browser/proto/states.pb.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_l10n_util.h" #include "components/autofill/core/common/autofill_util.h" @@ -251,35 +251,71 @@ bool FillStateSelectControl(const base::string16& value, const std::string& country_code, AddressNormalizer* address_normalizer, std::string* failure_to_fill) { + std::vector<base::string16> abbreviations; + std::vector<base::string16> full_names; + + if (base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap)) { + // Fetch the corresponding entry from AlternativeStateNameMap. + base::Optional<StateEntry> state_entry = + AlternativeStateNameMap::GetInstance()->GetEntry( + AlternativeStateNameMap::CountryCode(country_code), + AlternativeStateNameMap::StateName(value)); + if (state_entry) { + for (const auto& abbr : state_entry->abbreviations()) + abbreviations.push_back(base::UTF8ToUTF16(abbr)); + if (state_entry->has_canonical_name()) + full_names.push_back(base::UTF8ToUTF16(state_entry->canonical_name())); + for (const auto& alternative_name : state_entry->alternative_names()) + full_names.push_back(base::UTF8ToUTF16(alternative_name)); + } else { + full_names.push_back(value); + } + } + base::string16 full; base::string16 abbreviation; // |abbreviation| will be empty for non-US countries. state_names::GetNameAndAbbreviation(value, &full, &abbreviation); + if (!full.empty()) + full_names.push_back(std::move(full)); + + if (!abbreviation.empty()) + abbreviations.push_back(std::move(abbreviation)); + // Try an exact match of the abbreviation first. - if (!abbreviation.empty() && - SetSelectControlValue(abbreviation, field, /*best_match_index=*/nullptr, - failure_to_fill)) { - return true; + for (const auto& abbreviation : abbreviations) { + if (!abbreviation.empty() && + SetSelectControlValue(abbreviation, field, /*best_match_index=*/nullptr, + failure_to_fill)) { + return true; + } } // Try an exact match of the full name. - if (!full.empty() && - SetSelectControlValue(full, field, /*best_match_index=*/nullptr, - failure_to_fill)) { - return true; + for (const auto& full : full_names) { + if (!full.empty() && + SetSelectControlValue(full, field, /*best_match_index=*/nullptr, + failure_to_fill)) { + return true; + } } // Try an inexact match of the full name. - if (!full.empty() && SetSelectControlValueSubstringMatch(full, false, field, - failure_to_fill)) { - return true; + for (const auto& full : full_names) { + if (!full.empty() && SetSelectControlValueSubstringMatch(full, false, field, + failure_to_fill)) { + return true; + } } // Try an inexact match of the abbreviation name. - if (!abbreviation.empty() && - SetSelectControlValueTokenMatch(abbreviation, field, failure_to_fill)) { - return true; + for (const auto& abbreviation : abbreviations) { + if (!abbreviation.empty() && + SetSelectControlValueTokenMatch(abbreviation, field, failure_to_fill)) { + return true; + } } // Try to match a normalized |value| of the state and the |field| options. @@ -503,9 +539,10 @@ void FillCreditCardNumberField(const AutofillField& field, // found, falls back to alternate filling strategies based on the |type|. bool FillSelectControl(const AutofillType& type, const base::string16& value, - FormFieldData* field, - const AutofillDataModel& data_model, + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card, const std::string& app_locale, + FormFieldData* field, AddressNormalizer* address_normalizer, std::string* failure_to_fill) { DCHECK_EQ("select-one", field->form_control_type); @@ -534,9 +571,10 @@ bool FillSelectControl(const AutofillType& type, // If that fails, try specific fallbacks based on the field type. if (storable_type == ADDRESS_HOME_STATE) { - // Safe to cast the data model to AutofillProfile here. + DCHECK(absl::holds_alternative<const AutofillProfile*>( + profile_or_credit_card)); const std::string country_code = data_util::GetCountryCodeWithFallback( - static_cast<const AutofillProfile&>(data_model), app_locale); + *absl::get<const AutofillProfile*>(profile_or_credit_card), app_locale); return FillStateSelectControl(value, field, country_code, address_normalizer, failure_to_fill); } @@ -580,10 +618,17 @@ void FillStreetAddress(const base::string16& value, } // Returns whether the |field| was filled with the state in |value| or its -// abbreviation. First looks if |value| fits directly in the field, then looks -// if the abbreviation of |value| fits. Does not fill if neither |value| or its -// abbreviation are too long for the field. +// canonical state name or the abbreviation. +// First looks if |value| fits directly in the field, then looks if the +// abbreviation of |value| fits in case the +// |features::kAutofillUseAlternativeStateNameMap| is disabled. +// If the |features::kAutofillUseAlternativeStateNameMap| is enabled, the +// canonical state is checked if it fits in the field and at last the +// abbreviations are tried. +// Does not fill if neither |value| nor the canonical state name nor its +// abbreviation fit into the field. bool FillStateText(const base::string16& value, + const std::string& country_code, FormFieldData* field, std::string* failure_to_fill) { if (field->max_length == 0 || field->max_length >= value.size()) { @@ -591,6 +636,31 @@ bool FillStateText(const base::string16& value, field->value = value; return true; } + + if (base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap)) { + base::Optional<StateEntry> state = + AlternativeStateNameMap::GetInstance()->GetEntry( + AlternativeStateNameMap::CountryCode(country_code), + AlternativeStateNameMap::StateName(value)); + if (state) { + // Fill with the canonical state name if possible. + if (state->has_canonical_name() && !state->canonical_name().empty() && + field->max_length >= state->canonical_name().size()) { + field->value = base::UTF8ToUTF16(state->canonical_name()); + return true; + } + + // Fill with the abbreviation if possible. + for (const auto& abbr : state->abbreviations()) { + if (!abbr.empty() && field->max_length >= abbr.size()) { + field->value = base::i18n::ToUpper(base::UTF8ToUTF16(abbr)); + return true; + } + } + } + } + // Fill with the state abbreviation. base::string16 abbreviation; state_names::GetNameAndAbbreviation(value, nullptr, &abbreviation); @@ -598,6 +668,7 @@ bool FillStateText(const base::string16& value, field->value = base::i18n::ToUpper(abbreviation); return true; } + if (failure_to_fill) *failure_to_fill += "Could not fit raw state nor abbreviation. "; return false; @@ -743,32 +814,36 @@ FieldFiller::FieldFiller(const std::string& app_locale, FieldFiller::~FieldFiller() {} -bool FieldFiller::FillFormField(const AutofillField& field, - const AutofillDataModel& data_model, - FormFieldData* field_data, - const base::string16& cvc, - std::string* failure_to_fill) { +bool FieldFiller::FillFormField( + const AutofillField& field, + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card, + FormFieldData* field_data, + const base::string16& cvc, + std::string* failure_to_fill) { const AutofillType type = field.Type(); - // Don't fill if autocomplete=off is set on |field| on desktop for non credit - // card related fields. - if (!base::FeatureList::IsEnabled(features::kAutofillAlwaysFillAddresses) && - !field.should_autocomplete && IsDesktopPlatform() && - (type.group() != CREDIT_CARD)) { - if (failure_to_fill) - *failure_to_fill += "autocomplete=off. "; - return false; - } + base::string16 value; + if (absl::holds_alternative<const AutofillProfile*>(profile_or_credit_card)) { + const AutofillProfile* profile = + absl::get<const AutofillProfile*>(profile_or_credit_card); + if (profile->ShouldSkipFillingOrSuggesting(type.GetStorableType())) { + if (failure_to_fill) + *failure_to_fill += "ShouldSkipFillingOrSuggesting() returned true. "; + return false; + } - if (data_model.ShouldSkipFillingOrSuggesting(type.GetStorableType())) { - if (failure_to_fill) - *failure_to_fill += "ShouldSkipFillingOrSuggesting() returned true. "; - return false; - } + value = profile->GetInfo(type, app_locale_); + } else { + DCHECK(absl::holds_alternative<const CreditCard*>(profile_or_credit_card)); - base::string16 value = data_model.GetInfo(type, app_locale_); - if (type.GetStorableType() == CREDIT_CARD_VERIFICATION_CODE) - value = cvc; + if (type.GetStorableType() == CREDIT_CARD_VERIFICATION_CODE) { + value = cvc; + } else { + value = absl::get<const CreditCard*>(profile_or_credit_card) + ->GetInfo(type, app_locale_); + } + } // Do not attempt to fill empty values as it would skew the metrics. if (value.empty()) { @@ -777,7 +852,7 @@ bool FieldFiller::FillFormField(const AutofillField& field, return false; } - if (type.group() == PHONE_HOME) { + if (type.group() == FieldTypeGroup::kPhoneHome) { // If the |field_data| is a selection box and having the type // |PHONE_HOME_COUNTRY_CODE|, call |FillPhoneCountryCodeSelectControl|. if (base::FeatureList::IsEnabled( @@ -791,19 +866,21 @@ bool FieldFiller::FillFormField(const AutofillField& field, return true; } if (field_data->form_control_type == "select-one") { - return FillSelectControl(type, value, field_data, data_model, app_locale_, - address_normalizer_, failure_to_fill); + return FillSelectControl(type, value, profile_or_credit_card, app_locale_, + field_data, address_normalizer_, failure_to_fill); } if (field_data->form_control_type == "month") { - // Safe to cast to CreditCard here because month control type only applying - // to credit card expirations. - FillMonthControl(static_cast<const CreditCard&>(data_model), field_data); + DCHECK(absl::holds_alternative<const CreditCard*>(profile_or_credit_card)); + FillMonthControl(*absl::get<const CreditCard*>(profile_or_credit_card), + field_data); return true; } if (type.GetStorableType() == ADDRESS_HOME_STREET_ADDRESS) { - // Safe to cast to AutofillProfile here because of the address |type|. - const std::string profile_language_code = - static_cast<const AutofillProfile&>(data_model).language_code(); + DCHECK(absl::holds_alternative<const AutofillProfile*>( + profile_or_credit_card)); + const std::string& profile_language_code = + absl::get<const AutofillProfile*>(profile_or_credit_card) + ->language_code(); FillStreetAddress(value, profile_language_code, field_data); return true; } @@ -811,8 +888,14 @@ bool FieldFiller::FillFormField(const AutofillField& field, FillCreditCardNumberField(field, value, field_data); return true; } - if (type.GetStorableType() == ADDRESS_HOME_STATE) - return FillStateText(value, field_data, failure_to_fill); + if (type.GetStorableType() == ADDRESS_HOME_STATE) { + DCHECK(absl::holds_alternative<const AutofillProfile*>( + profile_or_credit_card)); + const std::string& country_code = data_util::GetCountryCodeWithFallback( + *absl::get<const AutofillProfile*>(profile_or_credit_card), + app_locale_); + return FillStateText(value, country_code, field_data, failure_to_fill); + } if (field_data->form_control_type == "text" && (type.GetStorableType() == CREDIT_CARD_EXP_2_DIGIT_YEAR || type.GetStorableType() == CREDIT_CARD_EXP_4_DIGIT_YEAR)) { diff --git a/chromium/components/autofill/core/browser/field_filler.h b/chromium/components/autofill/core/browser/field_filler.h index 85ddb2b56a1..d549587b980 100644 --- a/chromium/components/autofill/core/browser/field_filler.h +++ b/chromium/components/autofill/core/browser/field_filler.h @@ -9,12 +9,14 @@ #include "base/macros.h" #include "base/strings/string16.h" +#include "components/autofill/core/browser/data_model/autofill_profile.h" +#include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/common/form_field_data.h" +#include "third_party/abseil-cpp/absl/types/variant.h" namespace autofill { class AddressNormalizer; -class AutofillDataModel; class AutofillField; // Helper class to put user content in fields, to eventually send to the @@ -25,14 +27,14 @@ class FieldFiller { AddressNormalizer* address_normalizer); ~FieldFiller(); - // Set |field_data|'s value to the right value in |data_model|. Uses |field| - // to determine which field type should be filled, and |app_locale_| as hint - // when filling exceptional cases like phone number values. Returns |true| if - // the field has been filled, false otherwise. |cvc| is not stored in the - // data model and may be needed at fill time. If |failure_to_fill| is not - // null, errors are reported to that string. + // Set |field_data|'s value to the right value in |profile_or_credit_card|. + // Uses |field| to determine which field type should be filled, and + // |app_locale_| as hint when filling exceptional cases like phone number + // values. Returns |true| if the field has been filled, false otherwise. If + // |failure_to_fill| is not null, errors are reported to that string. bool FillFormField(const AutofillField& field, - const AutofillDataModel& data_model, + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card, FormFieldData* field_data, const base::string16& cvc, std::string* failure_to_fill = nullptr); diff --git a/chromium/components/autofill/core/browser/field_filler_unittest.cc b/chromium/components/autofill/core/browser/field_filler_unittest.cc index 02f1c95c11f..1d32da08cfc 100644 --- a/chromium/components/autofill/core/browser/field_filler_unittest.cc +++ b/chromium/components/autofill/core/browser/field_filler_unittest.cc @@ -26,6 +26,7 @@ #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h" #include "components/autofill/core/browser/geo/country_names.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_util.h" @@ -100,13 +101,13 @@ void TestFillingExpirationMonth(const std::vector<const char*>& values, // Try a single-digit month. CreditCard card = test::GetCreditCard(); card.SetExpirationMonth(3); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); GetIndexOfValue(field.option_values, field.value, &content_index); EXPECT_EQ(ASCIIToUTF16("Mar"), field.option_contents[content_index]); // Try a two-digit month. card.SetExpirationMonth(11); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); GetIndexOfValue(field.option_values, field.value, &content_index); EXPECT_EQ(ASCIIToUTF16("Nov"), field.option_contents[content_index]); } @@ -125,11 +126,11 @@ void TestFillingInvalidFields(const base::string16& state, field_city.set_heuristic_type(ADDRESS_HOME_CITY); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field_state, profile, &field_state, + filler.FillFormField(field_state, &profile, &field_state, /*cvc=*/base::string16()); EXPECT_EQ(state, field_state.value); - filler.FillFormField(field_city, profile, &field_city, + filler.FillFormField(field_city, &profile, &field_city, /*cvc=*/base::string16()); EXPECT_EQ(city, field_city.value); } @@ -177,37 +178,37 @@ TEST_F(AutofillFieldFillerTest, Type) { // Set the heuristic type and check it. field.set_heuristic_type(NAME_FIRST); EXPECT_EQ(NAME_FIRST, field.Type().GetStorableType()); - EXPECT_EQ(NAME, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kName, field.Type().group()); // Set the server type and check it. field.set_server_type(ADDRESS_BILLING_LINE1); EXPECT_EQ(ADDRESS_HOME_LINE1, field.Type().GetStorableType()); - EXPECT_EQ(ADDRESS_BILLING, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group()); // Checks that overall_type trumps everything. field.SetTypeTo(AutofillType(ADDRESS_BILLING_ZIP)); EXPECT_EQ(ADDRESS_HOME_ZIP, field.Type().GetStorableType()); - EXPECT_EQ(ADDRESS_BILLING, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group()); // Checks that setting server type resets overall type. field.set_server_type(ADDRESS_BILLING_LINE1); EXPECT_EQ(ADDRESS_HOME_LINE1, field.Type().GetStorableType()); - EXPECT_EQ(ADDRESS_BILLING, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group()); // Remove the server type to make sure the heuristic type is preserved. field.set_server_type(NO_SERVER_DATA); EXPECT_EQ(NAME_FIRST, field.Type().GetStorableType()); - EXPECT_EQ(NAME, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kName, field.Type().group()); // Checks that overall_type trumps everything. field.SetTypeTo(AutofillType(ADDRESS_BILLING_ZIP)); EXPECT_EQ(ADDRESS_HOME_ZIP, field.Type().GetStorableType()); - EXPECT_EQ(ADDRESS_BILLING, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group()); // Set the heuristic type and check it and reset overall Type. field.set_heuristic_type(NAME_FIRST); EXPECT_EQ(NAME_FIRST, field.Type().GetStorableType()); - EXPECT_EQ(NAME, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kName, field.Type().group()); } // Tests that a credit card related prediction made by the heuristics overrides @@ -354,31 +355,6 @@ TEST_F(AutofillFieldFillerTest, IsFieldFillable) { } // Verify that non credit card related fields with the autocomplete attribute -// set to off don't get filled on desktop when the feature to Autofill all -// addresses is disabled. -TEST_F(AutofillFieldFillerTest, - FillFormField_AutocompleteOffRespected_AddressField) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature(features::kAutofillAlwaysFillAddresses); - - AutofillField field; - field.should_autocomplete = false; - field.set_heuristic_type(NAME_FIRST); - - // Non credit card related field. - address()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("Test")); - FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, *address(), &field, /*cvc=*/base::string16()); - - // Verify that the field is filled on mobile but not on desktop. - if (IsDesktopPlatform()) { - EXPECT_EQ(base::string16(), field.value); - } else { - EXPECT_EQ(ASCIIToUTF16("Test"), field.value); - } -} - -// Verify that non credit card related fields with the autocomplete attribute // set to off are filled on desktop when the feature to Autofill all // addresses is enabled (default). TEST_F(AutofillFieldFillerTest, @@ -390,7 +366,7 @@ TEST_F(AutofillFieldFillerTest, // Non credit card related field. address()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("Test")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, *address(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, address(), &field, /*cvc=*/base::string16()); // Verify that the field is filled in all circumstances. EXPECT_EQ(ASCIIToUTF16("Test"), field.value); @@ -406,7 +382,7 @@ TEST_F(AutofillFieldFillerTest, FillFormField_AutocompleteOff_CreditCardField) { // Credit card related field. credit_card()->SetNumber(ASCIIToUTF16("4111111111111111")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, *credit_card(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, credit_card(), &field, /*cvc=*/base::string16()); // Verify that the field is filled. EXPECT_EQ(ASCIIToUTF16("4111111111111111"), field.value); @@ -424,7 +400,7 @@ TEST_F(AutofillFieldFillerTest, // Credit card related field. credit_card()->SetNumber(ASCIIToUTF16("0123456789999999")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, *credit_card(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, credit_card(), &field, /*cvc=*/base::string16()); // Verify that the field is filled with the fourth digit of the credit card // number. @@ -443,7 +419,7 @@ TEST_F(AutofillFieldFillerTest, // Credit card related field. credit_card()->SetNumber(ASCIIToUTF16("0123456789999999")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, *credit_card(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, credit_card(), &field, /*cvc=*/base::string16()); // Verify that the field is filled with the full credit card number. EXPECT_EQ(ASCIIToUTF16("0123456789999999"), field.value); @@ -461,7 +437,7 @@ TEST_F(AutofillFieldFillerTest, // Credit card related field. credit_card()->SetNumber(ASCIIToUTF16("0123456789999999")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, *credit_card(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, credit_card(), &field, /*cvc=*/base::string16()); // Verify that the field is filled with the third digit of the credit card // number. @@ -477,7 +453,7 @@ TEST_F(AutofillFieldFillerTest, FillFormField_MaxLength_CreditCardField) { // Credit card related field. credit_card()->SetNumber(ASCIIToUTF16("4111111111111111")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, *credit_card(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, credit_card(), &field, /*cvc=*/base::string16()); // Verify that the field is filled with only the first digit of the credit // card number. @@ -549,13 +525,13 @@ TEST_F(AutofillFieldFillerTest, FillFormField_Validity_CountryEmpty) { FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); // State is filled, because it's an address field. - filler.FillFormField(field_state, profile, &field_state, + filler.FillFormField(field_state, &profile, &field_state, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("CA"), field_state.value); // Email is not filled, because it's not an address field, and it doesn't // depend on the country. - filler.FillFormField(field_email, profile, &field_email, + filler.FillFormField(field_email, &profile, &field_email, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16(""), field_email.value); } @@ -581,7 +557,7 @@ TEST_P(PhoneNumberTest, FillPhoneNumber) { AutofillProfile address; address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+15145554578")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); } @@ -631,7 +607,7 @@ TEST_P(ExpirationYearTest, FillExpirationYearInput) { CreditCard card = test::GetCreditCard(); card.SetExpirationDateFromString(ASCIIToUTF16("12/2023")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); } @@ -694,7 +670,7 @@ TEST_P(ExpirationDateTest, FillExpirationDateInput) { card.SetExpirationDateFromString(ASCIIToUTF16("03/2022")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); bool response = - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); EXPECT_EQ(response, test_case.expected_response); } @@ -767,7 +743,10 @@ INSTANTIATE_TEST_SUITE_P( TEST_F(AutofillFieldFillerTest, FillSelectControlByValue) { std::vector<const char*> kOptions = { - "Eenie", "Meenie", "Miney", "Mo", + "Eenie", + "Meenie", + "Miney", + "Mo", }; AutofillField field; @@ -781,13 +760,16 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlByValue) { address()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("Meenie")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, *address(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, address(), &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("Meenie"), field.value); } TEST_F(AutofillFieldFillerTest, FillSelectControlByContents) { std::vector<const char*> kOptions = { - "Eenie", "Meenie", "Miney", "Mo", + "Eenie", + "Meenie", + "Miney", + "Mo", }; AutofillField field; test::CreateTestSelectField(kOptions, &field); @@ -800,7 +782,7 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlByContents) { address()->SetRawInfo(NAME_FIRST, ASCIIToUTF16("Miney")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, *address(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, address(), &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("2"), field.value); // Corresponds to "Miney". } @@ -829,6 +811,17 @@ class AutofillSelectWithStatesTest std::unique_ptr<Source>( new TestdataSource(true, file_path.AsUTF8Unsafe())), std::unique_ptr<Storage>(new NullStorage), "en-US"); + + test::PopulateAlternativeStateNameMapForTesting( + "US", "California", + {{.canonical_name = "California", + .abbreviations = {"CA"}, + .alternative_names = {}}}); + test::PopulateAlternativeStateNameMapForTesting( + "US", "North Carolina", + {{.canonical_name = "North Carolina", + .abbreviations = {"NC"}, + .alternative_names = {}}}); // Make sure the normalizer is done initializing its member(s) in // background task(s). task_environment_.RunUntilIdle(); @@ -854,7 +847,7 @@ TEST_P(AutofillSelectWithStatesTest, FillSelectWithStates) { AutofillProfile address = test::GetFullProfile(); address.SetRawInfo(ADDRESS_HOME_STATE, UTF8ToUTF16(test_case.input_value)); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); // nullptr means we expect them not to match without normalization. if (test_case.expected_value_without_normalization != nullptr) { EXPECT_EQ(UTF8ToUTF16(test_case.expected_value_without_normalization), @@ -867,7 +860,7 @@ TEST_P(AutofillSelectWithStatesTest, FillSelectWithStates) { UTF8ToUTF16(test_case.input_value)); // Fill a first time without loading the rules for the region. FieldFiller canadian_filler(/*app_locale=*/"en-US", normalizer()); - canadian_filler.FillFormField(field, canadian_address, &field, + canadian_filler.FillFormField(field, &canadian_address, &field, /*cvc=*/base::string16()); // If the expectation with normalization is nullptr, this means that the same // result than without a normalizer is expected. @@ -882,7 +875,7 @@ TEST_P(AutofillSelectWithStatesTest, FillSelectWithStates) { // Load the rules and try again. normalizer()->LoadRulesForRegion("CA"); - canadian_filler.FillFormField(field, canadian_address, &field, + canadian_filler.FillFormField(field, &canadian_address, &field, /*cvc=*/base::string16()); EXPECT_EQ(UTF8ToUTF16(test_case.expected_value_with_normalization), field.value); @@ -951,7 +944,7 @@ TEST_F(AutofillFieldFillerTest, FillSelectWithCountries) { AutofillProfile address = test::GetFullProfile(); address.SetRawInfo(ADDRESS_HOME_COUNTRY, UTF8ToUTF16("CA")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); EXPECT_EQ(UTF8ToUTF16("Canada"), field.value); } @@ -1077,7 +1070,7 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithAbbreviatedMonthName) { CreditCard card = test::GetCreditCard(); card.SetExpirationMonth(4); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("Apr"), field.value); } @@ -1093,7 +1086,7 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithMonthName) { CreditCard card = test::GetCreditCard(); card.SetExpirationMonth(4); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("April"), field.value); } @@ -1110,7 +1103,7 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithMonthNameAndDigits) { CreditCard card = test::GetCreditCard(); card.SetExpirationMonth(4); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("April (04)"), field.value); } @@ -1137,10 +1130,10 @@ TEST_F(AutofillFieldFillerTest, CreditCard card = test::GetCreditCard(); card.SetExpirationMonth(8); FieldFiller filler(/*app_locale=*/"fr-FR", /*address_normalizer=*/nullptr); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(UTF8ToUTF16("08 - AOÛT"), field.value); card.SetExpirationMonth(12); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(UTF8ToUTF16("12 - DECEMBRE"), field.value); } @@ -1154,15 +1147,15 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithMonthName_French) { CreditCard card = test::GetCreditCard(); card.SetExpirationMonth(2); FieldFiller filler(/*app_locale=*/"fr-FR", /*address_normalizer=*/nullptr); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(UTF8ToUTF16("FÉVR."), field.value); card.SetExpirationMonth(1); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(UTF8ToUTF16("JANV"), field.value); card.SetExpirationMonth(12); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(UTF8ToUTF16("décembre"), field.value); } @@ -1178,7 +1171,7 @@ TEST_F(AutofillFieldFillerTest, CreditCard card = test::GetCreditCard(); card.SetExpirationMonth(4); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("4"), field.value); } @@ -1192,7 +1185,7 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithTwoDigitCreditCardYear) { CreditCard card = test::GetCreditCard(); card.SetExpirationYear(2017); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("17"), field.value); } @@ -1207,22 +1200,22 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlWithCreditCardType) { // Normal case: card.SetNumber(ASCIIToUTF16("4111111111111111")); // Visa number. - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("Visa"), field.value); // Filling should be able to handle intervening whitespace: card.SetNumber(ASCIIToUTF16("5555555555554444")); // MC number. - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("Mastercard"), field.value); // American Express is sometimes abbreviated as AmEx: card.SetNumber(ASCIIToUTF16("378282246310005")); // Amex number. - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("AmEx"), field.value); // Case insensitivity: card.SetNumber(ASCIIToUTF16("6011111111111117")); // Discover number. - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("discover"), field.value); } @@ -1235,12 +1228,12 @@ TEST_F(AutofillFieldFillerTest, FillMonthControl) { // Try a month with two digits. CreditCard card = test::GetCreditCard(); card.SetExpirationDateFromString(ASCIIToUTF16("12/2017")); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("2017-12"), field.value); // Try a month with a leading zero. card.SetExpirationDateFromString(ASCIIToUTF16("03/2019")); - filler.FillFormField(field, card, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &card, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("2019-03"), field.value); } @@ -1254,7 +1247,7 @@ TEST_F(AutofillFieldFillerTest, FillStreetAddressTextArea) { "123 Fake St.\n" "Apt. 42"); address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), value, "en-US"); - filler.FillFormField(field, *address(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, address(), &field, /*cvc=*/base::string16()); EXPECT_EQ(value, field.value); base::string16 ja_value = UTF8ToUTF16( @@ -1263,7 +1256,7 @@ TEST_F(AutofillFieldFillerTest, FillStreetAddressTextArea) { address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), ja_value, "ja-JP"); address()->set_language_code("ja-JP"); - filler.FillFormField(field, *address(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, address(), &field, /*cvc=*/base::string16()); EXPECT_EQ(ja_value, field.value); } @@ -1277,7 +1270,7 @@ TEST_F(AutofillFieldFillerTest, FillStreetAddressTextField) { "123 Fake St.\n" "Apt. 42"); address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), value, "en-US"); - filler.FillFormField(field, *address(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, address(), &field, /*cvc=*/base::string16()); EXPECT_EQ(UTF8ToUTF16("123 Fake St., Apt. 42"), field.value); base::string16 ja_value = UTF8ToUTF16( @@ -1286,7 +1279,7 @@ TEST_F(AutofillFieldFillerTest, FillStreetAddressTextField) { address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), ja_value, "ja-JP"); address()->set_language_code("ja-JP"); - filler.FillFormField(field, *address(), &field, /*cvc=*/base::string16()); + filler.FillFormField(field, address(), &field, /*cvc=*/base::string16()); EXPECT_EQ(UTF8ToUTF16("桜丘町26-1セルリアンタワー6階"), field.value); } @@ -1297,7 +1290,7 @@ TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithoutSplits) { credit_card()->SetNumber(ASCIIToUTF16("41111111111111111")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(cc_number_full, *credit_card(), &cc_number_full, + filler.FillFormField(cc_number_full, credit_card(), &cc_number_full, /*cvc=*/base::string16()); // Verify that full card-number shall get filled properly. @@ -1325,7 +1318,7 @@ TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithEqualSizeSplits) { // Fill with a card-number; should fill just the card_number_part. credit_card()->SetNumber(ASCIIToUTF16(test.card_number_)); - filler.FillFormField(cc_number_part, *credit_card(), &cc_number_part, + filler.FillFormField(cc_number_part, credit_card(), &cc_number_part, /*cvc=*/base::string16()); // Verify for expected results. @@ -1339,7 +1332,7 @@ TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithEqualSizeSplits) { cc_number_full.set_heuristic_type(CREDIT_CARD_NUMBER); credit_card()->SetNumber(ASCIIToUTF16(test.card_number_)); - filler.FillFormField(cc_number_full, *credit_card(), &cc_number_full, + filler.FillFormField(cc_number_full, credit_card(), &cc_number_full, /*cvc=*/base::string16()); // Verify for expected results. @@ -1368,7 +1361,7 @@ TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithUnequalSizeSplits) { // Fill with a card-number; should fill just the card_number_part. credit_card()->SetNumber(ASCIIToUTF16(test.card_number_)); - filler.FillFormField(cc_number_part, *credit_card(), &cc_number_part, + filler.FillFormField(cc_number_part, credit_card(), &cc_number_part, /*cvc=*/base::string16()); // Verify for expected results. @@ -1382,7 +1375,7 @@ TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithUnequalSizeSplits) { AutofillField cc_number_full; cc_number_full.set_heuristic_type(CREDIT_CARD_NUMBER); credit_card()->SetNumber(ASCIIToUTF16(test.card_number_)); - filler.FillFormField(cc_number_full, *credit_card(), &cc_number_full, + filler.FillFormField(cc_number_full, credit_card(), &cc_number_full, /*cvc=*/base::string16()); // Verify for expected results. @@ -1457,7 +1450,7 @@ TEST_P(AutofillStateTextTest, FillStateText) { AutofillProfile address = test::GetFullProfile(); address.SetRawInfo(ADDRESS_HOME_STATE, UTF8ToUTF16(test_case.value_to_fill)); bool has_filled = - filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); EXPECT_EQ(test_case.should_fill, has_filled); EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); @@ -1511,7 +1504,7 @@ TEST_F(AutofillFieldFillerTest, AutofillProfile address; address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+15145554578")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("1"), field.value); } @@ -1532,7 +1525,7 @@ TEST_F(AutofillFieldFillerTest, AutofillProfile address; address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+918890888888")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("+91"), field.value); } @@ -1553,7 +1546,7 @@ TEST_F(AutofillFieldFillerTest, AutofillProfile address; address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+918890888888")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("0091"), field.value); } @@ -1574,7 +1567,7 @@ TEST_F(AutofillFieldFillerTest, FillSelectControlAugmentedPhoneCountryCode) { AutofillProfile address; address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+49151669087345")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("+49 (Germany)"), field.value); } @@ -1596,7 +1589,7 @@ TEST_F(AutofillFieldFillerTest, AutofillProfile address; address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+49151669087345")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("(00 49) Germany"), field.value); } @@ -1619,8 +1612,199 @@ TEST_F(AutofillFieldFillerTest, AutofillProfile address; address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("+49151669087345")); FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); - filler.FillFormField(field, address, &field, /*cvc=*/base::string16()); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); EXPECT_EQ(ASCIIToUTF16("(0049) Germany"), field.value); } +// Tests that the abbreviated state names are selected correctly. +TEST_F(AutofillFieldFillerTest, FillSelectAbbreviatedState) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillUseAlternativeStateNameMap); + + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting(); + std::vector<const char*> kState = {"BA", "BB", "BC", "BY"}; + + AutofillField field; + test::CreateTestSelectField(kState, &field); + field.set_heuristic_type(ADDRESS_HOME_STATE); + + AutofillProfile address; + address.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("Bavaria")); + address.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("DE")); + + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("BY"), field.value); +} + +// Tests that the localized state names are selected correctly. +TEST_F(AutofillFieldFillerTest, FillSelectLocalizedState) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillUseAlternativeStateNameMap); + + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting(); + std::vector<const char*> kState = {"Bayern", "Berlin", "Brandenburg", + "Bremen"}; + + AutofillField field; + test::CreateTestSelectField(kState, &field); + field.set_heuristic_type(ADDRESS_HOME_STATE); + + AutofillProfile address; + address.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("Bavaria")); + address.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("DE")); + + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("Bayern"), field.value); +} + +// Tests that the state names are selected correctly when the state name exists +// as a substring in the selection options. +TEST_F(AutofillFieldFillerTest, FillSelectLocalizedStateSubstring) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillUseAlternativeStateNameMap); + + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting(); + std::vector<const char*> kState = {"Bavaria Has Munich", "Berlin has Berlin"}; + + AutofillField field; + test::CreateTestSelectField(kState, &field); + field.set_heuristic_type(ADDRESS_HOME_STATE); + + AutofillProfile address; + address.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("Bavaria")); + address.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("DE")); + + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("Bavaria Has Munich"), field.value); +} + +// Tests that the state abbreviations are filled in the text field when the +// field length is limited. +TEST_F(AutofillFieldFillerTest, FillStateAbbreviationInTextField) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillUseAlternativeStateNameMap); + + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting(); + + AutofillField field; + test::CreateTestFormField("State", "state", "", "text", &field); + field.set_heuristic_type(ADDRESS_HOME_STATE); + field.max_length = 4; + + AutofillProfile address; + address.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("Bavaria")); + address.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("DE")); + + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("BY"), field.value); +} + +// Tests that the state names are selected correctly even though the state +// value saved in the address is not recognized by the AlternativeStateNameMap. +TEST_F(AutofillFieldFillerTest, FillStateFieldWithSavedValueInProfile) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillUseAlternativeStateNameMap); + + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting(); + std::vector<const char*> kState = {"Bavari", "Berlin", "Lower Saxony"}; + + AutofillField field; + test::CreateTestSelectField(kState, &field); + field.set_heuristic_type(ADDRESS_HOME_STATE); + + AutofillProfile address; + address.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("Bavari")); + address.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("DE")); + + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("Bavari"), field.value); +} + +// Tests that Autofill does not wrongly fill the state when the appropriate +// state is not in the list of selection options given that the abbreviation is +// saved in the profile. +TEST_F(AutofillFieldFillerTest, FillStateFieldWhenStateIsNotInOptions) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillUseAlternativeStateNameMap); + + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting( + "US", "Colorado", + {{.canonical_name = "Colorado", + .abbreviations = {"CO"}, + .alternative_names = {}}}); + std::vector<const char*> kState = {"Connecticut", "California"}; + + AutofillField field; + test::CreateTestSelectField(kState, &field); + field.set_heuristic_type(ADDRESS_HOME_STATE); + + AutofillProfile address; + address.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CO")); + address.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); + + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16(""), field.value); +} + +// Tests that Autofill uses the static states data of US as a fallback mechanism +// for filling when |AlternativeStateNameMap| is not populated. +TEST_F(AutofillFieldFillerTest, + FillStateFieldWhenAlternativeStateNameMapIsNotPopulated) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillUseAlternativeStateNameMap); + + test::ClearAlternativeStateNameMapForTesting(); + std::vector<const char*> kState = {"Colorado", "Connecticut", "California"}; + + AutofillField field; + test::CreateTestSelectField(kState, &field); + field.set_heuristic_type(ADDRESS_HOME_STATE); + + AutofillProfile address; + address.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CO")); + address.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); + + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("Colorado"), field.value); +} + +// Tests that Autofill fills upper case abbreviation in the input field when +// field length is limited. +TEST_F(AutofillFieldFillerTest, FillUpperCaseAbbreviationInStateTextField) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillUseAlternativeStateNameMap); + + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting("DE", "Bavaria", + {{.canonical_name = "Bavaria", + .abbreviations = {"by"}, + .alternative_names = {}}}); + + AutofillField field; + test::CreateTestFormField("State", "state", "", "text", &field); + field.set_heuristic_type(ADDRESS_HOME_STATE); + field.max_length = 4; + + AutofillProfile address; + address.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("Bavaria")); + address.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("DE")); + + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, &address, &field, /*cvc=*/base::string16()); + EXPECT_EQ(ASCIIToUTF16("BY"), field.value); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/field_types.cc b/chromium/components/autofill/core/browser/field_types.cc index d828d8acc87..78e6b52c81f 100644 --- a/chromium/components/autofill/core/browser/field_types.cc +++ b/chromium/components/autofill/core/browser/field_types.cc @@ -21,6 +21,7 @@ bool IsFillableFieldType(ServerFieldType field_type) { case NAME_LAST_SECOND: case NAME_MIDDLE_INITIAL: case NAME_FULL: + case NAME_FULL_WITH_HONORIFIC_PREFIX: case NAME_SUFFIX: case EMAIL_ADDRESS: case USERNAME_AND_EMAIL_ADDRESS: @@ -152,6 +153,8 @@ base::StringPiece FieldTypeToStringPiece(ServerFieldType type) { return "EMPTY_TYPE"; case NAME_HONORIFIC_PREFIX: return "NAME_HONORIFIC_PREFIX"; + case NAME_FULL_WITH_HONORIFIC_PREFIX: + return "NAME_FULL_WITH_HONORIFIC_PREFIX"; case NAME_FIRST: return "NAME_FIRST"; case NAME_MIDDLE: diff --git a/chromium/components/autofill/core/browser/field_types.h b/chromium/components/autofill/core/browser/field_types.h index 7af166bd7fb..52f3cfab947 100644 --- a/chromium/components/autofill/core/browser/field_types.h +++ b/chromium/components/autofill/core/browser/field_types.h @@ -10,6 +10,7 @@ #include "base/strings/string16.h" #include "base/strings/string_piece_forward.h" +#include "components/autofill/core/common/dense_set.h" namespace autofill { @@ -233,9 +234,12 @@ enum ServerFieldType { // The floor number within a building. ADDRESS_HOME_FLOOR = 116, + // The full name including the honorific prefix. + NAME_FULL_WITH_HONORIFIC_PREFIX = 117, + // No new types can be added without a corresponding change to the Autofill // server. - MAX_VALID_FIELD_TYPE = 117, + MAX_VALID_FIELD_TYPE = 118, }; // The list of all HTML autocomplete field type hints supported by Chrome. @@ -323,24 +327,25 @@ enum HtmlFieldMode { HTML_MODE_SHIPPING, }; -enum FieldTypeGroup { - NO_GROUP, - NAME, - NAME_BILLING, - EMAIL, - COMPANY, - ADDRESS_HOME, - ADDRESS_BILLING, - PHONE_HOME, - PHONE_BILLING, - CREDIT_CARD, - PASSWORD_FIELD, - TRANSACTION, - USERNAME_FIELD, - UNFILLABLE, +enum class FieldTypeGroup { + kNoGroup, + kName, + kNameBilling, + kEmail, + kCompany, + kAddressHome, + kAddressBilling, + kPhoneHome, + kPhoneBilling, + kCreditCard, + kPasswordField, + kTransaction, + kUsernameField, + kUnfillable, + kMaxValue = kUnfillable, }; -typedef std::set<ServerFieldType> ServerFieldTypeSet; +using ServerFieldTypeSet = DenseSet<ServerFieldType, MAX_VALID_FIELD_TYPE>; // Returns whether the field can be filled with data. bool IsFillableFieldType(ServerFieldType field_type); diff --git a/chromium/components/autofill/core/browser/form_data_importer.cc b/chromium/components/autofill/core/browser/form_data_importer.cc index 37c6daaa7a4..a81a2586b53 100644 --- a/chromium/components/autofill/core/browser/form_data_importer.cc +++ b/chromium/components/autofill/core/browser/form_data_importer.cc @@ -19,7 +19,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" -#include "components/autofill/core/browser/address_profiles/address_profile_save_manager.h" +#include "components/autofill/core/browser/address_profile_save_manager.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/autofill_type.h" @@ -52,22 +52,33 @@ using AddressImportRequirement = // Return true if the |field_type| and |value| are valid within the context // of importing a form. -bool IsValidFieldTypeAndValue(const std::set<ServerFieldType>& types_seen, +bool IsValidFieldTypeAndValue(const ServerFieldTypeSet types_seen, ServerFieldType field_type, const base::string16& value, LogBuffer* import_log_buffer) { // Abandon the import if two fields of the same type are encountered. // This indicates ambiguous data or miscategorization of types. - // Make an exception for PHONE_HOME_NUMBER however as both prefix and - // suffix are stored against this type, and for EMAIL_ADDRESS because it is - // common to see second 'confirm email address' fields on forms. - if (types_seen.count(field_type) && field_type != PHONE_HOME_NUMBER && - field_type != EMAIL_ADDRESS) { + // Make an exception for: + // - EMAIL_ADDRESS because it is common to see second 'confirm email address' + // field; + // - PHONE_HOME_NUMBER because it is used to store both prefix and suffix of a + // single number; + // - phone number components because a form might request several phone + // numbers. + // TODO(crbug.com/1156315) Remove feature & PHONE_HOME_NUMBER checks when + // launched. + auto field_type_group = AutofillType(field_type).group(); + if (types_seen.count(field_type) && field_type != EMAIL_ADDRESS && + (base::FeatureList::IsEnabled( + features::kAutofillEnableImportWhenMultiplePhoneNumbers) + ? field_type_group != FieldTypeGroup::kPhoneBilling && + field_type_group != FieldTypeGroup::kPhoneHome + : field_type != PHONE_HOME_NUMBER)) { if (import_log_buffer) { *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed << "Multiple fields of type " - << AutofillType(field_type).ToString() << "." - << CTag{}; + << AutofillType::ServerFieldTypeToString(field_type) + << "." << CTag{}; } return false; } @@ -77,7 +88,8 @@ bool IsValidFieldTypeAndValue(const std::set<ServerFieldType>& types_seen, if (import_log_buffer) { *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed << "Email address found in field of different type: " - << AutofillType(field_type).ToString() << CTag{}; + << AutofillType::ServerFieldTypeToString(field_type) + << CTag{}; } return false; } @@ -229,7 +241,8 @@ FormDataImporter::FormDataImporter(AutofillClient* client, app_locale, personal_data_manager)), address_profile_save_manager_( - std::make_unique<AddressProfileSaveManager>(personal_data_manager)), + std::make_unique<AddressProfileSaveManager>(client, + personal_data_manager)), #if !defined(OS_ANDROID) && !defined(OS_IOS) local_card_migration_manager_( std::make_unique<LocalCardMigrationManager>(client, @@ -421,7 +434,10 @@ bool FormDataImporter::ImportFormData( // - ImportAddressProfiles may eventually save or update one or more address // profiles. bool address_import = false; - if (profile_autofill_enabled) { + + // Only import addresses if enabled. + if (profile_autofill_enabled && + !base::FeatureList::IsEnabled(features::kAutofillDisableAddressImport)) { address_import = ImportAddressProfiles(submitted_form); } @@ -443,7 +459,7 @@ bool FormDataImporter::ImportAddressProfiles(const FormStructure& form) { // We save a maximum of 2 profiles per submitted form (e.g. for shipping and // billing). static const size_t kMaxNumAddressProfilesSaved = 2; - size_t num_saved_profiles = 0; + size_t num_complete_profiles = 0; if (!form.field_count()) { import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed @@ -452,12 +468,12 @@ bool FormDataImporter::ImportAddressProfiles(const FormStructure& form) { // Relevant sections for address fields. std::set<std::string> sections; for (const auto& field : form) { - if (field->Type().group() != CREDIT_CARD) + if (field->Type().group() != FieldTypeGroup::kCreditCard) sections.insert(field->section); } for (const std::string& section : sections) { - if (num_saved_profiles == kMaxNumAddressProfilesSaved) + if (num_complete_profiles == kMaxNumAddressProfilesSaved) break; // Log the output from a section in a separate div for readability. import_log_buffer << Tag{"div"} @@ -466,38 +482,38 @@ bool FormDataImporter::ImportAddressProfiles(const FormStructure& form) { << section << CTag{}; // Try to import an address profile from the form fields of this section. if (ImportAddressProfileForSection(form, section, &import_log_buffer)) - num_saved_profiles++; + num_complete_profiles++; // And close the div of the section import log. import_log_buffer << CTag{"div"}; } // Run the import on the union of the section if the import was not // successful and if there is more than one section. - if (num_saved_profiles > 0) { + if (num_complete_profiles > 0) { AutofillMetrics::LogAddressFormImportStatustMetric( AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT); } else if (sections.size() > 1) { // Try to import by combining all sections. if (ImportAddressProfileForSection(form, "", &import_log_buffer)) { - num_saved_profiles++; + num_complete_profiles++; AutofillMetrics::LogAddressFormImportStatustMetric( AutofillMetrics::AddressProfileImportStatusMetric:: SECTION_UNION_IMPORT); } } - if (num_saved_profiles == 0) { + if (num_complete_profiles == 0) { AutofillMetrics::LogAddressFormImportStatustMetric( AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT); } } import_log_buffer << LogMessage::kImportAddressProfileFromFormNumberOfImports - << num_saved_profiles << CTag{}; + << num_complete_profiles << CTag{}; // Write log buffer to autofill-internals. LogManager* log_manager = client_->GetLogManager(); if (log_manager) log_manager->Log() << std::move(import_log_buffer); - return num_saved_profiles > 0; + return num_complete_profiles > 0; } bool FormDataImporter::ImportAddressProfileForSection( @@ -514,7 +530,7 @@ bool FormDataImporter::ImportAddressProfileForSection( // Used to detect and discard address forms with multiple fields of the same // type. - std::set<ServerFieldType> types_seen; + ServerFieldTypeSet types_seen; // Tracks if the form section contains multiple distinct email addresses. bool has_multiple_distinct_email_addresses = false; @@ -528,6 +544,10 @@ bool FormDataImporter::ImportAddressProfileForSection( // Tracks if the form section contains an invalid country. bool has_invalid_country = false; + // Tracks if subsequent phone number fields should be ignored, + // since they do not belong to the first phone number in the form. + bool ignore_phone_number_fields = false; + // Go through each |form| field and attempt to constitute a valid profile. for (const auto& field : form) { // Reject fields that are not within the specified |section|. @@ -539,14 +559,20 @@ bool FormDataImporter::ImportAddressProfileForSection( base::TrimWhitespace(field->value, base::TRIM_ALL, &value); // If we don't know the type of the field, or the user hasn't entered any - // information into the field, then skip it. - if (!field->IsFieldFillable() || value.empty()) + // information into the field, or the field is non-focusable (hidden), then + // skip it. + // TODO(crbug.com/1101280): Remove |skip_unfocussable_field| + bool skip_unfocussable_field = + !field->is_focusable && + !base::FeatureList::IsEnabled( + features::kAutofillProfileImportFromUnfocusableFields); + if (!field->IsFieldFillable() || skip_unfocussable_field || value.empty()) continue; AutofillType field_type = field->Type(); // Credit card fields are handled by ImportCreditCard(). - if (field_type.group() == CREDIT_CARD) + if (field_type.group() == FieldTypeGroup::kCreditCard) continue; // There can be multiple email fields (e.g. in the case of 'confirm email' @@ -569,6 +595,27 @@ bool FormDataImporter::ImportAddressProfileForSection( if (!IsValidFieldTypeAndValue(types_seen, server_field_type, value, import_log_buffer)) has_invalid_field_types = true; + + // Found phone number component field. + // TODO(crbug.com/1156315) Remove feature check when launched. + if ((field_type.group() == FieldTypeGroup::kPhoneBilling || + field_type.group() == FieldTypeGroup::kPhoneHome) && + base::FeatureList::IsEnabled( + features::kAutofillEnableImportWhenMultiplePhoneNumbers)) { + if (ignore_phone_number_fields) + continue; + // PHONE_HOME_NUMBER is used for both prefix and suffix, so it might occur + // multiple times for a single number. Duplication of any other phone + // component means it belongs to a new number. Since Autofill currently + // supports storing only one phone number per profile, ignore this and all + // subsequent phone number fields. + if (server_field_type != PHONE_HOME_NUMBER && + types_seen.count(server_field_type)) { + ignore_phone_number_fields = true; + continue; + } + } + types_seen.insert(server_field_type); // We need to store phone data in the variables, before building the whole @@ -673,10 +720,13 @@ bool FormDataImporter::ImportAddressProfileForSection( if (!candidate_profile.FinalizeAfterImport()) return false; - std::string guid = - address_profile_save_manager_->SaveProfile(candidate_profile); + // At this stage, the saving of the profile can only be omitted by the + // incognito mode but the import is not triggered if the browser is in the + // incognito mode. + DCHECK(!personal_data_manager_->IsOffTheRecord()); + address_profile_save_manager_->SaveProfile(candidate_profile); - return !guid.empty(); + return true; } bool FormDataImporter::ImportCreditCard( @@ -803,7 +853,7 @@ CreditCard FormDataImporter::ExtractCreditCardFromForm( CreditCard candidate_credit_card; - std::set<ServerFieldType> types_seen; + ServerFieldTypeSet types_seen; for (const auto& field : form) { base::string16 value; base::TrimWhitespace(field->value, base::TRIM_ALL, &value); @@ -815,7 +865,7 @@ CreditCard FormDataImporter::ExtractCreditCardFromForm( AutofillType field_type = field->Type(); // Field was not identified as a credit card field. - if (field_type.group() != CREDIT_CARD) + if (field_type.group() != FieldTypeGroup::kCreditCard) continue; if (form.value_from_dynamic_change_form()) diff --git a/chromium/components/autofill/core/browser/form_data_importer.h b/chromium/components/autofill/core/browser/form_data_importer.h index 4ef28d575a6..a368708103f 100644 --- a/chromium/components/autofill/core/browser/form_data_importer.h +++ b/chromium/components/autofill/core/browser/form_data_importer.h @@ -112,7 +112,8 @@ class FormDataImporter { // Go through the |form| fields and attempt to extract and import valid // address profiles. Returns true on extraction success of at least one // profile. There are many reasons that extraction may fail (see - // implementation). + // implementation). The function returns true if at least one complete address + // profile was found. bool ImportAddressProfiles(const FormStructure& form); // Helper method for ImportAddressProfiles which only considers the fields for diff --git a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc index b15cee9ccae..8a51f0634dc 100644 --- a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc +++ b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc @@ -32,7 +32,6 @@ #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/form_structure.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/personal_data_manager_observer.h" #include "components/autofill/core/browser/test_autofill_client.h" @@ -124,6 +123,7 @@ class FormDataImporterTestBase { scoped_refptr<AutofillWebDataService>(autofill_database_service_), /*account_database=*/nullptr, /*pref_service=*/prefs_.get(), + /*local_state=*/prefs_.get(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, @@ -195,7 +195,7 @@ class FormDataImporterTestBase { const char* exp_cc_month, const char* exp_cc_year) { FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -232,7 +232,6 @@ class FormDataImporterTestBase { std::unique_ptr<PersonalDataManager> personal_data_manager_; std::unique_ptr<FormDataImporter> form_data_importer_; base::test::ScopedFeatureList scoped_feature_list_; - TestPatternProvider test_pattern_provider_; }; // TODO(crbug.com/1103421): Clean legacy implementation once structured names @@ -241,7 +240,7 @@ class FormDataImporterTestBase { class FormDataImporterTest : public FormDataImporterTestBase, public testing::Test, - public testing::WithParamInterface<std::tuple<bool, bool>> { + public testing::WithParamInterface<std::tuple<bool, bool, bool>> { protected: bool StructuredNames() const { return structured_names_enabled_; } bool StructuredAddresses() const { return structured_addresses_enabled_; } @@ -290,6 +289,7 @@ class FormDataImporterTest void InitializeFeatures() { structured_names_enabled_ = std::get<0>(GetParam()); structured_addresses_enabled_ = std::get<1>(GetParam()); + support_for_apartment_numbers_ = std::get<2>(GetParam()); std::vector<base::Feature> enabled_features; std::vector<base::Feature> disabled_features; @@ -309,15 +309,24 @@ class FormDataImporterTest disabled_features.push_back( features::kAutofillEnableSupportForMoreStructureInAddresses); } + + if (support_for_apartment_numbers_) { + enabled_features.push_back( + features::kAutofillEnableSupportForApartmentNumbers); + } else { + disabled_features.push_back( + features::kAutofillEnableSupportForApartmentNumbers); + } scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); } bool structured_names_enabled_; bool structured_addresses_enabled_; + bool support_for_apartment_numbers_; }; // ImportAddressProfiles tests. -TEST_P(FormDataImporterTest, ImportStructuredAddressProfile) { +TEST_P(FormDataImporterTest, ImportStructuredNameProfile) { base::test::ScopedFeatureList structured_addresses_feature; structured_addresses_feature.InitAndEnableFeature( features::kAutofillEnableSupportForMoreStructureInAddresses); @@ -342,7 +351,7 @@ TEST_P(FormDataImporterTest, ImportStructuredAddressProfile) { test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); const std::vector<AutofillProfile*>& results = @@ -394,7 +403,7 @@ TEST_P(FormDataImporterTest, test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); const std::vector<AutofillProfile*>& results = @@ -416,6 +425,65 @@ TEST_P(FormDataImporterTest, structured_address::VerificationStatus::kFormatted); } +TEST_P( + FormDataImporterTest, + ImportStructuredAddressProfile_StreetNameAndHouseNumberAndApartmentNumber) { + // This test is only applicable for enabled structured addresses and support + // for apartment numbers. + if (!base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForMoreStructureInAddresses) || + !base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForApartmentNumbers)) { + return; + } + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("Name:", "name", "Pablo Diego Ruiz y Picasso", + "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Email:", "email", "theprez@gmail.com", "text", + &field); + form.fields.push_back(field); + form.fields.push_back(field); + test::CreateTestFormField("Street name:", "street_name", "Laussat St", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("House number:", "house_number", "21", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Apartment", "apartment", "101", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("City:", "city", "San Francisco", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("State:", "state", "California", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); + form.fields.push_back(field); + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); + + const std::vector<AutofillProfile*>& results = + personal_data_manager_->GetProfiles(); + ASSERT_EQ(1U, results.size()); + + EXPECT_EQ(results[0]->GetRawInfo(ADDRESS_HOME_HOUSE_NUMBER), + base::ASCIIToUTF16("21")); + EXPECT_EQ(results[0]->GetRawInfo(ADDRESS_HOME_STREET_NAME), + base::ASCIIToUTF16("Laussat St")); + EXPECT_EQ(results[0]->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS), + base::ASCIIToUTF16("21 Laussat St APT 101")); + EXPECT_EQ(results[0]->GetRawInfo(ADDRESS_HOME_APT_NUM), + base::ASCIIToUTF16("101")); + EXPECT_EQ(results[0]->GetVerificationStatus(ADDRESS_HOME_HOUSE_NUMBER), + structured_address::VerificationStatus::kObserved); + EXPECT_EQ(results[0]->GetVerificationStatus(ADDRESS_HOME_STREET_NAME), + structured_address::VerificationStatus::kObserved); + EXPECT_EQ(results[0]->GetVerificationStatus(ADDRESS_HOME_STREET_ADDRESS), + structured_address::VerificationStatus::kFormatted); +} TEST_P(FormDataImporterTest, ImportStructuredAddressProfile_GermanStreetNameAndHouseNumber) { // This test is only applicable if structured addresses are enabled. @@ -446,7 +514,7 @@ TEST_P(FormDataImporterTest, test::CreateTestFormField("Zip:", "zip", "80992", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); const std::vector<AutofillProfile*>& results = @@ -494,7 +562,7 @@ TEST_P(FormDataImporterTest, ImportStructuredNameAddressProfile) { test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); const std::vector<AutofillProfile*>& results = @@ -517,6 +585,10 @@ TEST_P(FormDataImporterTest, ImportStructuredNameAddressProfile) { } TEST_P(FormDataImporterTest, ImportAddressProfiles) { + base::test::ScopedFeatureList dependent_locality_feature; + dependent_locality_feature.InitAndEnableFeature( + features::kAutofillEnableDependentLocalityParsing); + FormData form; form.url = GURL("https://wwww.foo.com"); @@ -533,6 +605,9 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles) { test::CreateTestFormField("Address:", "address1", "21 Laussat St", "text", &field); form.fields.push_back(field); + test::CreateTestFormField("Neighborhood:", "neighborhood", "Nob Hill", "text", + &field); + form.fields.push_back(field); test::CreateTestFormField("City:", "city", "San Francisco", "text", &field); form.fields.push_back(field); test::CreateTestFormField("State:", "state", "California", "text", &field); @@ -540,14 +615,14 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles) { test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected, "George", nullptr, "Washington", "theprez@gmail.com", nullptr, "21 Laussat St", nullptr, - "San Francisco", "California", "94102", nullptr, - nullptr); + "Nob Hill", "San Francisco", "California", "94102", + nullptr, nullptr); const std::vector<AutofillProfile*>& results = personal_data_manager_->GetProfiles(); ASSERT_EQ(1U, results.size()); @@ -578,7 +653,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfileFromUnifiedSection) { test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); // Assign the address field another section than the other fields. form_structure.field(3)->section = "another_section"; @@ -619,7 +694,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_BadEmail) { test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/false, form_structure); ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size()); @@ -650,7 +725,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_TwoEmails) { "example@example.com", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); ASSERT_EQ(1U, personal_data_manager_->GetProfiles().size()); @@ -681,12 +756,127 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_TwoDifferentEmails) { &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/false, form_structure); ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size()); } +// Tests that multiple phone numbers do not block profile import and the first +// one is saved. +TEST_P(FormDataImporterTest, ImportAddressProfiles_MultiplePhoneNumbers) { + base::test::ScopedFeatureList enable_import_when_multiple_phones_feature; + enable_import_when_multiple_phones_feature.InitAndEnableFeature( + features::kAutofillEnableImportWhenMultiplePhoneNumbers); + + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("First name:", "first_name", "George", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Last name:", "last_name", "Washington", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Address:", "address1", "21 Laussat St", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("City:", "city", "San Francisco", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("State:", "state", "California", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Phone 1:", "phone1", "+16505550000", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Phone 2:", "phone2", "+14155551234", "text", + &field); + form.fields.push_back(field); + + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); + + AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&expected, "George", nullptr, "Washington", nullptr, + nullptr, "21 Laussat St", nullptr, "San Francisco", + "California", "94102", nullptr, "1 650-555-0000"); + + const std::vector<AutofillProfile*>& results = + personal_data_manager_->GetProfiles(); + ASSERT_EQ(1U, results.size()); + EXPECT_EQ(0, expected.Compare(*results[0])); +} + +// Tests that multiple phone numbers do not block profile import and the first +// one is saved. +TEST_P(FormDataImporterTest, + ImportAddressProfiles_MultiplePhoneNumbersSplitAcrossMultipleFields) { + base::test::ScopedFeatureList enable_import_when_multiple_phones_feature; + enable_import_when_multiple_phones_feature.InitAndEnableFeature( + features::kAutofillEnableImportWhenMultiplePhoneNumbers); + + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("First name:", "first_name", "George", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Last name:", "last_name", "Washington", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Address:", "address1", "21 Laussat St", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("City:", "city", "San Francisco", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("State:", "state", "California", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Phone 1:", "home_phone_area_code1", "650", "text", + &field); + field.max_length = 3; + form.fields.push_back(field); + test::CreateTestFormField("Phone 1:", "home_phone_prefix1", "555", "text", + &field); + field.max_length = 3; + form.fields.push_back(field); + test::CreateTestFormField("Phone 1:", "home_phone_suffix1", "0000", "text", + &field); + field.max_length = 4; + form.fields.push_back(field); + test::CreateTestFormField("Phone 2:", "home_phone_area_code2", "202", "text", + &field); + field.max_length = 3; + form.fields.push_back(field); + test::CreateTestFormField("Phone 2:", "home_phone_prefix2", "555", "text", + &field); + field.max_length = 3; + form.fields.push_back(field); + test::CreateTestFormField("Phone 2:", "home_phone_suffix2", "1234", "text", + &field); + field.max_length = 4; + form.fields.push_back(field); + + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); + + AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&expected, "George", nullptr, "Washington", nullptr, + nullptr, "21 Laussat St", nullptr, "San Francisco", + "California", "94102", nullptr, "(650) 555-0000"); + + const std::vector<AutofillProfile*>& results = + personal_data_manager_->GetProfiles(); + ASSERT_EQ(1U, results.size()); + EXPECT_EQ(0, expected.Compare(*results[0])); +} + // Tests that not enough filled fields will result in not importing an address. TEST_P(FormDataImporterTest, ImportAddressProfiles_NotEnoughFilledFields) { FormData form; @@ -703,7 +893,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_NotEnoughFilledFields) { "4111 1111 1111 1111", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/false, form_structure); ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size()); @@ -731,7 +921,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_MinimumAddressUSA) { test::CreateTestFormField("Country:", "country", "USA", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); ASSERT_EQ(1U, personal_data_manager_->GetProfiles().size()); @@ -758,7 +948,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_MinimumAddressGB) { &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); ASSERT_EQ(1U, personal_data_manager_->GetProfiles().size()); @@ -780,7 +970,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_MinimumAddressGI) { test::CreateTestFormField("Country:", "country", "Gibraltar", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); ASSERT_EQ(1U, personal_data_manager_->GetProfiles().size()); @@ -820,7 +1010,7 @@ TEST_P(FormDataImporterTest, test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -833,9 +1023,7 @@ TEST_P(FormDataImporterTest, EXPECT_EQ(0, expected.Compare(*results[0])); } -// Test that a form is imported correctly even if some fields are not -// focusable. -TEST_P(FormDataImporterTest, ImportAddressProfiles_WithUnFocussableFields) { +TEST_P(FormDataImporterTest, ImportAddressProfiles_UnFocussableFields) { FormData form; form.url = GURL("https://wwww.foo.com"); @@ -857,8 +1045,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_WithUnFocussableFields) { form.fields.push_back(field); test::CreateTestFormField("City:", "city", "San Francisco", "text", &field); - - // Set this field to be not focusable. + // Set this field to be unfocusable. field.is_focusable = false; form.fields.push_back(field); @@ -869,8 +1056,20 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_WithUnFocussableFields) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); + + // Verify the status quo that the form is not imported with the unfocusable + // fields. + // TODO(crbug.com/1101280): Remove once feature is launched. + scoped_feature_list_.Reset(); + scoped_feature_list_.InitAndDisableFeature( + features::kAutofillProfileImportFromUnfocusableFields); + ImportAddressProfiles(/*extraction_successful=*/false, form_structure); + // Activate the feature and test again. + scoped_feature_list_.Reset(); + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillProfileImportFromUnfocusableFields); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -909,7 +1108,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_MultilineAddress) { test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -949,7 +1148,7 @@ TEST_P(FormDataImporterTest, form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure1); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -985,7 +1184,7 @@ TEST_P(FormDataImporterTest, form2.fields.push_back(field); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); AutofillProfile expected2(base::GenerateGUID(), test::kEmptyOrigin); @@ -1042,7 +1241,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_TwoValidProfilesSameForm) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -1116,7 +1315,7 @@ TEST_P(FormDataImporterTest, // Still able to do the import. FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -1198,7 +1397,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_ThreeValidProfilesSameForm) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); // Only two are saved. @@ -1252,7 +1451,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_SameProfileWithConflict) { form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure1); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -1299,7 +1498,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_SameProfileWithConflict) { form2.fields.push_back(field); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); const std::vector<AutofillProfile*>& results2 = @@ -1339,7 +1538,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_MissingInfoInOld) { form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure1); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -1377,7 +1576,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_MissingInfoInOld) { form2.fields.push_back(field); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); const std::vector<AutofillProfile*>& results2 = @@ -1424,7 +1623,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_MissingInfoInNew) { form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure1); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -1463,7 +1662,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_MissingInfoInNew) { form2.fields.push_back(field); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); const std::vector<AutofillProfile*>& results2 = @@ -1503,7 +1702,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_InsufficientAddress) { form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/false, form_structure1); // Since no refresh is expected, reload the data from the database to make @@ -1559,7 +1758,7 @@ TEST_P(FormDataImporterTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); // Expect that no new profile is saved. @@ -1576,7 +1775,7 @@ TEST_P(FormDataImporterTest, form.fields[0] = field; FormStructure form_structure2(form); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); @@ -1606,7 +1805,7 @@ TEST_P(FormDataImporterTest, NAME_FIRST, base::ASCIIToUTF16("Marion"), structured_address::VerificationStatus::kParsed); profile.SetRawInfoWithVerificationStatus( - NAME_FIRST, base::ASCIIToUTF16("Mitchell"), + NAME_MIDDLE, base::ASCIIToUTF16("Mitchell"), structured_address::VerificationStatus::kParsed); base::RunLoop run_loop; @@ -1641,7 +1840,7 @@ TEST_P(FormDataImporterTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); // The form submission should result in a change of name structure. @@ -1668,7 +1867,7 @@ TEST_P(FormDataImporterTest, form.fields[0] = field; FormStructure form_structure2(form); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); @@ -1740,7 +1939,7 @@ TEST_P(FormDataImporterTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); // The form submission should result in a change of the address structure. @@ -1791,7 +1990,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_UnrecognizedCountry) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/false, form_structure); // Since no refresh is expected, reload the data from the database to make @@ -1838,7 +2037,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_LocalizedCountryName) { // Verify that the country code is not determined from the country value if // the page language is not set. FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/false, form_structure); ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size()); @@ -1893,7 +2092,7 @@ TEST_P(FormDataImporterTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -1940,7 +2139,7 @@ TEST_P(FormDataImporterTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/false, form_structure); // Since no refresh is expected, reload the data from the database to make @@ -1963,7 +2162,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_Valid) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::HistogramTester histogram_tester; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); @@ -1993,7 +2192,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_InvalidCardNumber) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::HistogramTester histogram_tester; EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card)); @@ -2019,7 +2218,7 @@ TEST_P(FormDataImporterTest, AddFullCreditCardForm(&form, "Smalls Biggie", "4111-1111-1111-1111", "", ""); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::HistogramTester histogram_tester; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); @@ -2039,7 +2238,7 @@ TEST_P(FormDataImporterTest, "2000"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::HistogramTester histogram_tester; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); @@ -2071,7 +2270,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_MonthSelectInvalidText) { form.fields[2].option_contents = contents; FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::HistogramTester histogram_tester; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); @@ -2102,7 +2301,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_TwoValidCards) { "2999"); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2125,7 +2324,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_TwoValidCards) { AddFullCreditCardForm(&form2, "", "5500 0000 0000 0004", "02", "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2)); @@ -2254,7 +2453,7 @@ TEST_P(FormDataImporterTest, // The card should not be offered to be saved locally because the feature flag // is disabled. FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_FALSE(imported_credit_card); @@ -2285,7 +2484,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_DuplicateServerCards_FullCard) { // The card should not be offered to be saved locally because it only matches // the full server card. FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_FALSE(imported_credit_card); @@ -2300,7 +2499,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_SameCreditCardWithConflict) { "2998"); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2325,7 +2524,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_SameCreditCardWithConflict) { /* different year */ "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2)); EXPECT_FALSE(imported_credit_card2); @@ -2352,7 +2551,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_ShouldReturnLocalCard) { "2998"); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2377,7 +2576,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_ShouldReturnLocalCard) { /* different year */ "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_TRUE(ImportCreditCard(form_structure2, /* should_return_local_card= */ true, @@ -2407,7 +2606,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_EmptyCardWithConflict) { "2998"); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); @@ -2432,7 +2631,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_EmptyCardWithConflict) { "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_FALSE( ImportCreditCard(form_structure2, false, &imported_credit_card2)); @@ -2461,7 +2660,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_MissingInfoInNew) { "2999"); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2486,7 +2685,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_MissingInfoInNew) { "4111-1111-1111-1111", "01", "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2)); EXPECT_FALSE(imported_credit_card2); @@ -2513,7 +2712,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_MissingInfoInNew) { /* no year */ nullptr); FormStructure form_structure3(form3); - form_structure3.DetermineHeuristicTypes(); + form_structure3.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card3; EXPECT_FALSE( ImportCreditCard(form_structure3, false, &imported_credit_card3)); @@ -2557,7 +2756,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_MissingInfoInOld) { /* different year */ "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); EXPECT_FALSE(imported_credit_card); @@ -2600,7 +2799,7 @@ TEST_P(FormDataImporterTest, ImportCreditCard_SameCardWithSeparators) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); EXPECT_FALSE(imported_credit_card); @@ -2642,7 +2841,7 @@ TEST_P(FormDataImporterTest, /* different year */ "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_FALSE(imported_credit_card); @@ -2682,7 +2881,7 @@ TEST_P(FormDataImporterTest, "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_TRUE(form_data_importer_->ImportFormData( @@ -2706,7 +2905,7 @@ TEST_P(FormDataImporterTest, "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure2, /*profile_autofill_enabled=*/true, @@ -2746,7 +2945,7 @@ TEST_P(FormDataImporterTest, test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form3.fields.push_back(field); FormStructure form_structure3(form3); - form_structure3.DetermineHeuristicTypes(); + form_structure3.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card3; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure3, /*profile_autofill_enabled=*/true, @@ -2770,7 +2969,7 @@ TEST_P(FormDataImporterTest, "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_TRUE(form_data_importer_->ImportFormData( @@ -2809,7 +3008,7 @@ TEST_P(FormDataImporterTest, "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_TRUE(form_data_importer_->ImportFormData( @@ -2848,7 +3047,7 @@ TEST_P(FormDataImporterTest, "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -2886,7 +3085,7 @@ TEST_P(FormDataImporterTest, "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -2911,7 +3110,7 @@ TEST_P(FormDataImporterTest, "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -2938,7 +3137,7 @@ TEST_P( "1999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_TRUE(form_data_importer_->ImportFormData( @@ -2981,7 +3180,7 @@ TEST_P(FormDataImporterTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_TRUE(form_data_importer_->ImportFormData( @@ -3030,7 +3229,7 @@ TEST_P(FormDataImporterTest, ImportFormData_OneAddressOneCreditCard) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_TRUE(form_data_importer_->ImportFormData( @@ -3112,7 +3311,7 @@ TEST_P(FormDataImporterTest, ImportFormData_TwoAddressesOneCreditCard) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::RunLoop run_loop; @@ -3196,7 +3395,7 @@ TEST_P(FormDataImporterTest, ImportFormData_TwoAddressesNameFirst) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ImportAddressProfiles(/*extraction_successful=*/true, form_structure); // Test that both addresses have been saved. @@ -3235,7 +3434,7 @@ TEST_P(FormDataImporterTest, ImportFormData_AddressesDisabledOneCreditCard) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_TRUE(form_data_importer_->ImportFormData( @@ -3293,7 +3492,7 @@ TEST_P(FormDataImporterTest, ImportFormData_OneAddressCreditCardDisabled) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_TRUE(form_data_importer_->ImportFormData( @@ -3355,7 +3554,7 @@ TEST_P(FormDataImporterTest, ImportFormData_AddressCreditCardDisabled) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -3412,7 +3611,7 @@ TEST_P(FormDataImporterTest, DontDuplicateMaskedServerCard) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -3451,7 +3650,7 @@ TEST_P(FormDataImporterTest, ImportFormData_HiddenCreditCardFormAfterEntered) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; // Still returns true because the credit card import was successful. @@ -3487,7 +3686,7 @@ TEST_P(FormDataImporterTest, "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_TRUE(form_data_importer_->ImportFormData( @@ -3536,7 +3735,7 @@ TEST_P(FormDataImporterTest, DontDuplicateFullServerCard) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -3581,7 +3780,7 @@ TEST_P(FormDataImporterTest, base::HistogramTester histogram_tester; FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -3630,7 +3829,7 @@ TEST_P(FormDataImporterTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -3676,7 +3875,7 @@ TEST_P(FormDataImporterTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -3723,7 +3922,7 @@ TEST_P( form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_TRUE(form_data_importer_->ImportFormData( @@ -3769,7 +3968,7 @@ TEST_P(FormDataImporterTest, base::HistogramTester histogram_tester; FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -3818,7 +4017,7 @@ TEST_P(FormDataImporterTest, base::HistogramTester histogram_tester; FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -3868,7 +4067,7 @@ TEST_P(FormDataImporterTest, base::HistogramTester histogram_tester; FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; base::Optional<std::string> imported_upi_id; EXPECT_FALSE(form_data_importer_->ImportFormData( @@ -3892,7 +4091,7 @@ TEST_P(FormDataImporterTest, ImportUpiId) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; // Discarded. base::Optional<std::string> imported_upi_id; @@ -3917,7 +4116,7 @@ TEST_P(FormDataImporterTest, ImportUpiIdDisabled) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; // Discarded. base::Optional<std::string> imported_upi_id; @@ -3941,7 +4140,7 @@ TEST_P(FormDataImporterTest, ImportUpiIdIgnoreNonUpiId) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::unique_ptr<CreditCard> imported_credit_card; // Discarded. base::Optional<std::string> imported_upi_id; @@ -3955,10 +4154,13 @@ TEST_P(FormDataImporterTest, ImportUpiIdIgnoreNonUpiId) { EXPECT_FALSE(imported_upi_id.has_value()); } -// Runs the suite with the feature |kAutofillSupportForMoreStructuredNames| and -// |kAutofillSupportForMoreStructuredAddresses| enabled and disabled. +// Runs the suite with the feature |kAutofillSupportForMoreStructuredNames|, +// |kAutofillSupportForMoreStructuredAddresses| and +// |kAutofillEnableSupportForApartmentNumbers| enabled and disabled. INSTANTIATE_TEST_SUITE_P(, FormDataImporterTest, - testing::Combine(testing::Bool(), testing::Bool())); + testing::Combine(testing::Bool(), + testing::Bool(), + testing::Bool())); } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/address_field.cc b/chromium/components/autofill/core/browser/form_parsing/address_field.cc index 6ed67749e2f..e9e494e023a 100644 --- a/chromium/components/autofill/core/browser/form_parsing/address_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/address_field.cc @@ -38,6 +38,9 @@ bool SetFieldAndAdvanceCursor(AutofillScanner* scanner, AutofillField** field) { const int AddressField::kZipCodeMatchType = MATCH_DEFAULT | MATCH_TELEPHONE | MATCH_NUMBER; +const int AddressField::kDependentLocalityMatchType = + MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH; + // Select fields are allowed here. This occurs on top-100 site rediff.com. const int AddressField::kCityMatchType = MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH; @@ -45,9 +48,10 @@ const int AddressField::kCityMatchType = const int AddressField::kStateMatchType = MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH; -std::unique_ptr<FormField> AddressField::Parse(AutofillScanner* scanner, - const std::string& page_language, - LogManager* log_manager) { +std::unique_ptr<FormField> AddressField::Parse( + AutofillScanner* scanner, + const LanguageCode& page_language, + LogManager* log_manager) { if (scanner->IsEnd()) return nullptr; @@ -58,21 +62,25 @@ std::unique_ptr<FormField> AddressField::Parse(AutofillScanner* scanner, base::string16 attention_ignored = UTF8ToUTF16(kAttentionIgnoredRe); base::string16 region_ignored = UTF8ToUTF16(kRegionIgnoredRe); - // In JSON : EMAIL_ADDRESS - auto& patterns_email = PatternProvider::GetInstance().GetMatchPatterns( - "EMAIL_ADDRESS", page_language); - // In JSON : ADDRESS_LOOKUP - auto& patterns_al = PatternProvider::GetInstance().GetMatchPatterns( - "ADDRESS_LOOKUP", page_language); - // In JSON : ADDRESS_NAME_IGNORED - auto& patterns_ni = PatternProvider::GetInstance().GetMatchPatterns( - "ADDRESS_NAME_IGNORED", page_language); - // In JSON : ATTENTION_IGNORED - auto& patterns_ai = PatternProvider::GetInstance().GetMatchPatterns( - "ATTENTION_IGNORED", page_language); - // In JSON : REGION_IGNORED - auto& patterns_ri = PatternProvider::GetInstance().GetMatchPatterns( - "REGION_IGNORED", page_language); + const std::vector<MatchingPattern>& email_patterns = + PatternProvider::GetInstance().GetMatchPatterns("EMAIL_ADDRESS", + page_language); + + const std::vector<MatchingPattern>& address_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_LOOKUP", + page_language); + + const std::vector<MatchingPattern>& address_ignore_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_NAME_IGNORED", + page_language); + + const std::vector<MatchingPattern>& attention_ignore_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ATTENTION_IGNORED", + page_language); + + const std::vector<MatchingPattern>& region_ignore_patterns = + PatternProvider::GetInstance().GetMatchPatterns("REGION_IGNORED", + page_language); // Allow address fields to appear in any order. size_t begin_trailing_non_labeled_fields = 0; @@ -80,29 +88,30 @@ std::unique_ptr<FormField> AddressField::Parse(AutofillScanner* scanner, while (!scanner->IsEnd()) { const size_t cursor = scanner->SaveCursor(); // Ignore "Address Lookup" field. http://crbug.com/427622 - if (ParseField(scanner, base::UTF8ToUTF16(kAddressLookupRe), patterns_al, - nullptr, {log_manager, "kAddressLookupRe"}) || + if (ParseField(scanner, base::UTF8ToUTF16(kAddressLookupRe), + address_patterns, nullptr, + {log_manager, "kAddressLookupRe"}) || ParseField(scanner, base::UTF8ToUTF16(kAddressNameIgnoredRe), - patterns_ni, nullptr, + address_ignore_patterns, nullptr, {log_manager, "kAddressNameIgnoreRe"})) { continue; // Ignore email addresses. } else if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kEmailRe), MATCH_DEFAULT | MATCH_TEXT_AREA, - patterns_email, nullptr, + email_patterns, nullptr, {log_manager, "kEmailRe"}, {.augment_types = MATCH_TEXT_AREA})) { continue; } else if (address_field->ParseAddress(scanner, page_language) || - address_field->ParseCityStateCountryZipCode(scanner, - page_language) || + address_field->ParseDependentLocalityCityStateCountryZipCode( + scanner, page_language) || address_field->ParseCompany(scanner, page_language)) { has_trailing_non_labeled_fields = false; continue; - } else if (ParseField(scanner, attention_ignored, patterns_ai, nullptr, - {log_manager, "kAttentionIgnoredRe"}) || - ParseField(scanner, region_ignored, patterns_ri, nullptr, - {log_manager, "kRegionIgnoredRe"})) { + } else if (ParseField(scanner, attention_ignored, attention_ignore_patterns, + nullptr, {log_manager, "kAttentionIgnoredRe"}) || + ParseField(scanner, region_ignored, region_ignore_patterns, + nullptr, {log_manager, "kRegionIgnoredRe"})) { // We ignore the following: // * Attention. // * Province/Region/Other. @@ -135,7 +144,8 @@ std::unique_ptr<FormField> AddressField::Parse(AutofillScanner* scanner, address_field->street_address_ || address_field->city_ || address_field->state_ || address_field->zip_ || address_field->zip4_ || address_field->street_name_ || address_field->house_number_ || - address_field->country_) { + address_field->country_ || address_field->apartment_number_ || + address_field->dependent_locality_) { // Don't slurp non-labeled fields at the end into the address. if (has_trailing_non_labeled_fields) scanner->RewindTo(begin_trailing_non_labeled_fields); @@ -168,6 +178,8 @@ void AddressField::AddClassifications( field_candidates); AddClassification(street_address_, ADDRESS_HOME_STREET_ADDRESS, kBaseAddressParserScore, field_candidates); + AddClassification(dependent_locality_, ADDRESS_HOME_DEPENDENT_LOCALITY, + kBaseAddressParserScore, field_candidates); AddClassification(city_, ADDRESS_HOME_CITY, kBaseAddressParserScore, field_candidates); AddClassification(state_, ADDRESS_HOME_STATE, kBaseAddressParserScore, @@ -180,22 +192,26 @@ void AddressField::AddClassifications( kBaseAddressParserScore, field_candidates); AddClassification(street_name_, ADDRESS_HOME_STREET_NAME, kBaseAddressParserScore, field_candidates); + AddClassification(apartment_number_, ADDRESS_HOME_APT_NUM, + kBaseAddressParserScore, field_candidates); } bool AddressField::ParseCompany(AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { if (company_) return false; - // In JSON : COMPANY - auto& patterns_c = - PatternProvider::GetInstance().GetMatchPatterns("COMPANY", page_language); - return ParseField(scanner, UTF8ToUTF16(kCompanyRe), patterns_c, &company_, - {log_manager_, "kCompanyRe"}); + const std::vector<MatchingPattern>& company_patterns = + PatternProvider::GetInstance().GetMatchPatterns("COMPANY_NAME", + page_language); + + return ParseField(scanner, UTF8ToUTF16(kCompanyRe), company_patterns, + &company_, {log_manager_, "kCompanyRe"}); } -bool AddressField::ParseAddressFieldSequence(AutofillScanner* scanner, - const std::string& page_language) { +bool AddressField::ParseAddressFieldSequence( + AutofillScanner* scanner, + const LanguageCode& page_language) { // Search for a sequence of a street name field followed by a house number // field. Only if both are found in an abitrary order, the parsing is // considered successful. @@ -207,44 +223,62 @@ bool AddressField::ParseAddressFieldSequence(AutofillScanner* scanner, } const size_t cursor_position = scanner->CursorPosition(); - // In JSON : ---- maybe ADDRESS_LINE1(2,3) - auto& patterns_s = PatternProvider::GetInstance().GetMatchPatterns( - ADDRESS_HOME_STREET_NAME, page_language); - // In JSON : ---- - auto& patterns_h = PatternProvider::GetInstance().GetMatchPatterns( - ADDRESS_HOME_HOUSE_NUMBER, page_language); + + const std::vector<MatchingPattern>& street_name_patterns = + PatternProvider::GetInstance().GetMatchPatterns(ADDRESS_HOME_STREET_NAME, + page_language); + + const std::vector<MatchingPattern>& house_number_patterns = + PatternProvider::GetInstance().GetMatchPatterns(ADDRESS_HOME_HOUSE_NUMBER, + page_language); + const std::vector<MatchingPattern>& apartment_number_patterns = + PatternProvider::GetInstance().GetMatchPatterns(ADDRESS_HOME_APT_NUM, + page_language); while (!scanner->IsEnd()) { if (!street_name_ && ParseFieldSpecifics(scanner, UTF8ToUTF16(kStreetNameRe), MATCH_DEFAULT, - patterns_s, &street_name_, + street_name_patterns, &street_name_, {log_manager_, "kStreetNameRe"})) { continue; } if (!house_number_ && - ParseFieldSpecifics(scanner, UTF8ToUTF16(kHouseNumberRe), MATCH_DEFAULT, - patterns_h, &house_number_, + ParseFieldSpecifics(scanner, UTF8ToUTF16(kHouseNumberRe), + MATCH_DEFAULT | MATCH_NUMBER | MATCH_TELEPHONE, + house_number_patterns, &house_number_, {log_manager_, "kHouseNumberRe"})) { continue; } + // TODO(crbug.com/1153715): Remove finch guard once launched. + if (base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForApartmentNumbers) && + !apartment_number_ && + ParseFieldSpecifics(scanner, UTF8ToUTF16(kApartmentNumberRe), + MATCH_DEFAULT | MATCH_NUMBER | MATCH_TELEPHONE, + apartment_number_patterns, &apartment_number_, + {log_manager_, "kApartmentNumberRe"})) { + continue; + } + break; } + // The street name and house number are non-optional. if (street_name_ && house_number_) return true; - // Reset both fields in case one of them was found. - if (street_name_ || house_number_) { - street_name_ = nullptr; - house_number_ = nullptr; - } + // Reset all fields if the non-optional requirements could not be met. + street_name_ = nullptr; + house_number_ = nullptr; + apartment_number_ = nullptr; + scanner->RewindTo(cursor_position); return false; } bool AddressField::ParseAddress(AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { if (street_name_ && house_number_) { return false; } @@ -253,7 +287,7 @@ bool AddressField::ParseAddress(AutofillScanner* scanner, } bool AddressField::ParseAddressLines(AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { // We only match the string "address" in page text, not in element names, // because sometimes every element in a group of address fields will have // a name containing the string "address"; for example, on the page @@ -267,23 +301,26 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner, base::string16 pattern = UTF8ToUTF16(kAddressLine1Re); base::string16 label_pattern = UTF8ToUTF16(kAddressLine1LabelRe); - // In JSON : ADDRESS_LINE_1 - auto& patterns_l1 = PatternProvider::GetInstance().GetMatchPatterns( - "ADDRESS_LINE_1", page_language); - if (!ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT, patterns_l1, - &address1_, {log_manager_, "kAddressLine1Re"}) && + const std::vector<MatchingPattern>& address_line1_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_LINE_1", + page_language); + + if (!ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT, + address_line1_patterns, &address1_, + {log_manager_, "kAddressLine1Re"}) && !ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT, - patterns_l1, &address1_, + address_line1_patterns, &address1_, {log_manager_, "kAddressLine1LabelRe"}) && !ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_TEXT_AREA, - patterns_l1, &street_address_, + address_line1_patterns, &street_address_, {log_manager_, "kAddressLine1Re"}, {.augment_types = MATCH_TEXT_AREA}) && - !ParseFieldSpecifics( - scanner, label_pattern, MATCH_LABEL | MATCH_TEXT_AREA, patterns_l1, - &street_address_, {log_manager_, "kAddressLine1LabelRe"}, - {.augment_types = MATCH_TEXT_AREA})) + !ParseFieldSpecifics(scanner, label_pattern, + MATCH_LABEL | MATCH_TEXT_AREA, + address_line1_patterns, &street_address_, + {log_manager_, "kAddressLine1LabelRe"}, + {.augment_types = MATCH_TEXT_AREA})) return false; if (street_address_) @@ -294,32 +331,29 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner, // discussion on https://codereview.chromium.org/741493003/ pattern = UTF8ToUTF16(kAddressLine2Re); label_pattern = UTF8ToUTF16(kAddressLine2LabelRe); - // auto& patternsL2 = PatternProvider::GetInstance().GetMatchPatterns( - // "ADDRESS_HOME_LINE2", page_language); - // auto& patternsSA = PatternProvider::GetInstance().GetMatchPatterns( - // "ADDRESS_HOME_STREET_ADDRESS", page_language); - - // In JSON : ADDRESS_LINE_2 - auto& patterns_l2 = PatternProvider::GetInstance().GetMatchPatterns( - "ADDRESS_LINE_2", page_language); - // In JSON : ADDRESS_LINE_EXTRA - auto& patterns_le = PatternProvider::GetInstance().GetMatchPatterns( - "ADDRESS_LINE_EXTRA", page_language); - - if (!ParseField(scanner, pattern, patterns_l2, &address2_, + + const std::vector<MatchingPattern>& address_line2_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_LINE_2", + page_language); + + if (!ParseField(scanner, pattern, address_line2_patterns, &address2_, {log_manager_, "kAddressLine2Re"}) && !ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT, - patterns_l2, &address2_, + address_line2_patterns, &address2_, {log_manager_, "kAddressLine2LabelRe"})) return true; + const std::vector<MatchingPattern>& address_line_extra_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_LINE_EXTRA", + page_language); + // Optionally parse address line 3. This uses the same label regexp as // address 2 above. pattern = UTF8ToUTF16(kAddressLinesExtraRe); - if (!ParseField(scanner, pattern, patterns_le, &address3_, + if (!ParseField(scanner, pattern, address_line_extra_patterns, &address3_, {log_manager_, "kAddressLinesExtraRe"}) && !ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT, - patterns_l2, &address3_, + address_line2_patterns, &address3_, {log_manager_, "kAddressLine2LabelRe"})) return true; @@ -329,7 +363,7 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner, // Since these are rare, don't bother considering unlabeled lines as extra // address lines. pattern = UTF8ToUTF16(kAddressLinesExtraRe); - while (ParseField(scanner, pattern, patterns_le, nullptr, + while (ParseField(scanner, pattern, address_line_extra_patterns, nullptr, {log_manager_, "kAddressLinesExtraRe"})) { // Consumed a surplus line, try for another. } @@ -337,20 +371,20 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner, } bool AddressField::ParseCountry(AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { if (country_) return false; - // In JSON : COUNTRY - auto& patterns_c = + const std::vector<MatchingPattern>& country_patterns = PatternProvider::GetInstance().GetMatchPatterns("COUNTRY", page_language); - auto& patterns_cl = PatternProvider::GetInstance().GetMatchPatterns( - "COUNTRY_LOCATION", page_language); + const std::vector<MatchingPattern>& country_patternsl = + PatternProvider::GetInstance().GetMatchPatterns("COUNTRY_LOCATION", + page_language); scanner->SaveCursor(); if (ParseFieldSpecifics(scanner, UTF8ToUTF16(kCountryRe), MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH, - patterns_c, &country_, + country_patterns, &country_, {log_manager_, "kCountryRe"})) { return true; } @@ -360,56 +394,70 @@ bool AddressField::ParseCountry(AutofillScanner* scanner, scanner->Rewind(); return ParseFieldSpecifics( scanner, UTF8ToUTF16(kCountryLocationRe), - MATCH_LABEL | MATCH_NAME | MATCH_SELECT | MATCH_SEARCH, patterns_cl, + MATCH_LABEL | MATCH_NAME | MATCH_SELECT | MATCH_SEARCH, country_patternsl, &country_, {log_manager_, "kCountryLocationRe"}); } bool AddressField::ParseZipCode(AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { if (zip_) return false; - // auto& patternsZ = PatternProvider::GetInstance().GetMatchPatterns( - // "ADDRESS_HOME_ZIP", page_language); - // In JSON : ZIP_CODE - auto& patterns_z = PatternProvider::GetInstance().GetMatchPatterns( - "ZIP_CODE", page_language); - // In JSON : ZIP_4 - auto& patterns_z4 = + const std::vector<MatchingPattern>& zip_code_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ZIP_CODE", + page_language); + + const std::vector<MatchingPattern>& four_digit_zip_code_patterns = PatternProvider::GetInstance().GetMatchPatterns("ZIP_4", page_language); if (!ParseFieldSpecifics(scanner, UTF8ToUTF16(kZipCodeRe), kZipCodeMatchType, - patterns_z, &zip_, {log_manager_, "kZipCodeRe"})) { + zip_code_patterns, &zip_, + {log_manager_, "kZipCodeRe"})) { return false; } // Look for a zip+4, whose field name will also often contain // the substring "zip". ParseFieldSpecifics(scanner, UTF8ToUTF16(kZip4Re), kZipCodeMatchType, - patterns_z4, &zip4_, {log_manager_, "kZip4Re"}); + four_digit_zip_code_patterns, &zip4_, + {log_manager_, "kZip4Re"}); return true; } +bool AddressField::ParseDependentLocality(AutofillScanner* scanner, + const LanguageCode& page_language) { + const bool is_enabled_dependent_locality_parsing = + base::FeatureList::IsEnabled( + features::kAutofillEnableDependentLocalityParsing); + // TODO(crbug.com/1157405) Remove feature check when launched. + if (dependent_locality_ || !is_enabled_dependent_locality_parsing) + return false; + + const std::vector<MatchingPattern>& dependent_locality_patterns = + PatternProvider::GetInstance().GetMatchPatterns( + "ADDRESS_HOME_DEPENDENT_LOCALITY", page_language); + return ParseFieldSpecifics(scanner, UTF8ToUTF16(kDependentLocalityRe), + kDependentLocalityMatchType, + dependent_locality_patterns, &dependent_locality_, + {log_manager_, "kDependentLocalityRe"}); +} + bool AddressField::ParseCity(AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { if (city_) return false; - // In JSON : CITY - auto& patterns_city = + const std::vector<MatchingPattern>& city_patterns = PatternProvider::GetInstance().GetMatchPatterns("CITY", page_language); return ParseFieldSpecifics(scanner, UTF8ToUTF16(kCityRe), kCityMatchType, - patterns_city, &city_, {log_manager_, "kCityRe"}); + city_patterns, &city_, {log_manager_, "kCityRe"}); } bool AddressField::ParseState(AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { if (state_) return false; - // auto& patterns = PatternProvider::GetInstance().GetMatchPatterns( - // "ADDRESS_HOME_STATE", page_language); - // In JSON : STATE - auto& patterns_state = + const std::vector<MatchingPattern>& patterns_state = PatternProvider::GetInstance().GetMatchPatterns("STATE", page_language); return ParseFieldSpecifics(scanner, UTF8ToUTF16(kStateRe), kStateMatchType, patterns_state, &state_, @@ -449,28 +497,43 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelSeparately( return RESULT_MATCH_NONE; } -bool AddressField::ParseCityStateCountryZipCode( +bool AddressField::ParseDependentLocalityCityStateCountryZipCode( AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { // The |scanner| is not pointing at a field. if (scanner->IsEnd()) return false; + int num_of_missing_types = 0; + for (const auto* field : + {dependent_locality_, city_, state_, country_, zip_}) { + if (!field) + ++num_of_missing_types; + } + // All the field types have already been detected. - if (city_ && state_ && country_ && zip_) + if (num_of_missing_types == 0) return false; // Exactly one field type is missing. - if (state_ && country_ && zip_) - return ParseCity(scanner, page_language); - if (city_ && country_ && zip_) - return ParseState(scanner, page_language); - if (city_ && state_ && zip_) - return ParseCountry(scanner, page_language); - if (city_ && state_ && country_) - return ParseZipCode(scanner, page_language); + if (num_of_missing_types == 1) { + if (!dependent_locality_) + return ParseDependentLocality(scanner, page_language); + if (!city_) + return ParseCity(scanner, page_language); + if (!state_) + return ParseState(scanner, page_language); + if (!country_) + return ParseCountry(scanner, page_language); + if (!zip_) + return ParseZipCode(scanner, page_language); + } // Check for matches to both the name and the label. + ParseNameLabelResult dependent_locality_result = + ParseNameAndLabelForDependentLocality(scanner, page_language); + if (dependent_locality_result == RESULT_MATCH_NAME_LABEL) + return true; ParseNameLabelResult city_result = ParseNameAndLabelForCity(scanner, page_language); if (city_result == RESULT_MATCH_NAME_LABEL) @@ -488,63 +551,76 @@ bool AddressField::ParseCityStateCountryZipCode( if (zip_result == RESULT_MATCH_NAME_LABEL) return true; + int num_of_matches = 0; + for (const auto result : {dependent_locality_result, city_result, + state_result, country_result, zip_result}) { + if (result != RESULT_MATCH_NONE) + ++num_of_matches; + } + // Check if there is only one potential match. - bool maybe_city = city_result != RESULT_MATCH_NONE; - bool maybe_state = state_result != RESULT_MATCH_NONE; - bool maybe_country = country_result != RESULT_MATCH_NONE; - bool maybe_zip = zip_result != RESULT_MATCH_NONE; - if (maybe_city && !maybe_state && !maybe_country && !maybe_zip) - return SetFieldAndAdvanceCursor(scanner, &city_); - if (maybe_state && !maybe_city && !maybe_country && !maybe_zip) - return SetFieldAndAdvanceCursor(scanner, &state_); - if (maybe_country && !maybe_city && !maybe_state && !maybe_zip) - return SetFieldAndAdvanceCursor(scanner, &country_); - if (maybe_zip && !maybe_city && !maybe_state && !maybe_country) - return ParseZipCode(scanner, page_language); + if (num_of_matches == 1) { + if (dependent_locality_result != RESULT_MATCH_NONE) + return SetFieldAndAdvanceCursor(scanner, &dependent_locality_); + if (city_result != RESULT_MATCH_NONE) + return SetFieldAndAdvanceCursor(scanner, &city_); + if (state_result != RESULT_MATCH_NONE) + return SetFieldAndAdvanceCursor(scanner, &state_); + if (country_result != RESULT_MATCH_NONE) + return SetFieldAndAdvanceCursor(scanner, &country_); + if (zip_result != RESULT_MATCH_NONE) + return ParseZipCode(scanner, page_language); + } // If there is a clash between the country and the state, set the type of // the field to the country. - if (maybe_state && maybe_country && !maybe_city && !maybe_zip) + if (num_of_matches == 2 && state_result != RESULT_MATCH_NONE && + country_result != RESULT_MATCH_NONE) return SetFieldAndAdvanceCursor(scanner, &country_); - // Otherwise give the name priority over the label. - if (city_result == RESULT_MATCH_NAME) - return SetFieldAndAdvanceCursor(scanner, &city_); - if (state_result == RESULT_MATCH_NAME) - return SetFieldAndAdvanceCursor(scanner, &state_); - if (country_result == RESULT_MATCH_NAME) - return SetFieldAndAdvanceCursor(scanner, &country_); - if (zip_result == RESULT_MATCH_NAME) - return ParseZipCode(scanner, page_language); - - if (city_result == RESULT_MATCH_LABEL) - return SetFieldAndAdvanceCursor(scanner, &city_); - if (state_result == RESULT_MATCH_LABEL) - return SetFieldAndAdvanceCursor(scanner, &state_); - if (country_result == RESULT_MATCH_LABEL) - return SetFieldAndAdvanceCursor(scanner, &country_); - if (zip_result == RESULT_MATCH_LABEL) - return ParseZipCode(scanner, page_language); + // By default give the name priority over the label. + ParseNameLabelResult results_to_match[] = {RESULT_MATCH_NAME, + RESULT_MATCH_LABEL}; + if (page_language == LanguageCode("tr") && + base::FeatureList::IsEnabled( + features::kAutofillEnableLabelPrecedenceForTurkishAddresses)) { + // Give the label priority over the name to avoid misclassifications when + // the name has a misleading value (e.g. province field is named "city"). + std::swap(results_to_match[0], results_to_match[1]); + } + + for (const auto result : results_to_match) { + if (dependent_locality_result == result) + return SetFieldAndAdvanceCursor(scanner, &dependent_locality_); + if (city_result == result) + return SetFieldAndAdvanceCursor(scanner, &city_); + if (state_result == result) + return SetFieldAndAdvanceCursor(scanner, &state_); + if (country_result == result) + return SetFieldAndAdvanceCursor(scanner, &country_); + if (zip_result == result) + return ParseZipCode(scanner, page_language); + } return false; } AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForZipCode( AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { if (zip_) return RESULT_MATCH_NONE; - // In JSON : ZIP_CODE - auto& patterns_z = PatternProvider::GetInstance().GetMatchPatterns( - "ZIP_CODE", page_language); - // In JSON : - auto& patterns_z4 = + const std::vector<MatchingPattern>& zip_code_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ZIP_CODE", + page_language); + + const std::vector<MatchingPattern>& four_digit_zip_code_patterns = PatternProvider::GetInstance().GetMatchPatterns("ZIP_4", page_language); ParseNameLabelResult result = ParseNameAndLabelSeparately( - scanner, UTF8ToUTF16(kZipCodeRe), kZipCodeMatchType, patterns_z, &zip_, - {log_manager_, "kZipCodeRe"}); + scanner, UTF8ToUTF16(kZipCodeRe), kZipCodeMatchType, zip_code_patterns, + &zip_, {log_manager_, "kZipCodeRe"}); if (result != RESULT_MATCH_NAME_LABEL || scanner->IsEnd()) return result; @@ -565,33 +641,52 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForZipCode( // Look for a zip+4, whose field name will also often contain // the substring "zip". ParseFieldSpecifics(scanner, UTF8ToUTF16(kZip4Re), kZipCodeMatchType, - patterns_z4, &zip4_, {log_manager_, "kZip4Re"}); + four_digit_zip_code_patterns, &zip4_, + {log_manager_, "kZip4Re"}); } return result; } +AddressField::ParseNameLabelResult +AddressField::ParseNameAndLabelForDependentLocality( + AutofillScanner* scanner, + const LanguageCode& page_language) { + const bool is_enabled_dependent_locality_parsing = + base::FeatureList::IsEnabled( + features::kAutofillEnableDependentLocalityParsing); + // TODO(crbug.com/1157405) Remove feature check when launched. + if (dependent_locality_ || !is_enabled_dependent_locality_parsing) + return RESULT_MATCH_NONE; + + const std::vector<MatchingPattern>& dependent_locality_patterns = + PatternProvider::GetInstance().GetMatchPatterns( + "ADDRESS_HOME_DEPENDENT_LOCALITY", page_language); + return ParseNameAndLabelSeparately( + scanner, UTF8ToUTF16(kDependentLocalityRe), kDependentLocalityMatchType, + dependent_locality_patterns, &dependent_locality_, + {log_manager_, "kDependentLocalityRe"}); +} + AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCity( AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { if (city_) return RESULT_MATCH_NONE; - // In JSON : CITY - auto& patterns_city = + const std::vector<MatchingPattern>& city_patterns = PatternProvider::GetInstance().GetMatchPatterns("CITY", page_language); return ParseNameAndLabelSeparately(scanner, UTF8ToUTF16(kCityRe), - kCityMatchType, patterns_city, &city_, + kCityMatchType, city_patterns, &city_, {log_manager_, "kCityRe"}); } AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForState( AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { if (state_) return RESULT_MATCH_NONE; - // In JSON : STATE - auto& patterns_state = + const std::vector<MatchingPattern>& patterns_state = PatternProvider::GetInstance().GetMatchPatterns("STATE", page_language); return ParseNameAndLabelSeparately(scanner, UTF8ToUTF16(kStateRe), kStateMatchType, patterns_state, &state_, @@ -600,19 +695,20 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForState( AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCountry( AutofillScanner* scanner, - const std::string& page_language) { + const LanguageCode& page_language) { if (country_) return RESULT_MATCH_NONE; - // In JSON : COUNTRY - auto& patterns_c = + const std::vector<MatchingPattern>& country_patterns = PatternProvider::GetInstance().GetMatchPatterns("COUNTRY", page_language); - auto& patterns_cl = PatternProvider::GetInstance().GetMatchPatterns( - "COUNTRY_LOCATION", page_language); + + const std::vector<MatchingPattern>& country_location_patterns = + PatternProvider::GetInstance().GetMatchPatterns("COUNTRY_LOCATION", + page_language); ParseNameLabelResult country_result = ParseNameAndLabelSeparately( scanner, UTF8ToUTF16(kCountryRe), - MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH, patterns_c, &country_, + MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH, country_patterns, &country_, {log_manager_, "kCountryRe"}); if (country_result != RESULT_MATCH_NONE) return country_result; @@ -621,8 +717,9 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCountry( // "location". However, this only makes sense for select tags. return ParseNameAndLabelSeparately( scanner, UTF8ToUTF16(kCountryLocationRe), - MATCH_LABEL | MATCH_NAME | MATCH_SELECT | MATCH_SEARCH, patterns_cl, - &country_, {log_manager_, "kCountryLocationRe"}); + MATCH_LABEL | MATCH_NAME | MATCH_SELECT | MATCH_SEARCH, + country_location_patterns, &country_, + {log_manager_, "kCountryLocationRe"}); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/address_field.h b/chromium/components/autofill/core/browser/form_parsing/address_field.h index 0ee62ee3853..820ac0b2f11 100644 --- a/chromium/components/autofill/core/browser/form_parsing/address_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/address_field.h @@ -15,6 +15,7 @@ #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/form_parsing/form_field.h" #include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/common/language_code.h" namespace autofill { @@ -25,16 +26,9 @@ class LogManager; class AddressField : public FormField { public: static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); -#if defined(UNIT_TEST) - // Assign types to the fields for the testing purposes. - void AddClassificationsForTesting( - FieldCandidatesMap* field_candidates_for_testing) const { - AddClassifications(field_candidates_for_testing); - } -#endif protected: void AddClassifications(FieldCandidatesMap* field_candidates) const override; @@ -51,47 +45,47 @@ class AddressField : public FormField { static const int kZipCodeMatchType; static const int kCityMatchType; static const int kStateMatchType; + static const int kDependentLocalityMatchType; explicit AddressField(LogManager* log_manager); - bool ParseCompany(AutofillScanner* scanner, const std::string& page_language); + bool ParseCompany(AutofillScanner* scanner, + const LanguageCode& page_language); - bool ParseAddress(AutofillScanner* scanner, const std::string& page_language); + bool ParseAddress(AutofillScanner* scanner, + const LanguageCode& page_language); bool ParseAddressFieldSequence(AutofillScanner* scanner, - const std::string& page_language); + const LanguageCode& page_language); bool ParseAddressLines(AutofillScanner* scanner, - const std::string& page_language); + const LanguageCode& page_language); - bool ParseCountry(AutofillScanner* scanner, const std::string& page_language); + bool ParseCountry(AutofillScanner* scanner, + const LanguageCode& page_language); - bool ParseZipCode(AutofillScanner* scanner, const std::string& page_language); + bool ParseZipCode(AutofillScanner* scanner, + const LanguageCode& page_language); - bool ParseCity(AutofillScanner* scanner, const std::string& page_language); + bool ParseDependentLocality(AutofillScanner* scanner, + const LanguageCode& page_language); - bool ParseState(AutofillScanner* scanner, const std::string& page_language); + bool ParseCity(AutofillScanner* scanner, const LanguageCode& page_language); + + bool ParseState(AutofillScanner* scanner, const LanguageCode& page_language); // Parses the current field pointed to by |scanner|, if it exists, and tries - // to figure out whether the field's type: city, state, country, zip, or - // none of those. - bool ParseCityStateCountryZipCode(AutofillScanner* scanner, - const std::string& page_language); + // to determine if the field's type corresponds to one of the following: + // dependent locality, city, state, country, zip, or none of those. + bool ParseDependentLocalityCityStateCountryZipCode( + AutofillScanner* scanner, + const LanguageCode& page_language); // Like ParseFieldSpecifics(), but applies |pattern| against the name and // label of the current field separately. If the return value is // RESULT_MATCH_NAME_LABEL, then |scanner| advances and |match| is filled if // it is non-NULL. Otherwise |scanner| does not advance and |match| does not // change. - // ParseNameLabelResult ParseNameAndLabelSeparately( - // AutofillScanner* scanner, - // const base::string16& pattern, - // int match_type, - // AutofillField** match, - // const RegExLogging& logging); - - // New version of function above using new structure MatchingPattern and - // PatternProvider. ParseNameLabelResult ParseNameAndLabelSeparately( AutofillScanner* scanner, const base::string16& pattern, @@ -105,19 +99,23 @@ class AddressField : public FormField { // Otherwise |scanner| rewinds and the field is cleared. ParseNameLabelResult ParseNameAndLabelForZipCode( AutofillScanner* scanner, - const std::string& page_language); + const LanguageCode& page_language); + + ParseNameLabelResult ParseNameAndLabelForDependentLocality( + AutofillScanner* scanner, + const LanguageCode& page_language); ParseNameLabelResult ParseNameAndLabelForCity( AutofillScanner* scanner, - const std::string& page_language); + const LanguageCode& page_language); ParseNameLabelResult ParseNameAndLabelForCountry( AutofillScanner* scanner, - const std::string& page_language); + const LanguageCode& page_language); ParseNameLabelResult ParseNameAndLabelForState( AutofillScanner* scanner, - const std::string& page_language); + const LanguageCode& page_language); LogManager* log_manager_; AutofillField* company_ = nullptr; @@ -127,6 +125,8 @@ class AddressField : public FormField { AutofillField* address2_ = nullptr; AutofillField* address3_ = nullptr; AutofillField* street_address_ = nullptr; + AutofillField* apartment_number_ = nullptr; + AutofillField* dependent_locality_ = nullptr; AutofillField* city_ = nullptr; AutofillField* state_ = nullptr; AutofillField* zip_ = nullptr; diff --git a/chromium/components/autofill/core/browser/form_parsing/address_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/address_field_unittest.cc index bc6e0908571..63c6258f716 100644 --- a/chromium/components/autofill/core/browser/form_parsing/address_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/address_field_unittest.cc @@ -9,158 +9,57 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" -#include "components/autofill/core/browser/autofill_field.h" -#include "components/autofill/core/browser/form_parsing/autofill_scanner.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" +#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/form_field_data.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::ASCIIToUTF16; namespace autofill { -class AddressFieldTest : public testing::Test { +class AddressFieldTest : public FormFieldTest { public: AddressFieldTest() = default; AddressFieldTest(const AddressFieldTest&) = delete; AddressFieldTest& operator=(const AddressFieldTest&) = delete; protected: - // Downcast for tests. - static std::unique_ptr<AddressField> Parse(AutofillScanner* scanner) { - // An empty page_language means the language is unknown and patterns of all - // languages are used. - std::unique_ptr<FormField> field = - AddressField::Parse(scanner, /*page_language=*/"", nullptr); - return std::unique_ptr<AddressField>( - static_cast<AddressField*>(field.release())); + std::unique_ptr<FormField> Parse(AutofillScanner* scanner, + const LanguageCode& page_language) override { + return AddressField::Parse(scanner, page_language, nullptr); } - - std::vector<std::unique_ptr<AutofillField>> list_; - std::unique_ptr<AddressField> field_; - FieldCandidatesMap field_candidates_map_; - - // RAII object to mock the the PatternProvider. - TestPatternProvider test_pattern_provider_; }; TEST_F(AddressFieldTest, Empty) { - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_EQ(nullptr, field_.get()); + ClassifyAndVerify(ParseResult::NOT_PARSED); } TEST_F(AddressFieldTest, NonParse) { - list_.push_back(std::make_unique<AutofillField>()); - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_EQ(nullptr, field_.get()); + AddTextFormFieldData("", "", UNKNOWN_TYPE); + ClassifyAndVerify(ParseResult::NOT_PARSED); } TEST_F(AddressFieldTest, ParseOneLineAddress) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Address"); - field.name = ASCIIToUTF16("address"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("addr1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("addr1")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_LINE1, - field_candidates_map_[ASCIIToUTF16("addr1")].BestHeuristicType()); + AddTextFormFieldData("address", "Address", ADDRESS_HOME_LINE1); + ClassifyAndVerify(); } TEST_F(AddressFieldTest, ParseTwoLineAddress) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Address"); - field.name = ASCIIToUTF16("address"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("addr1"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("address2"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("addr2"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("addr1")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_LINE1, - field_candidates_map_[ASCIIToUTF16("addr1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("addr2")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_LINE2, - field_candidates_map_[ASCIIToUTF16("addr2")].BestHeuristicType()); + AddTextFormFieldData("address", "Address", ADDRESS_HOME_LINE1); + AddTextFormFieldData("address2", "Address", ADDRESS_HOME_LINE2); + ClassifyAndVerify(); } TEST_F(AddressFieldTest, ParseThreeLineAddress) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Address Line1"); - field.name = ASCIIToUTF16("Address1"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("addr1"))); - - field.label = ASCIIToUTF16("Address Line2"); - field.name = ASCIIToUTF16("Address2"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("addr2"))); - - field.label = ASCIIToUTF16("Address Line3"); - field.name = ASCIIToUTF16("Address3"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("addr3"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("addr1")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_LINE1, - field_candidates_map_[ASCIIToUTF16("addr1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("addr2")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_LINE2, - field_candidates_map_[ASCIIToUTF16("addr2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("addr3")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_LINE3, - field_candidates_map_[ASCIIToUTF16("addr3")].BestHeuristicType()); + AddTextFormFieldData("Address1", "Address Line 1", ADDRESS_HOME_LINE1); + AddTextFormFieldData("Address1", "Address Line 2", ADDRESS_HOME_LINE2); + AddTextFormFieldData("Address1", "Address Line 3", ADDRESS_HOME_LINE3); + ClassifyAndVerify(); } TEST_F(AddressFieldTest, ParseStreetAddressFromTextArea) { - FormFieldData field; - field.form_control_type = "textarea"; - - field.label = ASCIIToUTF16("Address"); - field.name = ASCIIToUTF16("address"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("addr"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("addr")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS, - field_candidates_map_[ASCIIToUTF16("addr")].BestHeuristicType()); + AddFormFieldData("textarea", "address", "Address", + ADDRESS_HOME_STREET_ADDRESS); + ClassifyAndVerify(); } // Tests that fields are classified as |ADDRESS_HOME_STREET_NAME| and @@ -172,63 +71,40 @@ TEST_F(AddressFieldTest, ParseStreetNameAndHouseNumber) { enabled.InitAndEnableFeature( features::kAutofillEnableSupportForMoreStructureInAddresses); - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Street"); - field.name = ASCIIToUTF16("street"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("street"))); - - field.label = ASCIIToUTF16("House number"); - field.name = ASCIIToUTF16("house-number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("house"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("street")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_STREET_NAME, - field_candidates_map_[ASCIIToUTF16("street")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("house")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_HOUSE_NUMBER, - field_candidates_map_[ASCIIToUTF16("house")].BestHeuristicType()); + AddTextFormFieldData("street", "Street", ADDRESS_HOME_STREET_NAME); + AddTextFormFieldData("house-number", "House number", + ADDRESS_HOME_HOUSE_NUMBER); + ClassifyAndVerify(); +} + +// Tests that fields are classified as |ADDRESS_HOME_STREET_NAME|, and +// |ADDRESS_HOME_HOUSE_NUMBER| |ADDRESS_HOME_APT_NUM| when they are labeled +// accordingly and all are present. +TEST_F(AddressFieldTest, ParseStreetNameAndHouseNumberAndApartmentNumber) { + // TODO(crbug.com/1125978): Remove once launched. + base::test::ScopedFeatureList enabled; + enabled.InitWithFeatures( + {features::kAutofillEnableSupportForMoreStructureInAddresses, + features::kAutofillEnableSupportForApartmentNumbers}, + {}); + + AddTextFormFieldData("street", "Street", ADDRESS_HOME_STREET_NAME); + AddTextFormFieldData("house-number", "House number", + ADDRESS_HOME_HOUSE_NUMBER); + AddTextFormFieldData("apartment", "apartment", ADDRESS_HOME_APT_NUM); + ClassifyAndVerify(); } // Tests that the field is not classified as |ADDRESS_HOME_STREET_NAME| when -// it is labeled accordingly but adjacent field classified as +// it is labeled accordingly but an adjacent field classified as // |ADDRESS_HOME_HOUSE_NUMBER| is absent. TEST_F(AddressFieldTest, NotParseStreetNameWithoutHouseNumber) { // TODO(crbug.com/1125978): Remove once launched. base::test::ScopedFeatureList enabled; enabled.InitAndEnableFeature( features::kAutofillEnableSupportForMoreStructureInAddresses); - - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Street"); - field.name = ASCIIToUTF16("street"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("street"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - - if (!field_.get()) - return; - field_->AddClassificationsForTesting(&field_candidates_map_); - if (field_candidates_map_.empty()) - return; - - EXPECT_NE(ADDRESS_HOME_STREET_NAME, - field_candidates_map_[ASCIIToUTF16("street")].BestHeuristicType()); + AddTextFormFieldData("street", "Street", ADDRESS_HOME_LINE1); + ClassifyAndVerify(); } // Tests that the field is not classified as |ADDRESS_HOME_HOUSE_NUMBER| when @@ -240,246 +116,122 @@ TEST_F(AddressFieldTest, NotParseHouseNumberWithoutStreetName) { enabled.InitAndEnableFeature( features::kAutofillEnableSupportForMoreStructureInAddresses); - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("House number"); - field.name = ASCIIToUTF16("house-number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("house"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); + AddTextFormFieldData("house-number", "House number", UNKNOWN_TYPE); + ClassifyAndVerify(ParseResult::NOT_PARSED); +} - if (!field_.get()) - return; - field_->AddClassificationsForTesting(&field_candidates_map_); - if (field_candidates_map_.empty()) - return; +// Tests that the dependent locality is correctly classified with +// an unambiguous field name and label. +TEST_F(AddressFieldTest, ParseDependentLocality) { + // TODO(crbug.com/1157405): Remove once launched. + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableDependentLocalityParsing); - EXPECT_NE(ADDRESS_HOME_HOUSE_NUMBER, - field_candidates_map_[ASCIIToUTF16("house")].BestHeuristicType()); + AddTextFormFieldData("neighborhood", "Neighborhood", + ADDRESS_HOME_DEPENDENT_LOCALITY); + ClassifyAndVerify(); } TEST_F(AddressFieldTest, ParseCity) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("City"); - field.name = ASCIIToUTF16("city"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("city1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("city1")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_CITY, - field_candidates_map_[ASCIIToUTF16("city1")].BestHeuristicType()); + AddTextFormFieldData("city", "City", ADDRESS_HOME_CITY); + ClassifyAndVerify(); } TEST_F(AddressFieldTest, ParseState) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("State"); - field.name = ASCIIToUTF16("state"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("state1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("state1")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_STATE, - field_candidates_map_[ASCIIToUTF16("state1")].BestHeuristicType()); + AddTextFormFieldData("state", "State", ADDRESS_HOME_STATE); + ClassifyAndVerify(); } TEST_F(AddressFieldTest, ParseZip) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Zip"); - field.name = ASCIIToUTF16("zip"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("zip1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("zip1")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_ZIP, - field_candidates_map_[ASCIIToUTF16("zip1")].BestHeuristicType()); + AddTextFormFieldData("zip", "Zip", ADDRESS_HOME_ZIP); + ClassifyAndVerify(); } TEST_F(AddressFieldTest, ParseStateAndZipOneLabel) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("State/Province, Zip/Postal Code"); - field.name = ASCIIToUTF16("state"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("state"))); - - field.label = ASCIIToUTF16("State/Province, Zip/Postal Code"); - field.name = ASCIIToUTF16("zip"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("zip"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("state")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_STATE, - field_candidates_map_[ASCIIToUTF16("state")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("zip")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_ZIP, - field_candidates_map_[ASCIIToUTF16("zip")].BestHeuristicType()); + AddTextFormFieldData("state", "State/Province, Zip/Postal Code", + ADDRESS_HOME_STATE); + AddTextFormFieldData("zip", "State/Province, Zip/Postal Code", + ADDRESS_HOME_ZIP); + ClassifyAndVerify(); } TEST_F(AddressFieldTest, ParseCountry) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Country"); - field.name = ASCIIToUTF16("country"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("country1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("country1")) != - field_candidates_map_.end()); - EXPECT_EQ( - ADDRESS_HOME_COUNTRY, - field_candidates_map_[ASCIIToUTF16("country1")].BestHeuristicType()); + AddTextFormFieldData("country", "Country", ADDRESS_HOME_COUNTRY); + ClassifyAndVerify(); } TEST_F(AddressFieldTest, ParseCompany) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Company"); - field.name = ASCIIToUTF16("company"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("company1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("company1")) != - field_candidates_map_.end()); - EXPECT_EQ( - COMPANY_NAME, - field_candidates_map_[ASCIIToUTF16("company1")].BestHeuristicType()); + AddTextFormFieldData("company", "Company", COMPANY_NAME); + ClassifyAndVerify(); } -// Tests that the city, state, country and zip-code fields are correctly -// classfied with unambiguous field names and labels. -TEST_F(AddressFieldTest, ParseCityStateCountryZipcodeTogether) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("City"); - field.name = ASCIIToUTF16("city"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("city1"))); - - field.label = ASCIIToUTF16("State"); - field.name = ASCIIToUTF16("state"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("state1"))); - - field.label = ASCIIToUTF16("Country"); - field.name = ASCIIToUTF16("country"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("country1"))); - - field.label = ASCIIToUTF16("Zip"); - field.name = ASCIIToUTF16("zip"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("zip1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("city1")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_CITY, - field_candidates_map_[ASCIIToUTF16("city1")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("state1")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_STATE, - field_candidates_map_[ASCIIToUTF16("state1")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("country1")) != - field_candidates_map_.end()); - EXPECT_EQ( - ADDRESS_HOME_COUNTRY, - field_candidates_map_[ASCIIToUTF16("country1")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("zip1")) != - field_candidates_map_.end()); - EXPECT_EQ(ADDRESS_HOME_ZIP, - field_candidates_map_[ASCIIToUTF16("zip1")].BestHeuristicType()); +// Tests that the dependent locality, city, state, country and zip-code +// fields are correctly classfied with unambiguous field names and labels. +TEST_F(AddressFieldTest, + ParseDependentLocalityCityStateCountryZipcodeTogether) { + // TODO(crbug.com/1157405): Remove once launched. + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableDependentLocalityParsing); + + AddTextFormFieldData("neighborhood", "Neighborhood", + ADDRESS_HOME_DEPENDENT_LOCALITY); + AddTextFormFieldData("city", "City", ADDRESS_HOME_CITY); + AddTextFormFieldData("state", "State", ADDRESS_HOME_STATE); + AddTextFormFieldData("country", "Country", ADDRESS_HOME_COUNTRY); + AddTextFormFieldData("zip", "Zip", ADDRESS_HOME_ZIP); + ClassifyAndVerify(); } // Tests that the field is classified as |ADDRESS_HOME_COUNTRY| when the field // label contains 'Region'. TEST_F(AddressFieldTest, ParseCountryLabelRegion) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Country/Region"); - field.name = ASCIIToUTF16("country"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("country1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("country1")) != - field_candidates_map_.end()); - EXPECT_EQ( - ADDRESS_HOME_COUNTRY, - field_candidates_map_[ASCIIToUTF16("country1")].BestHeuristicType()); + AddTextFormFieldData("country", "Country/Region", ADDRESS_HOME_COUNTRY); + ClassifyAndVerify(); } // Tests that the field is classified as |ADDRESS_HOME_COUNTRY| when the field // name contains 'region'. TEST_F(AddressFieldTest, ParseCountryNameRegion) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Land"); - field.name = ASCIIToUTF16("client_region"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("country1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("country1")) != - field_candidates_map_.end()); - EXPECT_EQ( - ADDRESS_HOME_COUNTRY, - field_candidates_map_[ASCIIToUTF16("country1")].BestHeuristicType()); + AddTextFormFieldData("client_region", "Land", ADDRESS_HOME_COUNTRY); + ClassifyAndVerify(); +} + +// Tests that city and state fields are classified correctly when their names +// contain keywords for different types. This is achieved by giving the priority +// to the label over the name for pages in Turkish. +TEST_F(AddressFieldTest, ParseTurkishCityStateWithLabelPrecedence) { + // TODO(crbug.com/1156315): Remove once launched. + base::test::ScopedFeatureList enabled; + enabled.InitAndEnableFeature( + features::kAutofillEnableLabelPrecedenceForTurkishAddresses); + + AddTextFormFieldData("city", "Il", ADDRESS_HOME_STATE); + AddTextFormFieldData("county", "Ilce", ADDRESS_HOME_CITY); + ClassifyAndVerify(ParseResult::PARSED, LanguageCode("tr")); +} + +// Tests that address name is not misclassified as address. +TEST_F(AddressFieldTest, NotParseAddressName) { + AddTextFormFieldData("address", "Adres Başlığı", UNKNOWN_TYPE); + ClassifyAndVerify(ParseResult::NOT_PARSED, LanguageCode("tr")); +} + +// Tests that the address components sequence in a label is classified +// as |ADDRESS_HOME_LINE1|. +TEST_F(AddressFieldTest, ParseAddressComponentsSequenceAsAddressLine1) { + AddTextFormFieldData("detail", "Улица, дом, квартира", ADDRESS_HOME_LINE1); + ClassifyAndVerify(ParseResult::PARSED, LanguageCode("ru")); +} + +// Tests that the address components sequence in a label is classified +// as |ADDRESS_HOME_STREET_ADDRESS|. +TEST_F(AddressFieldTest, ParseAddressComponentsSequenceAsStreetAddress) { + AddFormFieldData("textarea", "detail", + "Mahalle, sokak, cadde ve diğer bilgilerinizi girin", + ADDRESS_HOME_STREET_ADDRESS); + ClassifyAndVerify(ParseResult::PARSED, LanguageCode("tr")); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/autofill_parsing_utils.cc b/chromium/components/autofill/core/browser/form_parsing/autofill_parsing_utils.cc index ddd418eca54..33a4e9db8d4 100644 --- a/chromium/components/autofill/core/browser/form_parsing/autofill_parsing_utils.cc +++ b/chromium/components/autofill/core/browser/form_parsing/autofill_parsing_utils.cc @@ -7,10 +7,10 @@ namespace autofill { MatchingPattern::MatchingPattern() = default; -MatchingPattern::MatchingPattern(const MatchingPattern& mp) = default; -MatchingPattern& MatchingPattern::operator=(const MatchingPattern& mp) = - default; - +MatchingPattern::MatchingPattern(const MatchingPattern&) = default; +MatchingPattern& MatchingPattern::operator=(const MatchingPattern&) = default; +MatchingPattern::MatchingPattern(MatchingPattern&&) = default; +MatchingPattern& MatchingPattern::operator=(MatchingPattern&&) = default; MatchingPattern::~MatchingPattern() = default; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/autofill_parsing_utils.h b/chromium/components/autofill/core/browser/form_parsing/autofill_parsing_utils.h index b24b5b5a5d7..1a4160c01aa 100644 --- a/chromium/components/autofill/core/browser/form_parsing/autofill_parsing_utils.h +++ b/chromium/components/autofill/core/browser/form_parsing/autofill_parsing_utils.h @@ -8,6 +8,8 @@ #include <base/optional.h> #include <string> +#include "components/autofill/core/common/language_code.h" + namespace autofill { // A bit-field used for matching specific parts of a field in question. @@ -45,17 +47,18 @@ constexpr int MATCH_DEFAULT = MATCH_ATTRIBUTES_DEFAULT | MATCH_INPUTS_DEFAULT; // to recognize incorrect matches. struct MatchingPattern { MatchingPattern(); - MatchingPattern(const MatchingPattern& mp); - MatchingPattern& operator=(const MatchingPattern& mp); + MatchingPattern(const MatchingPattern&); + MatchingPattern& operator=(const MatchingPattern&); + MatchingPattern(MatchingPattern&&); + MatchingPattern& operator=(MatchingPattern&&); ~MatchingPattern(); - std::string pattern_identifier; + LanguageCode language; std::string positive_pattern; - float positive_score = 1.1f; - base::Optional<std::string> negative_pattern; - int match_field_attributes; - int match_field_input_types; - std::string language; + std::string negative_pattern; + float positive_score = 1.1; + uint8_t match_field_attributes; + uint16_t match_field_input_types; }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc index 27ee112a819..cb1ab38fd8d 100644 --- a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc @@ -24,6 +24,7 @@ #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" #include "components/autofill/core/browser/form_parsing/form_field.h" #include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_features.h" #include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" @@ -83,7 +84,7 @@ bool FieldCanFitDataForFieldType(int max_length, ServerFieldType type) { // static std::unique_ptr<FormField> CreditCardField::Parse( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { if (scanner->IsEnd()) return nullptr; @@ -91,18 +92,23 @@ std::unique_ptr<FormField> CreditCardField::Parse( auto credit_card_field = std::make_unique<CreditCardField>(log_manager); size_t saved_cursor = scanner->SaveCursor(); int nb_unknown_fields = 0; + bool cardholder_name_match_has_low_confidence = false; - auto& patterns = PatternProvider::GetInstance().GetMatchPatterns( - "NAME_ON_CARD", page_language); - // In JSON : NAME_ON_CARD_CONTEXTUAL - auto& patterns_cont = PatternProvider::GetInstance().GetMatchPatterns( - "NAME_ON_CARD_CONTEXTUAL", page_language); - // In JSON : LAST_NAME - auto& patterns_nl = PatternProvider::GetInstance().GetMatchPatterns( - "LAST_NAME", page_language); - // In JSON : CARD_CVC - auto& patterns_cvc = PatternProvider::GetInstance().GetMatchPatterns( - CREDIT_CARD_VERIFICATION_CODE, page_language); + const std::vector<MatchingPattern>& name_on_card_patterns = + PatternProvider::GetInstance().GetMatchPatterns("NAME_ON_CARD", + page_language); + + const std::vector<MatchingPattern>& name_on_card_contextual_patterns = + PatternProvider::GetInstance().GetMatchPatterns("NAME_ON_CARD_CONTEXTUAL", + page_language); + + const std::vector<MatchingPattern>& last_name_patterns = + PatternProvider::GetInstance().GetMatchPatterns("LAST_NAME", + page_language); + + const std::vector<MatchingPattern>& cvc_patterns = + PatternProvider::GetInstance().GetMatchPatterns( + CREDIT_CARD_VERIFICATION_CODE, page_language); // Credit card fields can appear in many different orders. // We loop until no more credit card related fields are found, see |break| at @@ -113,8 +119,8 @@ std::unique_ptr<FormField> CreditCardField::Parse( break; if (!credit_card_field->cardholder_) { - if (ParseField(scanner, base::UTF8ToUTF16(kNameOnCardRe), patterns, - &credit_card_field->cardholder_, + if (ParseField(scanner, base::UTF8ToUTF16(kNameOnCardRe), + name_on_card_patterns, &credit_card_field->cardholder_, {log_manager, "kNameOnCardRe"})) { continue; } @@ -128,8 +134,10 @@ std::unique_ptr<FormField> CreditCardField::Parse( if (fields > 0 && !credit_card_field->expiration_month_ && ParseField(scanner, base::UTF8ToUTF16(kNameOnCardContextualRe), - patterns_cont, &credit_card_field->cardholder_, + name_on_card_contextual_patterns, + &credit_card_field->cardholder_, {log_manager, "kNameOnCardContextualRe"})) { + cardholder_name_match_has_low_confidence = true; continue; } } else if (!credit_card_field->cardholder_last_) { @@ -138,8 +146,8 @@ std::unique_ptr<FormField> CreditCardField::Parse( // and haven't yet parsed the expiration date (which usually appears at // the end). if (!credit_card_field->expiration_month_ && - ParseField(scanner, base::UTF8ToUTF16(kLastNameRe), patterns_nl, - &credit_card_field->cardholder_last_, + ParseField(scanner, base::UTF8ToUTF16(kLastNameRe), + last_name_patterns, &credit_card_field->cardholder_last_, {log_manager, "kLastNameRe"})) { continue; } @@ -166,7 +174,7 @@ std::unique_ptr<FormField> CreditCardField::Parse( if (!credit_card_field->verification_ && ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kCardCvcRe), - kMatchNumTelAndPwd, patterns_cvc, + kMatchNumTelAndPwd, cvc_patterns, &credit_card_field->verification_, {log_manager, "kCardCvcRe"})) { // A couple of sites have multiple verification codes right after another. @@ -182,7 +190,7 @@ std::unique_ptr<FormField> CreditCardField::Parse( scanner->RewindTo(scanner->SaveCursor() - 2); if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kCardCvcRe), - kMatchNumTelAndPwd, patterns_cvc, + kMatchNumTelAndPwd, cvc_patterns, &credit_card_field->verification_, {log_manager, "kCardCvcRe"})) { // Reset the current cvv (The verification parse overwrote it). @@ -205,8 +213,9 @@ std::unique_ptr<FormField> CreditCardField::Parse( // TODO(crbug.com/591816): Make sure parsing cc-numbers of type password // doesn't have bad side effects. AutofillField* current_number_field; - auto& patterns = PatternProvider::GetInstance().GetMatchPatterns( - CREDIT_CARD_NUMBER, page_language); + const std::vector<MatchingPattern>& patterns = + PatternProvider::GetInstance().GetMatchPatterns(CREDIT_CARD_NUMBER, + page_language); if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kCardNumberRe), kMatchNumTelAndPwd, patterns, ¤t_number_field, {log_manager, "kCardNumberRe"})) { @@ -271,8 +280,18 @@ std::unique_ptr<FormField> CreditCardField::Parse( // Some pages have a billing address field after the cardholder name field. // For that case, allow only just the cardholder name field. The remaining // CC fields will be picked up in a following CreditCardField. - if (credit_card_field->cardholder_) - return std::move(credit_card_field); + if (credit_card_field->cardholder_) { + // If we got the cardholder name with a dangerous check, require at least a + // card number and one of expiration or verification fields. + if (!base::FeatureList::IsEnabled( + features::kAutofillStrictContextualCardNameConditions) || + !cardholder_name_match_has_low_confidence || + (!credit_card_field->numbers_.empty() && + (credit_card_field->verification_ || + credit_card_field->HasExpiration()))) { + return std::move(credit_card_field); + } + } // On some pages, the user selects a card type using radio buttons // (e.g. test page Apple Store Billing.html). We can't handle that yet, @@ -333,7 +352,7 @@ bool CreditCardField::LikelyCardMonthSelectField(AutofillScanner* scanner) { bool CreditCardField::LikelyCardYearSelectField( AutofillScanner* scanner, LogManager* log_manager, - const std::string& page_language) { + const LanguageCode& page_language) { if (scanner->IsEnd()) return false; @@ -352,11 +371,10 @@ bool CreditCardField::LikelyCardYearSelectField( } // Another way to eliminate days - filter out 'day' fields. - // In JSON : DAY (only in JSON) - auto& patterns_day = + const std::vector<MatchingPattern>& day_patterns = PatternProvider::GetInstance().GetMatchPatterns("DAY", page_language); if (FormField::ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kDayRe), - MATCH_DEFAULT | MATCH_SELECT, patterns_day, + MATCH_DEFAULT | MATCH_SELECT, day_patterns, nullptr, {log_manager, "kDayRe"})) { return false; } @@ -414,7 +432,7 @@ bool CreditCardField::LikelyCardTypeSelectField(AutofillScanner* scanner) { // static bool CreditCardField::IsGiftCardField(AutofillScanner* scanner, LogManager* log_manager, - const std::string& page_language) { + const LanguageCode& page_language) { if (scanner->IsEnd()) return false; @@ -422,31 +440,33 @@ bool CreditCardField::IsGiftCardField(AutofillScanner* scanner, MATCH_DEFAULT | MATCH_NUMBER | MATCH_TELEPHONE | MATCH_SEARCH; size_t saved_cursor = scanner->SaveCursor(); - // In JSON : DEBIT_CARD (only in JSON) - auto& patterns_d = PatternProvider::GetInstance().GetMatchPatterns( - "DEBIT_CARD", page_language); - // In JSON : DEBIT_GIFT_CARD (only in JSON) - auto& patterns_dg = PatternProvider::GetInstance().GetMatchPatterns( - "DEBIT_GIFT_CARD", page_language); - // In JSON : GIFT_CARD (only in JSON) - auto& patterns_g = PatternProvider::GetInstance().GetMatchPatterns( - "GIFT_CARD", page_language); + const std::vector<MatchingPattern>& debit_cards_patterns = + PatternProvider::GetInstance().GetMatchPatterns("DEBIT_CARD", + page_language); + + const std::vector<MatchingPattern>& debit_gift_card_patterns = + PatternProvider::GetInstance().GetMatchPatterns("DEBIT_GIFT_CARD", + page_language); + + const std::vector<MatchingPattern>& gift_card_patterns = + PatternProvider::GetInstance().GetMatchPatterns("GIFT_CARD", + page_language); if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kDebitCardRe), - kMatchFieldTypes, patterns_d, nullptr, + kMatchFieldTypes, debit_cards_patterns, nullptr, {log_manager, "kDebitCardRe"})) { scanner->RewindTo(saved_cursor); return false; } if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kDebitGiftCardRe), - kMatchFieldTypes, patterns_dg, nullptr, + kMatchFieldTypes, debit_gift_card_patterns, nullptr, {log_manager, "kDebitGiftCardRe"})) { scanner->RewindTo(saved_cursor); return false; } return ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kGiftCardRe), - kMatchFieldTypes, patterns_g, nullptr, + kMatchFieldTypes, gift_card_patterns, nullptr, {log_manager, "kGiftCardRe"}); } @@ -504,7 +524,7 @@ void CreditCardField::AddClassifications( bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, LogManager* log_manager, - const std::string& page_language) { + const LanguageCode& page_language) { if (!expiration_date_ && base::LowerCaseEqualsASCII( scanner->Cursor()->form_control_type, "month")) { expiration_date_ = scanner->Cursor(); @@ -538,22 +558,28 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, const int kMatchCCType = MATCH_DEFAULT | MATCH_NUMBER | MATCH_TELEPHONE | MATCH_SELECT | MATCH_SEARCH; - // In JSON : CARD_EXP_MONTH - auto& patterns_m = PatternProvider::GetInstance().GetMatchPatterns( - CREDIT_CARD_EXP_MONTH, page_language); - // In JSON : CARD_EXP_YEAR - auto& patterns_y = PatternProvider::GetInstance().GetMatchPatterns( - "CREDIT_CARD_EXP_YEAR", page_language); - auto& patterns_mm = PatternProvider::GetInstance().GetMatchPatterns( - "CREDIT_CARD_EXP_MONTH_BEFORE_YEAR", page_language); - auto& patterns_yy = PatternProvider::GetInstance().GetMatchPatterns( - "CREDIT_CARD_EXP_YEAR_AFTER_MONTH", page_language); + const std::vector<MatchingPattern>& cc_exp_month_patterns = + PatternProvider::GetInstance().GetMatchPatterns(CREDIT_CARD_EXP_MONTH, + page_language); + + const std::vector<MatchingPattern>& cc_exp_year_patterns = + PatternProvider::GetInstance().GetMatchPatterns("CREDIT_CARD_EXP_YEAR", + page_language); + + const std::vector<MatchingPattern>& cc_exp_month_before_year_patterns = + PatternProvider::GetInstance().GetMatchPatterns( + "CREDIT_CARD_EXP_MONTH_BEFORE_YEAR", page_language); + + const std::vector<MatchingPattern>& cc_exp_year_after_month_patterns = + PatternProvider::GetInstance().GetMatchPatterns( + "CREDIT_CARD_EXP_YEAR_AFTER_MONTH", page_language); if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kExpirationMonthRe), - kMatchCCType, patterns_m, &expiration_month_, + kMatchCCType, cc_exp_month_patterns, + &expiration_month_, {log_manager_, "kExpirationMonthRe"}) && ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kExpirationYearRe), - kMatchCCType, patterns_y, &expiration_year_, + kMatchCCType, cc_exp_year_patterns, &expiration_year_, {log_manager_, "kExpirationYearRe"})) { return true; } @@ -561,11 +587,11 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, // If that fails, look for just MM and/or YY(YY). scanner->RewindTo(month_year_saved_cursor); if (ParseFieldSpecifics(scanner, base::ASCIIToUTF16("^mm$"), kMatchCCType, - patterns_mm, &expiration_month_, + cc_exp_month_before_year_patterns, &expiration_month_, {log_manager_, "^mm$"}) && ParseFieldSpecifics(scanner, base::ASCIIToUTF16("^(yy|yyyy)$"), - kMatchCCType, patterns_yy, &expiration_year_, - {log_manager_, "^(yy|yyyy)$"})) { + kMatchCCType, cc_exp_year_after_month_patterns, + &expiration_year_, {log_manager_, "^(yy|yyyy)$"})) { return true; } @@ -580,23 +606,24 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, return false; // Try to look for a 2-digit year expiration date. - // In JSON : CARD_EXP_DATE_2_DIGIT_YEAR - auto& patterns_2dy = PatternProvider::GetInstance().GetMatchPatterns( - CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, page_language); - if (ParseFieldSpecifics(scanner, - base::UTF8ToUTF16(kExpirationDate2DigitYearRe), - kMatchCCType, patterns_2dy, &expiration_date_, - {log_manager_, "kExpirationDate2DigitYearRe"})) { + const std::vector<MatchingPattern>& cc_exp_2digit_year_patterns = + PatternProvider::GetInstance().GetMatchPatterns( + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, page_language); + if (ParseFieldSpecifics( + scanner, base::UTF8ToUTF16(kExpirationDate2DigitYearRe), kMatchCCType, + cc_exp_2digit_year_patterns, &expiration_date_, + {log_manager_, "kExpirationDate2DigitYearRe"})) { exp_year_type_ = CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR; expiration_month_ = nullptr; return true; } // Try to look for a generic expiration date field. (2 or 4 digit year) - auto& patterns_exp_d = PatternProvider::GetInstance().GetMatchPatterns( - "CREDIT_CARD_EXP_DATE", page_language); + const std::vector<MatchingPattern>& cc_exp_date_patterns = + PatternProvider::GetInstance().GetMatchPatterns("CREDIT_CARD_EXP_DATE", + page_language); if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kExpirationDateRe), - kMatchCCType, patterns_exp_d, &expiration_date_, + kMatchCCType, cc_exp_date_patterns, &expiration_date_, {log_manager_, "kExpirationDateRe"})) { // If such a field exists, but it cannot fit a 4-digit year expiration // date, then the likely possibility is that it is a 2-digit year expiration @@ -610,14 +637,15 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, } // Try to look for a 4-digit year expiration date. - auto& patterns_4dy = PatternProvider::GetInstance().GetMatchPatterns( - CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, page_language); + const std::vector<MatchingPattern>& cc_exp_date_4_digit_year_patterns = + PatternProvider::GetInstance().GetMatchPatterns( + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, page_language); if (FieldCanFitDataForFieldType(current_field_max_length, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) && - ParseFieldSpecifics(scanner, - base::UTF8ToUTF16(kExpirationDate4DigitYearRe), - kMatchCCType, patterns_4dy, &expiration_date_, - {log_manager_, "kExpirationDate4DigitYearRe"})) { + ParseFieldSpecifics( + scanner, base::UTF8ToUTF16(kExpirationDate4DigitYearRe), kMatchCCType, + cc_exp_date_4_digit_year_patterns, &expiration_date_, + {log_manager_, "kExpirationDate4DigitYearRe"})) { expiration_month_ = nullptr; return true; } diff --git a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h index a3aa409c065..8b3c7f69047 100644 --- a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.h @@ -13,6 +13,7 @@ #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/form_parsing/form_field.h" #include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/common/language_code.h" namespace autofill { @@ -25,7 +26,7 @@ class CreditCardField : public FormField { explicit CreditCardField(LogManager* log_manager); ~CreditCardField() override; static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); protected: @@ -44,7 +45,7 @@ class CreditCardField : public FormField { // to chrome://autofill-internals static bool LikelyCardYearSelectField(AutofillScanner* scanner, LogManager* log_manager, - const std::string& page_language); + const LanguageCode& page_language); // Returns true if |scanner| points to a <select> field that contains credit // card type options. @@ -56,13 +57,13 @@ class CreditCardField : public FormField { // a credit card. static bool IsGiftCardField(AutofillScanner* scanner, LogManager* log_manager, - const std::string& page_language); + const LanguageCode& page_language); // Parses the expiration month/year/date fields. Returns true if it finds // something new. bool ParseExpirationDate(AutofillScanner* scanner, LogManager* log_manager, - const std::string& page_language); + const LanguageCode& page_language); // For the combined expiration field we return |exp_year_type_|; otherwise if // |expiration_year_| is having year with |max_length| of 2-digits we return diff --git a/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc index 9d9815c4269..c2b1f7b8d61 100644 --- a/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/credit_card_field_unittest.cc @@ -11,10 +11,12 @@ #include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" +#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h" #include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/form_field_data.h" #include "testing/gtest/include/gtest/gtest.h" @@ -22,55 +24,60 @@ using base::ASCIIToUTF16; namespace autofill { -class CreditCardFieldTestBase { +class CreditCardFieldTestBase : public FormFieldTestBase { public: CreditCardFieldTestBase() = default; CreditCardFieldTestBase(const CreditCardFieldTestBase&) = delete; CreditCardFieldTestBase& operator=(const CreditCardFieldTestBase&) = delete; protected: - // Parses the contents of |list_| as a form, and stores the result into - // |field_|. - void Parse() { - AutofillScanner scanner(list_); - // An empty page_language means the language is unknown and patterns of all - // languages are used. - std::unique_ptr<FormField> field = - CreditCardField::Parse(&scanner, /*page_language=*/"", nullptr); - field_ = std::unique_ptr<CreditCardField>( - static_cast<CreditCardField*>(field.release())); + std::unique_ptr<FormField> Parse( + AutofillScanner* scanner, + const LanguageCode& page_language = LanguageCode("us")) override { + return CreditCardField::Parse(scanner, page_language, nullptr); } - void MultipleParses() { - std::unique_ptr<FormField> field; - + // Runs multiple parsing attempts until the end of the form is reached. + void ClassifyAndVerifyWithMultipleParses( + const LanguageCode& page_language = LanguageCode("")) { AutofillScanner scanner(list_); while (!scanner.IsEnd()) { // An empty page_language means the language is unknown and patterns of // all languages are used. - field = CreditCardField::Parse(&scanner, /*page_language=*/"", nullptr); - field_ = std::unique_ptr<CreditCardField>( - static_cast<CreditCardField*>(field.release())); + field_ = Parse(&scanner, page_language); if (field_ == nullptr) { scanner.Advance(); } else { - AddClassifications(); + field_->AddClassificationsForTesting(&field_candidates_map_); } } + TestClassificationExpectations(); } - // Associates fields with their corresponding types, based on the previous - // call to Parse(). - void AddClassifications() { - return field_->AddClassifications(&field_candidates_map_); + // Returns a vector of numeric months with a leading 0 and an additional "MM" + // entry. + std::vector<std::string> GetMonths() { + return std::vector<std::string>{"MM", "01", "02", "03", "04", "05", "06", + "07", "08", "09", "10", "11", "12"}; } - std::vector<std::unique_ptr<AutofillField>> list_; - std::unique_ptr<const CreditCardField> field_; - FieldCandidatesMap field_candidates_map_; + // Returns a vector of 10 consecutive years starting today in 2 digit format + // and an additional "YY" entry. + std::vector<std::string> Get2DigitYears() { + std::vector<std::string> years = {"YY"}; + + const base::Time time_now = AutofillClock::Now(); + base::Time::Exploded time_exploded; + time_now.UTCExplode(&time_exploded); + const int kYearsToAdd = 10; + + for (auto year = time_exploded.year; + year < time_exploded.year + kYearsToAdd; year++) { + years.push_back(base::NumberToString(year).substr(2)); + } - // RAII object to mock the the PatternProvider. - TestPatternProvider test_pattern_provider_; + return years; + } }; class CreditCardFieldTest : public CreditCardFieldTestBase, @@ -82,339 +89,89 @@ class CreditCardFieldTest : public CreditCardFieldTestBase, }; TEST_F(CreditCardFieldTest, Empty) { - Parse(); - ASSERT_EQ(nullptr, field_.get()); + ClassifyAndVerify(ParseResult::NOT_PARSED); } TEST_F(CreditCardFieldTest, NonParse) { - list_.push_back(std::make_unique<AutofillField>()); - Parse(); - ASSERT_EQ(nullptr, field_.get()); + AddTextFormFieldData("", "", UNKNOWN_TYPE); + + ClassifyAndVerify(ParseResult::NOT_PARSED); } TEST_F(CreditCardFieldTest, ParseCreditCardNoNumber) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Exp Month"); - field.name = ASCIIToUTF16("ccmonth"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month1"))); + AddTextFormFieldData("ccmonth", "Exp Month", UNKNOWN_TYPE); + AddTextFormFieldData("ccyear", "Exp Year", UNKNOWN_TYPE); - field.label = ASCIIToUTF16("Exp Year"); - field.name = ASCIIToUTF16("ccyear"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("year2"))); - - Parse(); - ASSERT_EQ(nullptr, field_.get()); + ClassifyAndVerify(ParseResult::NOT_PARSED); } TEST_F(CreditCardFieldTest, ParseCreditCardNoDate) { - FormFieldData field; - field.form_control_type = "text"; + AddTextFormFieldData("card_number", "Card Number", UNKNOWN_TYPE); - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number1"))); - - Parse(); - ASSERT_EQ(nullptr, field_.get()); + ClassifyAndVerify(ParseResult::NOT_PARSED); } TEST_F(CreditCardFieldTest, ParseMiniumCreditCard) { - FormFieldData field; - field.form_control_type = "text"; + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH); + AddTextFormFieldData("ccyear", "Exp Year", CREDIT_CARD_EXP_4_DIGIT_YEAR); - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number1"))); - - field.label = ASCIIToUTF16("Exp Month"); - field.name = ASCIIToUTF16("ccmonth"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month2"))); - - field.label = ASCIIToUTF16("Exp Year"); - field.name = ASCIIToUTF16("ccyear"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("year3"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number1")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month2")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year3")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year3")].BestHeuristicType()); + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(CreditCardFieldTest, ParseMinimumCreditCardWithExpiryDateOptions) { - FormFieldData cc_number_field; - FormFieldData month_field; - FormFieldData year_field; - - cc_number_field.form_control_type = "text"; - cc_number_field.label = ASCIIToUTF16("Card Number"); - cc_number_field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(cc_number_field, ASCIIToUTF16("number"))); - - // For month field, set the label and name to something which won't match - // any regex, so we can test matching of the options themselves. - month_field.form_control_type = "select-one"; - month_field.label = ASCIIToUTF16("Random label"); - month_field.name = ASCIIToUTF16("Random name"); - const std::vector<std::string> kMonths{"MM", "01", "02", "03", "04", - "05", "06", "07", "08", "09", - "10", "11", "12"}; - for (auto month : kMonths) { - month_field.option_contents.push_back(base::UTF8ToUTF16(month)); - month_field.option_values.push_back(base::UTF8ToUTF16(month)); - } - list_.push_back( - std::make_unique<AutofillField>(month_field, ASCIIToUTF16("month"))); - - // For year, keep the label and name to something which doesn't match regex - // so we can test matching of the options themselves. - year_field.form_control_type = "select-one"; - year_field.label = ASCIIToUTF16("Random label"); - year_field.name = ASCIIToUTF16("Random name"); - year_field.max_length = 2; - year_field.option_contents.push_back(base::ASCIIToUTF16("YY")); - year_field.option_values.push_back(base::ASCIIToUTF16("YY")); - - const base::Time time_now = AutofillClock::Now(); - base::Time::Exploded time_exploded; - time_now.UTCExplode(&time_exploded); - const int kYearsToAdd = 10; - - for (auto year = time_exploded.year; year < time_exploded.year + kYearsToAdd; - year++) { - year_field.option_contents.push_back( - base::NumberToString16(year).substr(2)); - year_field.option_values.push_back(base::NumberToString16(year).substr(2)); - } - list_.push_back( - std::make_unique<AutofillField>(year_field, ASCIIToUTF16("year"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_2_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year")].BestHeuristicType()); + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddSelectOneFormFieldData("Random Label", "Random Label", GetMonths(), + GetMonths(), CREDIT_CARD_EXP_MONTH); + AddSelectOneFormFieldDataWithLength("Random Label", "Random Label", 2, + Get2DigitYears(), Get2DigitYears(), + CREDIT_CARD_EXP_2_DIGIT_YEAR); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(CreditCardFieldTest, ParseFullCreditCard) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Name on Card"); - field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("name"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number"))); - - field.label = ASCIIToUTF16("Exp Month"); - field.name = ASCIIToUTF16("ccmonth"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month"))); - - field.label = ASCIIToUTF16("Exp Year"); - field.name = ASCIIToUTF16("ccyear"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("year"))); - - field.label = ASCIIToUTF16("Verification"); - field.name = ASCIIToUTF16("verification"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("cvc"))); - - field.form_control_type = "select-one"; - field.label = ASCIIToUTF16("Card Type"); - field.name = ASCIIToUTF16("card_type"); - field.option_contents.push_back(ASCIIToUTF16("visa")); - field.option_values.push_back(ASCIIToUTF16("visa")); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("type"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("type")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_TYPE, - field_candidates_map_[ASCIIToUTF16("type")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - field_candidates_map_[ASCIIToUTF16("name")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("cvc")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE, - field_candidates_map_[ASCIIToUTF16("cvc")].BestHeuristicType()); + AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL); + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH); + AddTextFormFieldData("ccyear", "Exp Year", CREDIT_CARD_EXP_4_DIGIT_YEAR); + AddTextFormFieldData("verification", "Verification", + CREDIT_CARD_VERIFICATION_CODE); + AddSelectOneFormFieldData("Card Type", "card_type", {"visa"}, {"visa"}, + CREDIT_CARD_TYPE); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(CreditCardFieldTest, ParseExpMonthYear) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Name on Card"); - field.name = ASCIIToUTF16("name_on_card"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number2"))); - - field.label = ASCIIToUTF16("ExpDate Month / Year"); - field.name = ASCIIToUTF16("ExpDate"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month3"))); - - field.label = ASCIIToUTF16("ExpDate Month / Year"); - field.name = ASCIIToUTF16("ExpDate"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("year4"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number2")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month3")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month3")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year4")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year4")].BestHeuristicType()); + AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL); + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddTextFormFieldData("ExpDate", "ExpDate Month / Year", + CREDIT_CARD_EXP_MONTH); + AddTextFormFieldData("ExpDate", "ExpDate Month / Year", + CREDIT_CARD_EXP_4_DIGIT_YEAR); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(CreditCardFieldTest, ParseExpMonthYear2) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Name on Card"); - field.name = ASCIIToUTF16("name_on_card"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number2"))); - - field.label = ASCIIToUTF16("Expiration date Month / Year"); - field.name = ASCIIToUTF16("ExpDate"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month3"))); - - field.label = ASCIIToUTF16("Expiration date Month / Year"); - field.name = ASCIIToUTF16("ExpDate"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("year4"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number2")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month3")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month3")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year4")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year4")].BestHeuristicType()); + AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL); + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddTextFormFieldData("ExpDate", "Expiration date Month / Year", + CREDIT_CARD_EXP_MONTH); + AddTextFormFieldData("ExpDate", "Expiration date Month / Year", + CREDIT_CARD_EXP_4_DIGIT_YEAR); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(CreditCardFieldTest, ParseGiftCard) { - FormFieldData field; - field.form_control_type = "text"; + AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL); + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddTextFormFieldData("gift.certificate", "Gift certificate", UNKNOWN_TYPE); + AddTextFormFieldData("gift-card", "Gift card", UNKNOWN_TYPE); - field.label = ASCIIToUTF16("Name on Card"); - field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("name"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number"))); - - field.label = ASCIIToUTF16("Gift certificate"); - field.name = ASCIIToUTF16("gift.certificate"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("giftcert"))); - - field.label = ASCIIToUTF16("Gift card"); - field.name = ASCIIToUTF16("gift-card"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("giftcard"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - field_candidates_map_[ASCIIToUTF16("name")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("giftcert")) == - field_candidates_map_.end()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("giftcard")) == - field_candidates_map_.end()); + ClassifyAndVerify(ParseResult::PARSED); } typedef struct { @@ -430,32 +187,13 @@ class ParseExpFieldTest : public CreditCardFieldTestBase, TEST_P(ParseExpFieldTest, ParseExpField) { auto test_case = GetParam(); - // Clean up after previous test cases. - list_.clear(); - field_.reset(); - field_candidates_map_.clear(); - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Name on Card"); - field.name = ASCIIToUTF16("name_on_card"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.form_control_type = test_case.cc_fields_form_control_type; - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("num2"))); - - field.label = ASCIIToUTF16(test_case.label); - if (test_case.max_length != 0) { - field.max_length = test_case.max_length; - } - field.name = ASCIIToUTF16("cc_exp"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("exp3"))); - - Parse(); + AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL); + AddFormFieldData(test_case.cc_fields_form_control_type, "card_number", + "Card Number", CREDIT_CARD_NUMBER); + AddFormFieldDataWithLength(test_case.cc_fields_form_control_type, "cc_exp", + test_case.label, test_case.max_length, + test_case.expected_prediction); // Assists in identifing which case has failed. SCOPED_TRACE(test_case.expected_prediction); @@ -466,31 +204,15 @@ TEST_P(ParseExpFieldTest, ParseExpField) { // Expect failure and continue to next test case. // The expiry date is a required field for credit card forms, and thus the // parse sets |field_| to nullptr. - EXPECT_EQ(nullptr, field_.get()); + ClassifyAndVerify(ParseResult::NOT_PARSED); return; } - // Ensure that the form was determined as valid. - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("num2")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("num2")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("exp3")) != - field_candidates_map_.end()); - EXPECT_EQ(test_case.expected_prediction, - field_candidates_map_[ASCIIToUTF16("exp3")].BestHeuristicType()); + ClassifyAndVerify(ParseResult::PARSED); } INSTANTIATE_TEST_SUITE_P( - CreditCardFieldTest, + , ParseExpFieldTest, testing::Values( // CC fields input_type="text" @@ -630,397 +352,155 @@ INSTANTIATE_TEST_SUITE_P( CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR})); TEST_F(CreditCardFieldTest, ParseCreditCardHolderNameWithCCFullName) { - FormFieldData field; - field.form_control_type = "text"; + AddTextFormFieldData("ccfullname", "Name", CREDIT_CARD_NAME_FULL); - field.label = ASCIIToUTF16("Name"); - field.name = ASCIIToUTF16("ccfullname"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); + ClassifyAndVerify(ParseResult::PARSED); } // Verifies that <input type="month"> controls are able to be parsed correctly. TEST_F(CreditCardFieldTest, ParseMonthControl) { - FormFieldData field; + AddTextFormFieldData("ccnumber", "Card number:", CREDIT_CARD_NUMBER); + AddFormFieldData("month", "ccexp", + "Expiration date:", CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR); - field.form_control_type = "text"; - field.label = ASCIIToUTF16("Card number:"); - field.name = ASCIIToUTF16("ccnumber"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number1"))); - - field.form_control_type = "month"; - field.label = ASCIIToUTF16("Expiration date:"); - field.name = ASCIIToUTF16("ccexp"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("date2"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number1")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("date2")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("date2")].BestHeuristicType()); + ClassifyAndVerify(ParseResult::PARSED); } // Verify that heuristics <input name="ccyear" maxlength="2"/> considers // *maxlength* attribute while parsing 2 Digit expiration year. TEST_F(CreditCardFieldTest, ParseCreditCardExpYear_2DigitMaxLength) { - FormFieldData field; - field.form_control_type = "text"; + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddTextFormFieldData("ccmonth", "Expiration Date", CREDIT_CARD_EXP_MONTH); + AddFormFieldDataWithLength("text", "ccyear", "Expiration Date", 2, + CREDIT_CARD_EXP_2_DIGIT_YEAR); - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number"))); - - field.label = ASCIIToUTF16("Expiration Date"); - field.name = ASCIIToUTF16("ccmonth"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month"))); - - field.name = ASCIIToUTF16("ccyear"); - field.max_length = 2; - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("year"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_2_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year")].BestHeuristicType()); + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(CreditCardFieldTest, ParseCreditCardNumberWithSplit) { FormFieldData field; field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number_q1"); - field.max_length = 4; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number1"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number_q2"); - field.max_length = 4; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number2"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number_q3"); - field.max_length = 4; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number3"))); - + AddFormFieldDataWithLength("text", "card_number_q1", "Card Number", 4, + CREDIT_CARD_NUMBER); + AddFormFieldDataWithLength("text", "card_number_q2", "Card Number", 4, + CREDIT_CARD_NUMBER); + AddFormFieldDataWithLength("text", "card_number_q3", "Card Number", 4, + CREDIT_CARD_NUMBER); // For last credit card number input field it simply ignores the |max_length| // attribute. So even having a very big number, does not conside it an invalid // split for autofilling. - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number_q4"); - field.max_length = 20; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number4"))); - - field.label = ASCIIToUTF16("Exp Month"); - field.name = ASCIIToUTF16("ccmonth"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month5"))); - - field.label = ASCIIToUTF16("Exp Year"); - field.name = ASCIIToUTF16("ccyear"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("year6"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number1")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number1")].BestHeuristicType()); - EXPECT_EQ(0U, list_[0]->credit_card_number_offset()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number2")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number2")].BestHeuristicType()); - EXPECT_EQ(4U, list_[1]->credit_card_number_offset()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number3")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number3")].BestHeuristicType()); - EXPECT_EQ(8U, list_[2]->credit_card_number_offset()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number4")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number4")].BestHeuristicType()); - EXPECT_EQ(12U, list_[3]->credit_card_number_offset()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month5")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month5")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year6")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year6")].BestHeuristicType()); + AddFormFieldDataWithLength("text", "card_number_q4", "Card Number", 20, + CREDIT_CARD_NUMBER); + + AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH); + AddTextFormFieldData("ccyear", "Exp Year", CREDIT_CARD_EXP_4_DIGIT_YEAR); + + ClassifyAndVerify(ParseResult::PARSED); + + // Test the for the right credit card number offsets. + ASSERT_TRUE(list_.size() > 4); + EXPECT_EQ(list_[0]->credit_card_number_offset(), 0U); + EXPECT_EQ(list_[1]->credit_card_number_offset(), 4U); + EXPECT_EQ(list_[2]->credit_card_number_offset(), 8U); + EXPECT_EQ(list_[3]->credit_card_number_offset(), 12U); } TEST_F(CreditCardFieldTest, ParseMultipleCreditCardNumbers) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Name on Card"); - field.name = ASCIIToUTF16("name_on_card"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number2"))); - - field.label = ASCIIToUTF16("Confirm Card Number"); - field.name = ASCIIToUTF16("confirm_card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number3"))); - - field.label = ASCIIToUTF16("Exp Month"); - field.name = ASCIIToUTF16("ccmonth"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month4"))); - - field.label = ASCIIToUTF16("Exp Year"); - field.name = ASCIIToUTF16("ccyear"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("year5"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number2")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number3")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number3")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month4")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month4")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year5")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year5")].BestHeuristicType()); + AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL); + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddTextFormFieldData("confirm_card_number", "Confirm Card Number", + CREDIT_CARD_NUMBER); + AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH); + AddTextFormFieldData("ccyear", "Exp Year", CREDIT_CARD_EXP_4_DIGIT_YEAR); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(CreditCardFieldTest, ParseFirstAndLastNames) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("First Name on Card"); - field.name = ASCIIToUTF16("cc-fname"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("Last Name"); - field.name = ASCIIToUTF16("cc-lname"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number3"))); - - field.label = ASCIIToUTF16("Exp Month"); - field.name = ASCIIToUTF16("ccmonth"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month4"))); - - field.label = ASCIIToUTF16("Exp Year"); - field.name = ASCIIToUTF16("ccyear"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("year5"))); - - Parse(); - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number3")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number3")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month4")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month4")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year5")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year5")].BestHeuristicType()); + AddTextFormFieldData("cc-fname", "First Name on Card", + CREDIT_CARD_NAME_FIRST); + AddTextFormFieldData("cc-lname", "Last Name", CREDIT_CARD_NAME_LAST); + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH); + AddTextFormFieldData("ccyear", "Exp Year", CREDIT_CARD_EXP_4_DIGIT_YEAR); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(CreditCardFieldTest, ParseConsecutiveCvc) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Name on Card"); - field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("name"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number"))); - - field.label = ASCIIToUTF16("Exp Month"); - field.name = ASCIIToUTF16("ccmonth"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month"))); - - field.label = ASCIIToUTF16("Exp Year"); - field.name = ASCIIToUTF16("ccyear"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("year"))); - - field.label = ASCIIToUTF16("Verification"); - field.name = ASCIIToUTF16("verification"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("cvc"))); - - field.label = ASCIIToUTF16("Verification"); - field.name = ASCIIToUTF16("verification"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("cvc2"))); - - MultipleParses(); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - field_candidates_map_[ASCIIToUTF16("name")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("cvc")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE, - field_candidates_map_[ASCIIToUTF16("cvc")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("cvc2")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE, - field_candidates_map_[ASCIIToUTF16("cvc2")].BestHeuristicType()); + AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL); + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH); + AddTextFormFieldData("ccyear", "Exp Year", CREDIT_CARD_EXP_4_DIGIT_YEAR); + AddTextFormFieldData("verification", "Verification", + CREDIT_CARD_VERIFICATION_CODE); + AddTextFormFieldData("verification", "Verification", + CREDIT_CARD_VERIFICATION_CODE); + + ClassifyAndVerifyWithMultipleParses(); } TEST_F(CreditCardFieldTest, ParseNonConsecutiveCvc) { - FormFieldData field; - field.form_control_type = "text"; + AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL); + AddTextFormFieldData("card_number", "Card Number", CREDIT_CARD_NUMBER); + AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH); + AddTextFormFieldData("ccyear", "Exp Year", CREDIT_CARD_EXP_4_DIGIT_YEAR); + AddTextFormFieldData("verification", "Verification", + CREDIT_CARD_VERIFICATION_CODE); + AddTextFormFieldData("unknown", "Unknown", UNKNOWN_TYPE); + + ClassifyAndVerifyWithMultipleParses(); +} + +TEST_F(CreditCardFieldTest, ParseCreditCardContextualNameNotCard) { + base::test::ScopedFeatureList enabled; + enabled.InitWithFeatures( + {features::kAutofillStrictContextualCardNameConditions}, {}); + + AddTextFormFieldData("accNum", "Account ID", UNKNOWN_TYPE); + AddTextFormFieldData("name", "Account Name", UNKNOWN_TYPE); + AddTextFormFieldData("toAcctNum", "Move to Account ID", UNKNOWN_TYPE); + + ClassifyAndVerify(ParseResult::NOT_PARSED); +} + +TEST_F(CreditCardFieldTest, ParseCreditCardContextualNameNotCardAcctMatch) { + base::test::ScopedFeatureList enabled; + enabled.InitWithFeatures( + {features::kAutofillStrictContextualCardNameConditions}, {}); + + // TODO(crbug.com/1167977): This should be not parseable, but waiting before + // changing kNameOnCardRe to use word boundaries. + AddTextFormFieldData("acctNum", "Account ID", CREDIT_CARD_NUMBER); + AddTextFormFieldData("acctName", "Account Name", CREDIT_CARD_NAME_FULL); + AddTextFormFieldData("toAcctNum", "Move to Account ID", CREDIT_CARD_NUMBER); + + ClassifyAndVerify(ParseResult::PARSED); +} + +TEST_F(CreditCardFieldTest, ParseCreditCardContextualNameWithExpiration) { + base::test::ScopedFeatureList enabled; + enabled.InitWithFeatures( + {features::kAutofillStrictContextualCardNameConditions}, {}); + + AddTextFormFieldData("acctNum", "Account ID", CREDIT_CARD_NUMBER); + AddTextFormFieldData("name", "Account Name", CREDIT_CARD_NAME_FULL); + AddTextFormFieldData("ccmonth", "Exp Month", CREDIT_CARD_EXP_MONTH); + AddTextFormFieldData("ccyear", "Exp Year", CREDIT_CARD_EXP_4_DIGIT_YEAR); + + ClassifyAndVerify(ParseResult::PARSED); +} + +TEST_F(CreditCardFieldTest, ParseCreditCardContextualNameWithVerification) { + base::test::ScopedFeatureList enabled; + enabled.InitWithFeatures( + {features::kAutofillStrictContextualCardNameConditions}, {}); + + AddTextFormFieldData("acctNum", "Account ID", CREDIT_CARD_NUMBER); + AddTextFormFieldData("name", "Account Name", CREDIT_CARD_NAME_FULL); + AddTextFormFieldData("cvv", "Verification", CREDIT_CARD_VERIFICATION_CODE); - field.label = ASCIIToUTF16("Name on Card"); - field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("name"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("number"))); - - field.label = ASCIIToUTF16("Exp Month"); - field.name = ASCIIToUTF16("ccmonth"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("month"))); - - field.label = ASCIIToUTF16("Exp Year"); - field.name = ASCIIToUTF16("ccyear"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("year"))); - - field.label = ASCIIToUTF16("Verification"); - field.name = ASCIIToUTF16("verification"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("cvc"))); - - field.label = ASCIIToUTF16("Unknown"); - field.name = ASCIIToUTF16("unknown"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("unknown"))); - - field.label = ASCIIToUTF16("Verification"); - field.name = ASCIIToUTF16("verification"); - list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("cvc2"))); - - MultipleParses(); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - field_candidates_map_[ASCIIToUTF16("name")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_MONTH, - field_candidates_map_[ASCIIToUTF16("month")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, - field_candidates_map_[ASCIIToUTF16("year")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("cvc")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE, - field_candidates_map_[ASCIIToUTF16("cvc")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("unknown")) == - field_candidates_map_.end()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("cvc2")) == - field_candidates_map_.end()); + ClassifyAndVerify(ParseResult::PARSED); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/email_field.cc b/chromium/components/autofill/core/browser/form_parsing/email_field.cc index 99d87e8fc17..655749affc6 100644 --- a/chromium/components/autofill/core/browser/form_parsing/email_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/email_field.cc @@ -12,13 +12,14 @@ namespace autofill { // static std::unique_ptr<FormField> EmailField::Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { AutofillField* field; - auto& patterns = PatternProvider::GetInstance().GetMatchPatterns( - "EMAIL_ADDRESS", page_language); + const std::vector<MatchingPattern>& email_patterns = + PatternProvider::GetInstance().GetMatchPatterns("EMAIL_ADDRESS", + page_language); if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kEmailRe), - MATCH_DEFAULT | MATCH_EMAIL, patterns, &field, + MATCH_DEFAULT | MATCH_EMAIL, email_patterns, &field, {log_manager, "kEmailRe"})) { return std::make_unique<EmailField>(field); } diff --git a/chromium/components/autofill/core/browser/form_parsing/email_field.h b/chromium/components/autofill/core/browser/form_parsing/email_field.h index 3765457f766..8d7169abd1d 100644 --- a/chromium/components/autofill/core/browser/form_parsing/email_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/email_field.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "components/autofill/core/browser/form_parsing/form_field.h" #include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/common/language_code.h" namespace autofill { @@ -19,7 +20,7 @@ class LogManager; class EmailField : public FormField { public: static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); explicit EmailField(const AutofillField* field); diff --git a/chromium/components/autofill/core/browser/form_parsing/field_candidates.h b/chromium/components/autofill/core/browser/form_parsing/field_candidates.h index f1ef7246213..4cf3c6d7204 100644 --- a/chromium/components/autofill/core/browser/form_parsing/field_candidates.h +++ b/chromium/components/autofill/core/browser/form_parsing/field_candidates.h @@ -5,10 +5,11 @@ #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_FIELD_CANDIDATES_H_ #define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_FIELD_CANDIDATES_H_ -#include <unordered_map> #include <vector> +#include "base/containers/flat_map.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/common/renderer_id.h" namespace autofill { @@ -50,7 +51,7 @@ class FieldCandidates { }; // A map from the field's unique name to its possible candidates. -using FieldCandidatesMap = std::unordered_map<base::string16, FieldCandidates>; +using FieldCandidatesMap = base::flat_map<FieldRendererId, FieldCandidates>; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/form_field.cc b/chromium/components/autofill/core/browser/form_parsing/form_field.cc index 4b5f80e3ca8..696b422b450 100644 --- a/chromium/components/autofill/core/browser/form_parsing/form_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/form_field.cc @@ -53,7 +53,7 @@ const float FormField::kBaseSearchParserScore = 0.8f; // static FieldCandidatesMap FormField::ParseFormFields( const std::vector<std::unique_ptr<AutofillField>>& fields, - const std::string& page_language, + const LanguageCode& page_language, bool is_form_tag, LogManager* log_manager) { // Set up a working copy of the fields to be processed. @@ -133,7 +133,7 @@ FieldCandidatesMap FormField::ParseFormFields( } for (const auto& candidate : field_candidates) { LogBuffer name; - name << "Type candidate for: " << candidate.first; + name << "Type candidate for renderer ID: " << candidate.first.value(); LogBuffer description; ServerFieldType field_type = candidate.second.BestHeuristicType(); description << "BestHeuristicType: " @@ -174,10 +174,9 @@ bool FormField::ParseField(AutofillScanner* scanner, AutofillField** match, const RegExLogging& logging) { if (base::FeatureList::IsEnabled( - features::kAutofillUsePageLanguageToSelectFieldParsingPatterns) || + features::kAutofillParsingPatternsLanguageDependent) || base::FeatureList::IsEnabled( - features:: - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics)) { + features::kAutofillParsingPatternsNegativeMatching)) { return ParseField(scanner, patterns, match, logging); } else { return ParseField(scanner, pattern, match, logging); @@ -221,18 +220,17 @@ bool FormField::ParseFieldSpecifics( // TODO(crbug.com/1132831): Remove feature check once launched. if (base::FeatureList::IsEnabled( - features:: - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics)) { - if (pattern.negative_pattern.has_value() && - FormField::Match(field, - base::UTF8ToUTF16(pattern.negative_pattern.value()), + features::kAutofillParsingPatternsNegativeMatching)) { + if (!pattern.negative_pattern.empty() && + FormField::Match(field, base::UTF8ToUTF16(pattern.negative_pattern), pattern.match_field_attributes, pattern.match_field_input_types, logging)) { continue; } } - if (MatchAndAdvance(scanner, base::UTF8ToUTF16(pattern.positive_pattern), + if (!pattern.positive_pattern.empty() && + MatchAndAdvance(scanner, base::UTF8ToUTF16(pattern.positive_pattern), pattern.match_field_attributes, pattern.match_field_input_types, match, logging)) { return true; @@ -263,22 +261,19 @@ bool FormField::ParseFieldSpecifics( const RegExLogging& logging, MatchFieldBitmasks match_field_bitmasks) { if (base::FeatureList::IsEnabled( - features::kAutofillUsePageLanguageToSelectFieldParsingPatterns) || + features::kAutofillParsingPatternsLanguageDependent) || base::FeatureList::IsEnabled( - features:: - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics)) { + features::kAutofillParsingPatternsNegativeMatching)) { // TODO(crbug/1142936): This hack is to allow // AddressField::ParseNameAndLabelSeparately(). if (match_field_bitmasks.restrict_attributes != ~0 || match_field_bitmasks.augment_types != 0) { - std::vector<MatchingPattern> patterns_with_restricted_match_type = - patterns; - for (MatchingPattern& mp : patterns_with_restricted_match_type) { + std::vector<MatchingPattern> modified_patterns = patterns; + for (MatchingPattern& mp : modified_patterns) { mp.match_field_attributes &= match_field_bitmasks.restrict_attributes; mp.match_field_input_types |= match_field_bitmasks.augment_types; } - return ParseFieldSpecifics(scanner, patterns_with_restricted_match_type, - match, logging); + return ParseFieldSpecifics(scanner, modified_patterns, match, logging); } return ParseFieldSpecifics(scanner, patterns, match, logging); } else { @@ -302,7 +297,7 @@ void FormField::AddClassification(const AutofillField* field, if (field == nullptr) return; - FieldCandidates& candidates = (*field_candidates)[field->unique_name()]; + FieldCandidates& candidates = (*field_candidates)[field->unique_renderer_id]; candidates.AddFieldCandidate(type, score); } @@ -347,16 +342,25 @@ bool FormField::Match(const AutofillField* field, base::StringPiece16 value; base::string16 match; + // TODO(crbug/1165780): Remove once shared labels are launched. + const base::string16& label = + base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForParsingWithSharedLabels) + ? field->parseable_label() + : field->label; + + const base::string16& name = field->parseable_name(); + if ((match_field_attributes & MATCH_LABEL) && - MatchesPattern(field->label, pattern, &match)) { + MatchesPattern(label, pattern, &match)) { found_match = true; match_type_string = "Match in label"; - value = field->label; + value = label; } else if ((match_field_attributes & MATCH_NAME) && - MatchesPattern(field->parseable_name(), pattern, &match)) { + MatchesPattern(name, pattern, &match)) { found_match = true; match_type_string = "Match in name"; - value = field->parseable_name(); + value = name; } if (found_match && logging.log_manager) { @@ -391,7 +395,7 @@ bool FormField::Match(const AutofillField* field, void FormField::ParseFormFieldsPass(ParseFunction parse, const std::vector<AutofillField*>& fields, FieldCandidatesMap* field_candidates, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { AutofillScanner scanner(fields); while (!scanner.IsEnd()) { diff --git a/chromium/components/autofill/core/browser/form_parsing/form_field.h b/chromium/components/autofill/core/browser/form_parsing/form_field.h index 7eca61ab200..904c373e517 100644 --- a/chromium/components/autofill/core/browser/form_parsing/form_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/form_field.h @@ -14,6 +14,7 @@ #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h" #include "components/autofill/core/browser/form_parsing/field_candidates.h" +#include "components/autofill/core/common/language_code.h" namespace autofill { @@ -35,17 +36,25 @@ struct RegExLogging { // name, phone number, or address field. class FormField { public: - virtual ~FormField() {} + virtual ~FormField() = default; // Classifies each field in |fields| with its heuristically detected type. // Each field has a derived unique name that is used as the key into the // returned FieldCandidatesMap. static FieldCandidatesMap ParseFormFields( const std::vector<std::unique_ptr<AutofillField>>& fields, - const std::string& page_language, + const LanguageCode& page_language, bool is_form_tag, LogManager* log_manager = nullptr); +#if defined(UNIT_TEST) + // Assign types to the fields for the testing purposes. + void AddClassificationsForTesting( + FieldCandidatesMap* field_candidates_for_testing) const { + AddClassifications(field_candidates_for_testing); + } +#endif + protected: // Initial values assigned to FieldCandidates by their corresponding parsers. static const float kBaseEmailParserScore; @@ -102,6 +111,7 @@ class FormField { int match_field_input_types, AutofillField** match, const RegExLogging& logging = {}); + struct MatchFieldBitmasks { int restrict_attributes = ~0; int augment_types = 0; @@ -141,12 +151,13 @@ class FormField { private: FRIEND_TEST_ALL_PREFIXES(FormFieldTest, Match); + FRIEND_TEST_ALL_PREFIXES(FormFieldTest, TestParseableLabels); // Function pointer type for the parsing function that should be passed to the // ParseFormFieldsPass() helper function. typedef std::unique_ptr<FormField> ParseFunction( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); // Matches |pattern| to the contents of the field at the head of the @@ -192,7 +203,7 @@ class FormField { static void ParseFormFieldsPass(ParseFunction parse, const std::vector<AutofillField*>& fields, FieldCandidatesMap* field_candidates, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager = nullptr); DISALLOW_COPY_AND_ASSIGN(FormField); diff --git a/chromium/components/autofill/core/browser/form_parsing/form_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/form_field_unittest.cc index ed5baa21072..1d41fdeea2d 100644 --- a/chromium/components/autofill/core/browser/form_parsing/form_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/form_field_unittest.cc @@ -10,7 +10,7 @@ #include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/form_parsing/form_field.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" +#include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/common/autofill_features.h" #include "testing/gtest/include/gtest/gtest.h" @@ -19,6 +19,20 @@ using base::ASCIIToUTF16; namespace autofill { +namespace { +FieldRendererId MakeFieldRendererId() { + static uint64_t id_counter_ = 0; + return FieldRendererId(++id_counter_); +} + +// Sets both the field label and parseable label to |label|. +void SetFieldLabels(AutofillField* field, const std::string& label) { + field->label = base::UTF8ToUTF16(label); + field->set_parseable_label(base::UTF8ToUTF16(label)); +} + +} // namespace + TEST(FormFieldTest, Match) { AutofillField field; @@ -26,69 +40,69 @@ TEST(FormFieldTest, Match) { EXPECT_TRUE(FormField::Match(&field, base::string16(), MATCH_LABEL)); // Empty pattern matches non-empty string. - field.label = ASCIIToUTF16("a"); + SetFieldLabels(&field, "a"); EXPECT_TRUE(FormField::Match(&field, base::string16(), MATCH_LABEL)); // Strictly empty pattern matches empty string. - field.label = base::string16(); + SetFieldLabels(&field, ""); EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^$"), MATCH_LABEL)); // Strictly empty pattern does not match non-empty string. - field.label = ASCIIToUTF16("a"); + SetFieldLabels(&field, "a"); EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^$"), MATCH_LABEL)); // Non-empty pattern doesn't match empty string. - field.label = base::string16(); + SetFieldLabels(&field, ""); EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("a"), MATCH_LABEL)); // Beginning of line. - field.label = ASCIIToUTF16("head_tail"); + SetFieldLabels(&field, "head_tail"); EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^head"), MATCH_LABEL)); EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^tail"), MATCH_LABEL)); // End of line. - field.label = ASCIIToUTF16("head_tail"); + SetFieldLabels(&field, "head_tail"); EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("head$"), MATCH_LABEL)); EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("tail$"), MATCH_LABEL)); // Exact. - field.label = ASCIIToUTF16("head_tail"); + SetFieldLabels(&field, "head_tail"); EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^head$"), MATCH_LABEL)); EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^tail$"), MATCH_LABEL)); EXPECT_TRUE( FormField::Match(&field, ASCIIToUTF16("^head_tail$"), MATCH_LABEL)); // Escaped dots. - field.label = ASCIIToUTF16("m.i."); + SetFieldLabels(&field, "m.i."); // Note: This pattern is misleading as the "." characters are wild cards. EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m.i."), MATCH_LABEL)); EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m\\.i\\."), MATCH_LABEL)); - field.label = ASCIIToUTF16("mXiX"); + SetFieldLabels(&field, "mXiX"); EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m.i."), MATCH_LABEL)); EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("m\\.i\\."), MATCH_LABEL)); // Repetition. - field.label = ASCIIToUTF16("headtail"); + SetFieldLabels(&field, "headtail"); EXPECT_TRUE( FormField::Match(&field, ASCIIToUTF16("head.*tail"), MATCH_LABEL)); - field.label = ASCIIToUTF16("headXtail"); + SetFieldLabels(&field, "headXtail"); EXPECT_TRUE( FormField::Match(&field, ASCIIToUTF16("head.*tail"), MATCH_LABEL)); - field.label = ASCIIToUTF16("headXXXtail"); + SetFieldLabels(&field, "headXXXtail"); EXPECT_TRUE( FormField::Match(&field, ASCIIToUTF16("head.*tail"), MATCH_LABEL)); - field.label = ASCIIToUTF16("headtail"); + SetFieldLabels(&field, "headtail"); EXPECT_FALSE( FormField::Match(&field, ASCIIToUTF16("head.+tail"), MATCH_LABEL)); - field.label = ASCIIToUTF16("headXtail"); + SetFieldLabels(&field, "headXtail"); EXPECT_TRUE( FormField::Match(&field, ASCIIToUTF16("head.+tail"), MATCH_LABEL)); - field.label = ASCIIToUTF16("headXXXtail"); + SetFieldLabels(&field, "headXXXtail"); EXPECT_TRUE( FormField::Match(&field, ASCIIToUTF16("head.+tail"), MATCH_LABEL)); // Alternation. - field.label = ASCIIToUTF16("head_tail"); + SetFieldLabels(&field, "head_tail"); EXPECT_TRUE( FormField::Match(&field, ASCIIToUTF16("head|other"), MATCH_LABEL)); EXPECT_TRUE( @@ -96,11 +110,11 @@ TEST(FormFieldTest, Match) { EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("bad|good"), MATCH_LABEL)); // Case sensitivity. - field.label = ASCIIToUTF16("xxxHeAd_tAiLxxx"); + SetFieldLabels(&field, "xxxHeAd_tAiLxxx"); EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head_tail"), MATCH_LABEL)); // Word boundaries. - field.label = ASCIIToUTF16("contains word:"); + SetFieldLabels(&field, "contains word:"); EXPECT_TRUE( FormField::Match(&field, ASCIIToUTF16("\\bword\\b"), MATCH_LABEL)); EXPECT_FALSE( @@ -112,74 +126,66 @@ TEST(FormFieldTest, Match) { // Test that we ignore checkable elements. TEST(FormFieldTest, ParseFormFields) { - TestPatternProvider test_pattern_provider_; - std::vector<std::unique_ptr<AutofillField>> fields; FormFieldData field_data; field_data.form_control_type = "text"; field_data.check_status = FormFieldData::CheckStatus::kCheckableButUnchecked; field_data.label = ASCIIToUTF16("Is PO Box"); - fields.push_back( - std::make_unique<AutofillField>(field_data, field_data.label)); + field_data.unique_renderer_id = MakeFieldRendererId(); + fields.push_back(std::make_unique<AutofillField>(field_data)); // Does not parse since there are only field and it's checkable. // An empty page_language means the language is unknown and patterns of all // languages are used. EXPECT_TRUE( - FormField::ParseFormFields(fields, /*page_language=*/"", true).empty()); + FormField::ParseFormFields(fields, LanguageCode(""), true).empty()); // reset |is_checkable| to false. field_data.check_status = FormFieldData::CheckStatus::kNotCheckable; - field_data.label = ASCIIToUTF16("Address line1"); - fields.push_back( - std::make_unique<AutofillField>(field_data, field_data.label)); + field_data.unique_renderer_id = MakeFieldRendererId(); + fields.push_back(std::make_unique<AutofillField>(field_data)); // Parse a single address line 1 field. - ASSERT_EQ( - 0u, - FormField::ParseFormFields(fields, /*page_language=*/"", true).size()); + ASSERT_EQ(0u, + FormField::ParseFormFields(fields, LanguageCode(""), true).size()); // Parses address line 1 and 2. field_data.label = ASCIIToUTF16("Address line2"); - fields.push_back( - std::make_unique<AutofillField>(field_data, field_data.label)); + field_data.unique_renderer_id = MakeFieldRendererId(); + fields.push_back(std::make_unique<AutofillField>(field_data)); // An empty page_language means the language is unknown and patterns of // all languages are used. - ASSERT_EQ( - 0u, - FormField::ParseFormFields(fields, /*page_language=*/"", true).size()); + ASSERT_EQ(0u, + FormField::ParseFormFields(fields, LanguageCode(""), true).size()); } // Test that the minimum number of required fields for the heuristics considers // whether a field is actually fillable. TEST(FormFieldTest, ParseFormFieldEnforceMinFillableFields) { - TestPatternProvider test_pattern_provider_; - std::vector<std::unique_ptr<AutofillField>> fields; FormFieldData field_data; field_data.form_control_type = "text"; field_data.label = ASCIIToUTF16("Address line 1"); - fields.push_back( - std::make_unique<AutofillField>(field_data, field_data.label)); + field_data.unique_renderer_id = MakeFieldRendererId(); + fields.push_back(std::make_unique<AutofillField>(field_data)); field_data.label = ASCIIToUTF16("Address line 2"); - fields.push_back( - std::make_unique<AutofillField>(field_data, field_data.label)); + field_data.unique_renderer_id = MakeFieldRendererId(); + fields.push_back(std::make_unique<AutofillField>(field_data)); // Don't parse forms with 2 fields. // An empty page_language means the language is unknown and patterns of all // languages are used. - EXPECT_EQ( - 0u, - FormField::ParseFormFields(fields, /*page_language=*/"", true).size()); + EXPECT_EQ(0u, + FormField::ParseFormFields(fields, LanguageCode(""), true).size()); field_data.label = ASCIIToUTF16("Search"); - fields.push_back( - std::make_unique<AutofillField>(field_data, field_data.label)); + field_data.unique_renderer_id = MakeFieldRendererId(); + fields.push_back(std::make_unique<AutofillField>(field_data)); // Before the fix in kAutofillFixFillableFieldTypes, we would parse the form // now, although a search field is not fillable. @@ -189,8 +195,7 @@ TEST(FormFieldTest, ParseFormFieldEnforceMinFillableFields) { // An empty page_language means the language is unknown and patterns of all // languages are used. EXPECT_EQ( - 3u, - FormField::ParseFormFields(fields, /*page_language=*/"", true).size()); + 3u, FormField::ParseFormFields(fields, LanguageCode(""), true).size()); } // With the fix, we don't parse the form because search fields are not @@ -201,11 +206,34 @@ TEST(FormFieldTest, ParseFormFieldEnforceMinFillableFields) { // An empty page_language means the language is unknown and patterns of all // languages are used. const FieldCandidatesMap field_candidates_map = - FormField::ParseFormFields(fields, /*page_language=*/"", true); + FormField::ParseFormFields(fields, LanguageCode(""), true); EXPECT_EQ( - 0u, - FormField::ParseFormFields(fields, /*page_language=*/"", true).size()); + 0u, FormField::ParseFormFields(fields, LanguageCode(""), true).size()); } } +// Test that the parseable label is used when the feature is enabled. +TEST(FormFieldTest, TestParseableLabels) { + FormFieldData field_data; + field_data.form_control_type = "text"; + + field_data.label = ASCIIToUTF16("not a parseable label"); + field_data.unique_renderer_id = MakeFieldRendererId(); + auto autofill_field = std::make_unique<AutofillField>(field_data); + autofill_field->set_parseable_label(ASCIIToUTF16("First Name")); + { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + features::kAutofillEnableSupportForParsingWithSharedLabels); + EXPECT_TRUE(FormField::Match(autofill_field.get(), + ASCIIToUTF16("First Name"), MATCH_LABEL)); + } + { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndDisableFeature( + features::kAutofillEnableSupportForParsingWithSharedLabels); + EXPECT_FALSE(FormField::Match(autofill_field.get(), + ASCIIToUTF16("First Name"), MATCH_LABEL)); + } +} } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/name_field.cc b/chromium/components/autofill/core/browser/form_parsing/name_field.cc index 0fd65aa87c9..1e3bf74d083 100644 --- a/chromium/components/autofill/core/browser/form_parsing/name_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/name_field.cc @@ -24,7 +24,7 @@ namespace { class FullNameField : public NameField { public: static std::unique_ptr<FullNameField> Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); explicit FullNameField(AutofillField* field); @@ -43,11 +43,11 @@ class FirstTwoLastNamesField : public NameField { public: static std::unique_ptr<FirstTwoLastNamesField> ParseComponentNames( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); static std::unique_ptr<FirstTwoLastNamesField> Parse( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); protected: @@ -71,15 +71,15 @@ class FirstLastNameField : public NameField { public: static std::unique_ptr<FirstLastNameField> ParseSpecificName( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); static std::unique_ptr<FirstLastNameField> ParseComponentNames( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); static std::unique_ptr<FirstLastNameField> Parse( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); protected: @@ -101,7 +101,7 @@ class FirstLastNameField : public NameField { // static std::unique_ptr<FormField> NameField::Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { if (scanner->IsEnd()) return nullptr; @@ -126,15 +126,22 @@ void NameField::AddClassifications(FieldCandidatesMap* field_candidates) const { // static std::unique_ptr<FullNameField> FullNameField::Parse( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { // Exclude e.g. "username" or "nickname" fields. scanner->SaveCursor(); - auto& patterns_ni = PatternProvider::GetInstance().GetMatchPatterns( - "NAME_IGNORED", page_language); + const std::vector<MatchingPattern>& name_ignored_patterns = + PatternProvider::GetInstance().GetMatchPatterns("NAME_IGNORED", + page_language); + const std::vector<MatchingPattern>& address_name_ignored_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_NAME_IGNORED", + page_language); bool should_ignore = - ParseField(scanner, UTF8ToUTF16(kNameIgnoredRe), patterns_ni, nullptr, - {log_manager, "kNameIgnoredRe"}); + ParseField(scanner, UTF8ToUTF16(kNameIgnoredRe), name_ignored_patterns, + nullptr, {log_manager, "kNameIgnoredRe"}) || + ParseField(scanner, UTF8ToUTF16(kAddressNameIgnoredRe), + address_name_ignored_patterns, nullptr, + {log_manager, "kAddressNameIgnoredRe"}); scanner->Rewind(); if (should_ignore) return nullptr; @@ -143,10 +150,11 @@ std::unique_ptr<FullNameField> FullNameField::Parse( // for example, Travelocity_Edit travel profile.html contains a field // "Travel Profile Name". AutofillField* field = nullptr; - // In JSON : FULL_NAME (closest vatiant) - auto& patterns_name = PatternProvider::GetInstance().GetMatchPatterns( - "FULL_NAME", page_language); - if (ParseField(scanner, UTF8ToUTF16(kNameRe), patterns_name, &field, + + const std::vector<MatchingPattern>& name_patterns = + PatternProvider::GetInstance().GetMatchPatterns("FULL_NAME", + page_language); + if (ParseField(scanner, UTF8ToUTF16(kNameRe), name_patterns, &field, {log_manager, "kNameRe"})) return std::make_unique<FullNameField>(field); @@ -165,7 +173,7 @@ FirstTwoLastNamesField::FirstTwoLastNamesField() = default; // static std::unique_ptr<FirstTwoLastNamesField> FirstTwoLastNamesField::Parse( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { return ParseComponentNames(scanner, page_language, log_manager); } @@ -173,34 +181,51 @@ std::unique_ptr<FirstTwoLastNamesField> FirstTwoLastNamesField::Parse( // static std::unique_ptr<FirstTwoLastNamesField> FirstTwoLastNamesField::ParseComponentNames(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { std::unique_ptr<FirstTwoLastNamesField> v(new FirstTwoLastNamesField); scanner->SaveCursor(); - auto& patterns_hp = PatternProvider::GetInstance().GetMatchPatterns( - "HONORIFIC_PREFIX", page_language); - auto& patterns_ni = PatternProvider::GetInstance().GetMatchPatterns( - "NAME_IGNORED", page_language); - auto& patterns_fn = PatternProvider::GetInstance().GetMatchPatterns( - "FIRST_NAME", page_language); - auto& patterns_mn = PatternProvider::GetInstance().GetMatchPatterns( - "MIDDLE_NAME", page_language); - auto& patterns_ln1 = PatternProvider::GetInstance().GetMatchPatterns( - "LAST_NAME_FIRST", page_language); - auto& patterns_ln2 = PatternProvider::GetInstance().GetMatchPatterns( - "LAST_NAME_SECOND", page_language); + const std::vector<MatchingPattern>& honorific_prefix_patterns = + PatternProvider::GetInstance().GetMatchPatterns("HONORIFIC_PREFIX", + page_language); + const std::vector<MatchingPattern>& name_ignored_patterns = + PatternProvider::GetInstance().GetMatchPatterns("NAME_IGNORED", + page_language); + const std::vector<MatchingPattern>& address_name_ignored_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_NAME_IGNORED", + page_language); + const std::vector<MatchingPattern>& first_name_patterns = + PatternProvider::GetInstance().GetMatchPatterns("FIRST_NAME", + page_language); + const std::vector<MatchingPattern>& middle_name_patterns = + PatternProvider::GetInstance().GetMatchPatterns("MIDDLE_NAME", + page_language); + const std::vector<MatchingPattern>& first_last_name_patterns = + PatternProvider::GetInstance().GetMatchPatterns("LAST_NAME_FIRST", + page_language); + const std::vector<MatchingPattern>& second_last_name_patterns = + PatternProvider::GetInstance().GetMatchPatterns("LAST_NAME_SECOND", + page_language); // Allow name fields to appear in any order. while (!scanner->IsEnd()) { + // Skip over address label fields, which can have misleading names + // e.g. "title" or "name". + if (ParseFieldSpecifics(scanner, UTF8ToUTF16(kAddressNameIgnoredRe), + MATCH_DEFAULT, address_name_ignored_patterns, + nullptr, {log_manager, "kAddressNameIgnoredRe"})) { + continue; + } + // Scan for the honorific prefix before checking for unrelated name fields // because a honorific prefix field is expected to have very specific labels // including "Title:". The latter is matched with |kNameIgnoredRe|. // TODO(crbug.com/1098943): Remove check once feature is launched or // removed. if (!v->honorific_prefix_ && - ParseField(scanner, UTF8ToUTF16(kHonorificPrefixRe), patterns_hp, - &v->honorific_prefix_, + ParseField(scanner, UTF8ToUTF16(kHonorificPrefixRe), + honorific_prefix_patterns, &v->honorific_prefix_, {log_manager, "kHonorificPrefixRe"})) { continue; } @@ -208,32 +233,33 @@ FirstTwoLastNamesField::ParseComponentNames(AutofillScanner* scanner, // Skip over any unrelated fields, e.g. "username" or "nickname". if (ParseFieldSpecifics(scanner, UTF8ToUTF16(kNameIgnoredRe), MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH, - patterns_ni, nullptr, + name_ignored_patterns, nullptr, {log_manager, "kNameIgnoredRe"})) { continue; } if (!v->first_name_ && - ParseField(scanner, UTF8ToUTF16(kFirstNameRe), patterns_fn, + ParseField(scanner, UTF8ToUTF16(kFirstNameRe), first_name_patterns, &v->first_name_, {log_manager, "kFirstNameRe"})) { continue; } if (!v->middle_name_ && - ParseField(scanner, UTF8ToUTF16(kMiddleNameRe), patterns_mn, + ParseField(scanner, UTF8ToUTF16(kMiddleNameRe), middle_name_patterns, &v->middle_name_, {log_manager, "kMiddleNameRe"})) { continue; } if (!v->first_last_name_ && - ParseField(scanner, UTF8ToUTF16(kNameLastFirstRe), patterns_ln1, - &v->first_last_name_, {log_manager, "kNameLastFirstRe"})) { + ParseField(scanner, UTF8ToUTF16(kNameLastFirstRe), + first_last_name_patterns, &v->first_last_name_, + {log_manager, "kNameLastFirstRe"})) { continue; } if (!v->second_last_name_ && - ParseField(scanner, UTF8ToUTF16(kNameLastSecondRe), patterns_ln2, - &v->second_last_name_, + ParseField(scanner, UTF8ToUTF16(kNameLastSecondRe), + second_last_name_patterns, &v->second_last_name_, {log_manager, "kNameLastSecondtRe"})) { continue; } @@ -267,7 +293,7 @@ void FirstTwoLastNamesField::AddClassifications( std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseSpecificName( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { // Some pages (e.g. Overstock_comBilling.html, SmithsonianCheckout.html) // have the label "Name" followed by two or three text fields. @@ -275,10 +301,11 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseSpecificName( scanner->SaveCursor(); AutofillField* next = nullptr; - auto& patterns_ns = PatternProvider::GetInstance().GetMatchPatterns( - "NAME_SPECIFIC", page_language); + const std::vector<MatchingPattern>& name_specific_patterns = + PatternProvider::GetInstance().GetMatchPatterns("NAME_SPECIFIC", + page_language); - if (ParseField(scanner, UTF8ToUTF16(kNameSpecificRe), patterns_ns, + if (ParseField(scanner, UTF8ToUTF16(kNameSpecificRe), name_specific_patterns, &v->first_name_, {log_manager, "kNameSpecificRe"}) && ParseEmptyLabel(scanner, &next)) { if (ParseEmptyLabel(scanner, &v->last_name_)) { @@ -300,7 +327,7 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseSpecificName( // static std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { std::unique_ptr<FirstLastNameField> v(new FirstLastNameField); scanner->SaveCursor(); @@ -317,20 +344,37 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames( // Allow name fields to appear in any order. - auto& patterns_hp = PatternProvider::GetInstance().GetMatchPatterns( - "HONORIFIC_PREFIX", page_language); - auto& patterns_ni = PatternProvider::GetInstance().GetMatchPatterns( - "NAME_IGNORED", page_language); - auto& patterns_fn = PatternProvider::GetInstance().GetMatchPatterns( - "FIRST_NAME", page_language); - auto& patterns_mi = PatternProvider::GetInstance().GetMatchPatterns( - "MIDDLE_INITIAL", page_language); - auto& patterns_mn = PatternProvider::GetInstance().GetMatchPatterns( - "MIDDLE_NAME", page_language); - auto& patterns_ln = PatternProvider::GetInstance().GetMatchPatterns( - "LAST_NAME", page_language); + const std::vector<MatchingPattern>& honorific_prefix_patterns = + PatternProvider::GetInstance().GetMatchPatterns("HONORIFIC_PREFIX", + page_language); + const std::vector<MatchingPattern>& name_ignored_patterns = + PatternProvider::GetInstance().GetMatchPatterns("NAME_IGNORED", + page_language); + const std::vector<MatchingPattern>& address_name_ignored_patterns = + PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_NAME_IGNORED", + page_language); + const std::vector<MatchingPattern>& first_name_patterns = + PatternProvider::GetInstance().GetMatchPatterns("FIRST_NAME", + page_language); + const std::vector<MatchingPattern>& middle_name_initial_patterns = + PatternProvider::GetInstance().GetMatchPatterns("MIDDLE_INITIAL", + page_language); + const std::vector<MatchingPattern>& middle_name_patterns = + PatternProvider::GetInstance().GetMatchPatterns("MIDDLE_NAME", + page_language); + const std::vector<MatchingPattern>& last_name_patterns = + PatternProvider::GetInstance().GetMatchPatterns("LAST_NAME", + page_language); while (!scanner->IsEnd()) { + // Skip over address label fields, which can have misleading names + // e.g. "title" or "name". + if (ParseFieldSpecifics(scanner, UTF8ToUTF16(kAddressNameIgnoredRe), + MATCH_DEFAULT, address_name_ignored_patterns, + nullptr, {log_manager, "kAddressNameIgnoredRe"})) { + continue; + } + // Scan for the honorific prefix before checking for unrelated fields // because a honorific prefix field is expected to have very specific labels // including "Title:". The latter is matched with |kNameIgnoredRe|. @@ -339,8 +383,8 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames( if (base::FeatureList::IsEnabled( features::kAutofillEnableSupportForMoreStructureInNames)) { if (!v->honorific_prefix_ && - ParseField(scanner, UTF8ToUTF16(kHonorificPrefixRe), patterns_hp, - &v->honorific_prefix_, + ParseField(scanner, UTF8ToUTF16(kHonorificPrefixRe), + honorific_prefix_patterns, &v->honorific_prefix_, {log_manager, "kHonorificPrefixRe"})) { continue; } @@ -349,13 +393,13 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames( // Skip over any unrelated name fields, e.g. "username" or "nickname". if (ParseFieldSpecifics(scanner, UTF8ToUTF16(kNameIgnoredRe), MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH, - patterns_ni, nullptr, + name_ignored_patterns, nullptr, {log_manager, "kNameIgnoredRe"})) { continue; } if (!v->first_name_ && - ParseField(scanner, UTF8ToUTF16(kFirstNameRe), patterns_fn, + ParseField(scanner, UTF8ToUTF16(kFirstNameRe), first_name_patterns, &v->first_name_, {log_manager, "kFirstNameRe"})) { continue; } @@ -366,20 +410,21 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames( // "txtmiddlename"); such a field probably actually represents a // middle initial. if (!v->middle_name_ && - ParseField(scanner, UTF8ToUTF16(kMiddleInitialRe), patterns_mi, - &v->middle_name_, {log_manager, "kMiddleInitialRe"})) { + ParseField(scanner, UTF8ToUTF16(kMiddleInitialRe), + middle_name_initial_patterns, &v->middle_name_, + {log_manager, "kMiddleInitialRe"})) { v->middle_initial_ = true; continue; } if (!v->middle_name_ && - ParseField(scanner, UTF8ToUTF16(kMiddleNameRe), patterns_mn, + ParseField(scanner, UTF8ToUTF16(kMiddleNameRe), middle_name_patterns, &v->middle_name_, {log_manager, "kMiddleNameRe"})) { continue; } if (!v->last_name_ && - ParseField(scanner, UTF8ToUTF16(kLastNameRe), patterns_ln, + ParseField(scanner, UTF8ToUTF16(kLastNameRe), last_name_patterns, &v->last_name_, {log_manager, "kLastNameRe"})) { continue; } @@ -399,7 +444,7 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames( // static std::unique_ptr<FirstLastNameField> FirstLastNameField::Parse( AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { std::unique_ptr<FirstLastNameField> field = ParseSpecificName(scanner, page_language, log_manager); diff --git a/chromium/components/autofill/core/browser/form_parsing/name_field.h b/chromium/components/autofill/core/browser/form_parsing/name_field.h index 7298fbdb7d5..99c011e70f8 100644 --- a/chromium/components/autofill/core/browser/form_parsing/name_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/name_field.h @@ -14,6 +14,7 @@ #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/form_parsing/form_field.h" #include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/common/language_code.h" namespace autofill { @@ -24,19 +25,11 @@ class LogManager; class NameField : public FormField { public: static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); -#ifdef UNIT_TEST - // Calls the protected method |AddClassification| for testing. - void AddClassificationsForTesting( - FieldCandidatesMap* field_candidates) const { - AddClassifications(field_candidates); - } -#endif - protected: - NameField() {} + NameField() = default; void AddClassifications(FieldCandidatesMap* field_candidates) const override; diff --git a/chromium/components/autofill/core/browser/form_parsing/name_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/name_field_unittest.cc index e83e8187b8c..af7fdf56732 100644 --- a/chromium/components/autofill/core/browser/form_parsing/name_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/name_field_unittest.cc @@ -7,122 +7,49 @@ #include <memory> #include <vector> -#include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" -#include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/autofill_regexes.h" -#include "components/autofill/core/browser/form_parsing/autofill_scanner.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" +#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/form_field_data.h" -#include "testing/gtest/include/gtest/gtest.h" using base::ASCIIToUTF16; namespace autofill { -class NameFieldTest : public testing::Test { +class NameFieldTest : public FormFieldTest { public: NameFieldTest() = default; NameFieldTest(const NameFieldTest&) = delete; NameFieldTest& operator=(const NameFieldTest&) = delete; protected: - // Downcast for tests. - static std::unique_ptr<NameField> Parse(AutofillScanner* scanner) { - // An empty page_language means the language is unknown and patterns of all - // languages are used. - std::unique_ptr<FormField> field = - NameField::Parse(scanner, /*page_language=*/"", nullptr); - return std::unique_ptr<NameField>(static_cast<NameField*>(field.release())); + std::unique_ptr<FormField> Parse( + AutofillScanner* scanner, + const LanguageCode& page_language = LanguageCode("us")) override { + return NameField::Parse(scanner, page_language, nullptr); } - - std::vector<std::unique_ptr<AutofillField>> list_; - std::unique_ptr<NameField> field_; - FieldCandidatesMap field_candidates_map_; - - // RAII object to mock the the PatternProvider. - TestPatternProvider test_pattern_provider_; }; TEST_F(NameFieldTest, FirstMiddleLast) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("First Name"); - field.name = ASCIIToUTF16("First"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("Middle Name"); - field.name = ASCIIToUTF16("Middle"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - field.label = ASCIIToUTF16("Last Name"); - field.name = ASCIIToUTF16("Last"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name3"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_MIDDLE, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name3")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name3")].BestHeuristicType()); + AddTextFormFieldData("First Name", "First", NAME_FIRST); + AddTextFormFieldData("Name Middle", "Middle", NAME_MIDDLE); + AddTextFormFieldData("Last Name", "Last", NAME_LAST); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(NameFieldTest, FirstMiddleLast2) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = base::string16(); - field.name = ASCIIToUTF16("firstName"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("middleName"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("lastName"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name3"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_MIDDLE, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name3")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name3")].BestHeuristicType()); + AddTextFormFieldData("firstName", "", NAME_FIRST); + AddTextFormFieldData("middleName", "", NAME_MIDDLE); + AddTextFormFieldData("lastName", "", NAME_LAST); + + ClassifyAndVerify(ParseResult::PARSED); } -// Test that a field for a honoric title is parsed correctly. +// Test that a field for a honorific title is parsed correctly. TEST_F(NameFieldTest, HonorificPrefixFirstLast) { // With support for two last names, the parsing should find the first name // field and the two last name fields. @@ -131,257 +58,63 @@ TEST_F(NameFieldTest, HonorificPrefixFirstLast) { scoped_feature_list.InitAndEnableFeature( features::kAutofillEnableSupportForMoreStructureInNames); - FormFieldData field; - field.form_control_type = "text"; - - field.label = base::string16(); - field.name = ASCIIToUTF16("salutation"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name0"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("first_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("last_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name0")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_HONORIFIC_PREFIX, - field_candidates_map_[ASCIIToUTF16("name0")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); + AddTextFormFieldData("salutation", "", NAME_HONORIFIC_PREFIX); + AddTextFormFieldData("first_name", "", NAME_FIRST); + AddTextFormFieldData("last_name", "", NAME_LAST); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(NameFieldTest, FirstLast) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = base::string16(); - field.name = ASCIIToUTF16("first_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("last_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); + AddTextFormFieldData("first_name", "", NAME_FIRST); + AddTextFormFieldData("last_name", "", NAME_LAST); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(NameFieldTest, FirstLast2) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Name"); - field.name = ASCIIToUTF16("first_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("Name"); - field.name = ASCIIToUTF16("last_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); + AddTextFormFieldData("first_name", "Name", NAME_FIRST); + AddTextFormFieldData("last_name", "Name", NAME_LAST); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(NameFieldTest, FirstLastMiddleWithSpaces) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("First Name"); - field.name = ASCIIToUTF16("first_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("Middle Name"); - field.name = ASCIIToUTF16("middle_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - field.label = ASCIIToUTF16("Last Name"); - field.name = ASCIIToUTF16("last_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name3"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_MIDDLE, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name3")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name3")].BestHeuristicType()); + AddTextFormFieldData("fist_name", "First Name", NAME_FIRST); + AddTextFormFieldData("middle_name", "Middle Name", NAME_MIDDLE); + AddTextFormFieldData("last_name", "Last Name", NAME_LAST); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(NameFieldTest, FirstLastEmpty) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Name"); - field.name = ASCIIToUTF16("first_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("last_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); + AddTextFormFieldData("first_name", "Name", NAME_FIRST); + AddTextFormFieldData("last_name", "", NAME_LAST); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(NameFieldTest, FirstMiddleLastEmpty) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Name"); - field.name = ASCIIToUTF16("first_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("middle_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("last_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name3"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_MIDDLE_INITIAL, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name3")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name3")].BestHeuristicType()); + AddTextFormFieldData("first_name", "Name", NAME_FIRST); + AddTextFormFieldData("middle_name", "", NAME_MIDDLE_INITIAL); + AddTextFormFieldData("last_name", "", NAME_LAST); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(NameFieldTest, MiddleInitial) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("First Name"); - field.name = ASCIIToUTF16("first_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("MI"); - field.name = ASCIIToUTF16("middle_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - field.label = ASCIIToUTF16("Last Name"); - field.name = ASCIIToUTF16("last_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name3"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_MIDDLE_INITIAL, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name3")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name3")].BestHeuristicType()); + AddTextFormFieldData("first_name", "Name", NAME_FIRST); + AddTextFormFieldData("middle_name", "MI", NAME_MIDDLE_INITIAL); + AddTextFormFieldData("last_name", "", NAME_LAST); + + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(NameFieldTest, MiddleInitialNoLastName) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("First Name"); - field.name = ASCIIToUTF16("first_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("MI"); - field.name = ASCIIToUTF16("middle_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_EQ(nullptr, field_.get()); + AddTextFormFieldData("first_name", "First Name", UNKNOWN_TYPE); + AddTextFormFieldData("middle_name", "MI", UNKNOWN_TYPE); + + ClassifyAndVerify(ParseResult::NOT_PARSED); } // Tests that a website with a first and second surname field is parsed @@ -394,53 +127,13 @@ TEST_F(NameFieldTest, HonorificPrefixAndFirstNameAndHispanicLastNames) { scoped_feature_list.InitAndEnableFeature( features::kAutofillEnableSupportForMoreStructureInNames); - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("tratamiento"); - field.name = ASCIIToUTF16("tratamiento"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name3"))); - - field.label = ASCIIToUTF16("nombre"); - field.name = ASCIIToUTF16("nombre"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name0"))); - - field.label = ASCIIToUTF16("apellido paterno"); - field.name = ASCIIToUTF16("apellido_paterno"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("segunda apellido"); - field.name = ASCIIToUTF16("segunda_apellido"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - AutofillScanner scanner(list_); - - field_ = Parse(&scanner); - field_->AddClassificationsForTesting(&field_candidates_map_); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name0")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name0")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST_SECOND, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name3")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_HONORIFIC_PREFIX, - field_candidates_map_[ASCIIToUTF16("name3")].BestHeuristicType()); + AddTextFormFieldData("tratamiento", "tratamiento", NAME_HONORIFIC_PREFIX); + AddTextFormFieldData("nombre", "nombre", NAME_FIRST); + AddTextFormFieldData("apellido paterno", "apellido_paterno", NAME_LAST_FIRST); + AddTextFormFieldData("segunda apellido", "segunda_apellido", + NAME_LAST_SECOND); + + ClassifyAndVerify(ParseResult::PARSED); } // Tests that a website with a first and second surname field is parsed @@ -452,89 +145,23 @@ TEST_F(NameFieldTest, FirstNameAndOptionalMiddleNameAndHispanicLastNames) { scoped_feature_list.InitAndEnableFeature( features::kAutofillEnableSupportForMoreStructureInNames); - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("nombre"); - field.name = ASCIIToUTF16("nombre"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name0"))); - - field.label = ASCIIToUTF16("apellido paterno"); - field.name = ASCIIToUTF16("apellido_paterno"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("segunda apellido"); - field.name = ASCIIToUTF16("segunda_apellido"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - field.label = ASCIIToUTF16("middle name"); - field.name = ASCIIToUTF16("middle_name"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name3"))); - - AutofillScanner scanner(list_); - - field_ = Parse(&scanner); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name0")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name0")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST_SECOND, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name3")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_MIDDLE, - field_candidates_map_[ASCIIToUTF16("name3")].BestHeuristicType()); + AddTextFormFieldData("nombre", "nombre", NAME_FIRST); + AddTextFormFieldData("middle name", "middle_name", NAME_MIDDLE); + AddTextFormFieldData("apellido paterno", "apellido_paterno", NAME_LAST_FIRST); + AddTextFormFieldData("segunda apellido", "segunda_apellido", + NAME_LAST_SECOND); + + ClassifyAndVerify(ParseResult::PARSED); } // This case is from the dell.com checkout page. The middle initial "mi" string // came at the end following other descriptive text. http://crbug.com/45123. TEST_F(NameFieldTest, MiddleInitialAtEnd) { - FormFieldData field; - field.form_control_type = "text"; - - field.label = base::string16(); - field.name = ASCIIToUTF16("XXXnameXXXfirst"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("XXXnameXXXmi"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name2"))); - - field.label = base::string16(); - field.name = ASCIIToUTF16("XXXnameXXXlast"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("name3"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_FIRST, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_MIDDLE_INITIAL, - field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType()); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name3")) != - field_candidates_map_.end()); - EXPECT_EQ(NAME_LAST, - field_candidates_map_[ASCIIToUTF16("name3")].BestHeuristicType()); + AddTextFormFieldData("XXXnameXXXfirst", "", NAME_FIRST); + AddTextFormFieldData("XXXnameXXXmi", "", NAME_MIDDLE_INITIAL); + AddTextFormFieldData("XXXnameXXXlast", "", NAME_LAST); + + ClassifyAndVerify(ParseResult::PARSED); } // Test the coverage of all found strings for first and second last names. @@ -574,4 +201,19 @@ TEST_F(NameFieldTest, HispanicLastNameRegexConverage) { } } +// Tests that address name is not misclassified as name or honorific prefix. +TEST_F(NameFieldTest, NotAddressName) { + AddTextFormFieldData("name", "Identificação do Endereço", UNKNOWN_TYPE); + AddTextFormFieldData("title", "Adres Adı", UNKNOWN_TYPE); + + ClassifyAndVerify(ParseResult::NOT_PARSED); +} + +// Tests that contact name is classified as full name. +TEST_F(NameFieldTest, ContactNameFull) { + AddTextFormFieldData("contact", "Контактное лицо", NAME_FULL); + + ClassifyAndVerify(ParseResult::PARSED); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc b/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc new file mode 100644 index 00000000000..93fc4be0625 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc @@ -0,0 +1,129 @@ +// Copyright 2021 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 "components/autofill/core/browser/form_parsing/parsing_test_utils.h" + +namespace autofill { + +FormFieldTestBase::FormFieldTestBase() = default; +FormFieldTestBase::~FormFieldTestBase() = default; + +void FormFieldTestBase::AddFormFieldData(std::string control_type, + std::string name, + std::string label, + ServerFieldType expected_type) { + AddFormFieldDataWithLength(control_type, name, label, /*max_length=*/0, + expected_type); +} + +void FormFieldTestBase::AddFormFieldDataWithLength( + std::string control_type, + std::string name, + std::string label, + int max_length, + ServerFieldType expected_type) { + FormFieldData field_data; + field_data.form_control_type = control_type; + field_data.name = base::UTF8ToUTF16(name); + field_data.label = base::UTF8ToUTF16(label); + field_data.max_length = max_length; + field_data.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field_data)); + expected_classifications_.insert( + std::make_pair(field_data.unique_renderer_id, expected_type)); +} + +void FormFieldTestBase::AddSelectOneFormFieldData( + std::string name, + std::string label, + const std::vector<std::string>& options_contents, + const std::vector<std::string>& options_values, + ServerFieldType expected_type) { + AddSelectOneFormFieldDataWithLength(name, label, 0, options_contents, + options_values, expected_type); +} + +void FormFieldTestBase::AddSelectOneFormFieldDataWithLength( + std::string name, + std::string label, + int max_length, + const std::vector<std::string>& options_contents, + const std::vector<std::string>& options_values, + ServerFieldType expected_type) { + AddFormFieldData("select-one", name, label, expected_type); + FormFieldData* field_data = list_.back().get(); + field_data->max_length = max_length; + + for (auto option_content : options_contents) { + field_data->option_contents.push_back(base::UTF8ToUTF16(option_content)); + } + + for (auto option_value : options_values) { + field_data->option_values.push_back(base::UTF8ToUTF16(option_value)); + } +} + +// Convenience wrapper for text control elements. +void FormFieldTestBase::AddTextFormFieldData( + std::string name, + std::string label, + ServerFieldType expected_classification) { + AddFormFieldData("text", name, label, expected_classification); +} + +// Apply parsing and verify the expected types. +// |parsed| indicates if at least one field could be parsed successfully. +// |page_language| the language to be used for parsing, default empty value +// means the language is unknown and patterns of all languages are used. +void FormFieldTestBase::ClassifyAndVerify(ParseResult parse_result, + const LanguageCode& page_language) { + AutofillScanner scanner(list_); + field_ = Parse(&scanner, page_language); + + if (parse_result == ParseResult::NOT_PARSED) { + ASSERT_EQ(nullptr, field_.get()); + return; + } + ASSERT_NE(nullptr, field_.get()); + field_->AddClassificationsForTesting(&field_candidates_map_); + + TestClassificationExpectations(); +} + +void FormFieldTestBase::TestClassificationExpectations() { + for (const std::pair<FieldRendererId, ServerFieldType> it : + expected_classifications_) { + if (it.second != UNKNOWN_TYPE) { + SCOPED_TRACE(testing::Message() + << "Found type " + << AutofillType::ServerFieldTypeToString( + field_candidates_map_[it.first].BestHeuristicType()) + << ", expected type " + << AutofillType::ServerFieldTypeToString(it.second)); + + ASSERT_TRUE(field_candidates_map_.find(it.first) != + field_candidates_map_.end()); + EXPECT_EQ(it.second, field_candidates_map_[it.first].BestHeuristicType()); + } else { + SCOPED_TRACE( + testing::Message() + << "Expected type UNKNOWN_TYPE but got " + << AutofillType::ServerFieldTypeToString( + field_candidates_map_.find(it.first) != + field_candidates_map_.end() + ? field_candidates_map_[it.first].BestHeuristicType() + : UNKNOWN_TYPE)); + EXPECT_EQ(field_candidates_map_.find(it.first), + field_candidates_map_.end()); + } + } +} + +FieldRendererId FormFieldTestBase::MakeFieldRendererId() { + return FieldRendererId(++id_counter_); +} + +FormFieldTest::FormFieldTest() = default; +FormFieldTest::~FormFieldTest() = default; +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.h b/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.h new file mode 100644 index 00000000000..df5d271565f --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.h @@ -0,0 +1,112 @@ +// Copyright 2021 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_PARSING_TEST_UTILS_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_PARSING_TEST_UTILS_H_ + +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/form_parsing/address_field.h" +#include "components/autofill/core/browser/form_parsing/autofill_scanner.h" +#include "components/autofill/core/common/form_field_data.h" +#include "components/autofill/core/common/language_code.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +enum class ParseResult { + // The form was successfully parsed and at least one type was assigned. + PARSED = 0, + // Not a single type was assigned. + NOT_PARSED, + kMaxValue = NOT_PARSED +}; + +class FormFieldTestBase { + public: + FormFieldTestBase(const FormFieldTestBase&) = delete; + FormFieldTestBase(); + ~FormFieldTestBase(); + FormFieldTestBase& operator=(const FormFieldTestBase&) = delete; + + protected: + // Add a field with |control_type|, the |name|, the |label| the expected + // parsed type |expected_type|. + void AddFormFieldData(std::string control_type, + std::string name, + std::string label, + ServerFieldType expected_type); + + // Convenience wrapper for text control elements with a maximal length. + void AddFormFieldDataWithLength(std::string control_type, + std::string name, + std::string label, + int max_length, + ServerFieldType expected_type); + + // Convenience wrapper for text control elements. + void AddTextFormFieldData(std::string name, + std::string label, + ServerFieldType expected_classification); + + // Convenience wrapper for 'select-one' elements with a max length. + void AddSelectOneFormFieldDataWithLength( + std::string name, + std::string label, + int max_length, + const std::vector<std::string>& options_contents, + const std::vector<std::string>& options_values, + ServerFieldType expected_type); + + // Convenience wrapper for 'select-one' elements. + void AddSelectOneFormFieldData( + std::string name, + std::string label, + const std::vector<std::string>& options_contents, + const std::vector<std::string>& options_values, + ServerFieldType expected_type); + + // Apply parsing and verify the expected types. + // |parsed| indicates if at least one field could be parsed successfully. + // |page_language| the language to be used for parsing, default empty value + // means the language is unknown and patterns of all languages are used. + void ClassifyAndVerify(ParseResult parse_result = ParseResult::PARSED, + const LanguageCode& page_language = LanguageCode("")); + + // Test the parsed verifications against the expectations. + void TestClassificationExpectations(); + + // Apply the parsing with a specific parser. + virtual std::unique_ptr<FormField> Parse( + AutofillScanner* scanner, + const LanguageCode& page_language) = 0; + + FieldRendererId MakeFieldRendererId(); + + std::vector<std::unique_ptr<AutofillField>> list_; + std::unique_ptr<FormField> field_; + FieldCandidatesMap field_candidates_map_; + std::map<FieldRendererId, ServerFieldType> expected_classifications_; + + private: + uint64_t id_counter_ = 0; +}; + +class FormFieldTest : public FormFieldTestBase, public testing::Test { + public: + FormFieldTest(const FormFieldTest&) = delete; + FormFieldTest(); + ~FormFieldTest() override; + FormFieldTest& operator=(const FormFieldTest&) = delete; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_PARSING_TEST_UTILS_H_ diff --git a/chromium/components/autofill/core/browser/form_parsing/phone_field.cc b/chromium/components/autofill/core/browser/form_parsing/phone_field.cc index b50388208be..451ca2c75d6 100644 --- a/chromium/components/autofill/core/browser/form_parsing/phone_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/phone_field.cc @@ -213,7 +213,7 @@ bool PhoneField::LikelyAugmentedPhoneCountryCode( // static std::unique_ptr<FormField> PhoneField::Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { if (scanner->IsEnd()) return nullptr; @@ -420,7 +420,8 @@ const char* PhoneField::GetRegExpName(RegexType regex_id) { return ""; } -// +// Returns the string representation of |phonetype_id| as it is used to key to +// identify coressponding patterns. std::string PhoneField::GetJSONFieldType(RegexType phonetype_id) { switch (phonetype_id) { case REGEX_COUNTRY: @@ -455,14 +456,15 @@ bool PhoneField::ParsePhoneField(AutofillScanner* scanner, const RegExLogging& logging, const bool is_country_code_field, const std::string& json_field_type, - const std::string& page_language) { + const LanguageCode& page_language) { int match_type = MATCH_DEFAULT | MATCH_TELEPHONE | MATCH_NUMBER; // Include the selection boxes too for the matching of the phone country code. if (is_country_code_field) match_type |= MATCH_SELECT; - auto& patterns = PatternProvider::GetInstance().GetMatchPatterns( - json_field_type, page_language); + const std::vector<MatchingPattern>& patterns = + PatternProvider::GetInstance().GetMatchPatterns(json_field_type, + page_language); return ParseFieldSpecifics(scanner, base::UTF8ToUTF16(regex), match_type, patterns, field, logging); diff --git a/chromium/components/autofill/core/browser/form_parsing/phone_field.h b/chromium/components/autofill/core/browser/form_parsing/phone_field.h index 6ac25f26693..1b3bf5d7fde 100644 --- a/chromium/components/autofill/core/browser/form_parsing/phone_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/phone_field.h @@ -18,6 +18,7 @@ #include "components/autofill/core/browser/data_model/phone_number.h" #include "components/autofill/core/browser/form_parsing/form_field.h" #include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/common/language_code.h" namespace autofill { @@ -36,7 +37,7 @@ class PhoneField : public FormField { PhoneField& operator=(const PhoneField&) = delete; static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); #if defined(UNIT_TEST) @@ -108,7 +109,7 @@ class PhoneField : public FormField { const RegExLogging& logging, const bool is_country_code_field, const std::string& json_field_type, - const std::string& page_language); + const LanguageCode& page_language); // Returns true if |scanner| points to a <select> field that appears to be the // phone country code by looking at its option contents. diff --git a/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc index 71e0cf605c4..1257ba01c13 100644 --- a/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/phone_field_unittest.cc @@ -9,14 +9,13 @@ #include <memory> #include <vector> +#include "base/containers/contains.h" #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/form_field_data.h" #include "testing/gtest/include/gtest/gtest.h" @@ -47,7 +46,7 @@ class PhoneFieldTest : public testing::Test { // An empty page_language means the language is unknown and patterns of all // languages are used. std::unique_ptr<FormField> field = - PhoneField::Parse(scanner, /*page_language=*/"", nullptr); + PhoneField::Parse(scanner, LanguageCode(""), nullptr); return std::unique_ptr<PhoneField>( static_cast<PhoneField*>(field.release())); } @@ -58,11 +57,11 @@ class PhoneFieldTest : public testing::Test { field_candidates_map_.clear(); } - void CheckField(const std::string& name, + void CheckField(const FieldRendererId id, ServerFieldType expected_type) const { - auto it = field_candidates_map_.find(ASCIIToUTF16(name)); - ASSERT_TRUE(it != field_candidates_map_.end()) << name; - EXPECT_EQ(expected_type, it->second.BestHeuristicType()) << name; + auto it = field_candidates_map_.find(id); + ASSERT_TRUE(it != field_candidates_map_.end()); + EXPECT_EQ(expected_type, it->second.BestHeuristicType()); } // Populates a select |field| with the |label|, the |name| and the |contents|. @@ -85,8 +84,12 @@ class PhoneFieldTest : public testing::Test { std::unique_ptr<PhoneField> field_; FieldCandidatesMap field_candidates_map_; - // RAII object to mock the the PatternProvider. - TestPatternProvider test_pattern_provider_; + FieldRendererId MakeFieldRendererId() { + return FieldRendererId(++id_counter_); + } + + private: + uint64_t id_counter_ = 0; }; TEST_F(PhoneFieldTest, Empty) { @@ -111,14 +114,15 @@ TEST_F(PhoneFieldTest, ParseOneLinePhone) { field.form_control_type = field_type; field.label = ASCIIToUTF16("Phone"); field.name = ASCIIToUTF16("phone"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phone1"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId phone1 = list_.back()->unique_renderer_id; AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassificationsForTesting(&field_candidates_map_); - CheckField("phone1", PHONE_HOME_WHOLE_NUMBER); + CheckField(phone1, PHONE_HOME_WHOLE_NUMBER); } } @@ -131,20 +135,22 @@ TEST_F(PhoneFieldTest, ParseTwoLinePhone) { field.form_control_type = field_type; field.label = ASCIIToUTF16("Area Code"); field.name = ASCIIToUTF16("area code"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("areacode1"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId areacode1 = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16("Phone"); field.name = ASCIIToUTF16("phone"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phone2"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId phone2 = list_.back()->unique_renderer_id; AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassificationsForTesting(&field_candidates_map_); - CheckField("areacode1", PHONE_HOME_CITY_CODE); - CheckField("phone2", PHONE_HOME_NUMBER); + CheckField(areacode1, PHONE_HOME_CITY_CODE); + CheckField(phone2, PHONE_HOME_NUMBER); } } @@ -163,35 +169,39 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumber) { field.label = ASCIIToUTF16("Phone:"); field.name = ASCIIToUTF16("dayphone1"); field.max_length = 0; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("areacode1"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId areacode1 = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16("-"); field.name = ASCIIToUTF16("dayphone2"); field.max_length = 3; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("prefix2"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId prefix2 = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16("-"); field.name = ASCIIToUTF16("dayphone3"); field.max_length = 4; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("suffix3"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId suffix3 = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16("ext.:"); field.name = ASCIIToUTF16("dayphone4"); field.max_length = 0; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("ext4"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId ext4 = list_.back()->unique_renderer_id; AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassificationsForTesting(&field_candidates_map_); - CheckField("areacode1", PHONE_HOME_CITY_CODE); - CheckField("prefix2", PHONE_HOME_NUMBER); - CheckField("suffix3", PHONE_HOME_NUMBER); - EXPECT_TRUE(base::Contains(field_candidates_map_, ASCIIToUTF16("ext4"))); + CheckField(areacode1, PHONE_HOME_CITY_CODE); + CheckField(prefix2, PHONE_HOME_NUMBER); + CheckField(suffix3, PHONE_HOME_NUMBER); + EXPECT_TRUE(base::Contains(field_candidates_map_, ext4)); } } @@ -207,26 +217,29 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix) { field.form_control_type = field_type; field.label = ASCIIToUTF16("Phone:"); field.name = ASCIIToUTF16("area"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("areacode1"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId areacode1 = list_.back()->unique_renderer_id; field.label = base::string16(); field.name = ASCIIToUTF16("prefix"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("prefix2"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId prefix2 = list_.back()->unique_renderer_id; field.label = base::string16(); field.name = ASCIIToUTF16("suffix"); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("suffix3"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId suffix3 = list_.back()->unique_renderer_id; AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassificationsForTesting(&field_candidates_map_); - CheckField("areacode1", PHONE_HOME_CITY_CODE); - CheckField("prefix2", PHONE_HOME_NUMBER); - CheckField("suffix3", PHONE_HOME_NUMBER); + CheckField(areacode1, PHONE_HOME_CITY_CODE); + CheckField(prefix2, PHONE_HOME_NUMBER); + CheckField(suffix3, PHONE_HOME_NUMBER); } } @@ -240,28 +253,31 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix2) { field.label = ASCIIToUTF16("("); field.name = ASCIIToUTF16("phone1"); field.max_length = 3; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phone1"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId phone1 = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16(")"); field.name = ASCIIToUTF16("phone2"); field.max_length = 3; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phone2"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId phone2 = list_.back()->unique_renderer_id; field.label = base::string16(); field.name = ASCIIToUTF16("phone3"); field.max_length = 4; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phone3"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId phone3 = list_.back()->unique_renderer_id; AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassificationsForTesting(&field_candidates_map_); - CheckField("phone1", PHONE_HOME_CITY_CODE); - CheckField("phone2", PHONE_HOME_NUMBER); - CheckField("phone3", PHONE_HOME_NUMBER); + CheckField(phone1, PHONE_HOME_CITY_CODE); + CheckField(phone2, PHONE_HOME_NUMBER); + CheckField(phone3, PHONE_HOME_NUMBER); } } @@ -277,21 +293,23 @@ TEST_F(PhoneFieldTest, CountryAndCityAndPhoneNumber) { field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("CountryCode"); field.max_length = 3; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("country"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId country = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("PhoneNumber"); field.max_length = 10; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phone"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId phone = list_.back()->unique_renderer_id; AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassificationsForTesting(&field_candidates_map_); - CheckField("country", PHONE_HOME_COUNTRY_CODE); - CheckField("phone", PHONE_HOME_CITY_AND_NUMBER); + CheckField(country, PHONE_HOME_COUNTRY_CODE); + CheckField(phone, PHONE_HOME_CITY_AND_NUMBER); } } @@ -307,23 +325,25 @@ TEST_F(PhoneFieldTest, CountryAndCityAndPhoneNumberWithLongerMaxLength) { field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("CountryCode"); field.max_length = 3; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("country"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId country = list_.back()->unique_renderer_id; // Verify if websites expect a longer formatted number like: // (514)-123-1234, autofill is able to classify correctly. field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("PhoneNumber"); field.max_length = 14; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phone"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId phone = list_.back()->unique_renderer_id; AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassificationsForTesting(&field_candidates_map_); - CheckField("country", PHONE_HOME_COUNTRY_CODE); - CheckField("phone", PHONE_HOME_CITY_AND_NUMBER); + CheckField(country, PHONE_HOME_COUNTRY_CODE); + CheckField(phone, PHONE_HOME_CITY_AND_NUMBER); } } @@ -335,29 +355,32 @@ TEST_F(PhoneFieldTest, CountryCodeIsSelectElement) { field.label = ASCIIToUTF16("Phone Country Code"); field.name = ASCIIToUTF16("ccode"); field.form_control_type = "select-one"; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("countryCode"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId country_code = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16("Phone City Code"); field.name = ASCIIToUTF16("areacode"); field.form_control_type = "text"; field.max_length = 3; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("cityCode"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId cityCode = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("phonenumber"); field.max_length = 0; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phoneNumber"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId phoneNumber = list_.back()->unique_renderer_id; AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassificationsForTesting(&field_candidates_map_); - CheckField("countryCode", PHONE_HOME_COUNTRY_CODE); - CheckField("cityCode", PHONE_HOME_CITY_CODE); - CheckField("phoneNumber", PHONE_HOME_NUMBER); + CheckField(country_code, PHONE_HOME_COUNTRY_CODE); + CheckField(cityCode, PHONE_HOME_CITY_CODE); + CheckField(phoneNumber, PHONE_HOME_NUMBER); } // Tests if the country code, city code and phone number fields are correctly @@ -375,29 +398,32 @@ TEST_F(PhoneFieldTest, CountryCodeWithOptions) { "(+91) India", "(+49) Germany", "(+1) United States", "(+20) Egypt", "(+1242) Bahamas", "(+593) Ecuador", "(+7) Russia"}; CreateTestSelectField("PC", "PC", augmented_field_options_list, &field); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("countryCode"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId country_code = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16("Phone City Code"); field.name = ASCIIToUTF16("areacode"); field.form_control_type = "text"; field.max_length = 3; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("cityCode"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId cityCode = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("phonenumber"); field.max_length = 0; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phoneNumber"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId phoneNumber = list_.back()->unique_renderer_id; AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassificationsForTesting(&field_candidates_map_); - CheckField("countryCode", PHONE_HOME_COUNTRY_CODE); - CheckField("cityCode", PHONE_HOME_CITY_CODE); - CheckField("phoneNumber", PHONE_HOME_NUMBER); + CheckField(country_code, PHONE_HOME_COUNTRY_CODE); + CheckField(cityCode, PHONE_HOME_CITY_CODE); + CheckField(phoneNumber, PHONE_HOME_NUMBER); } // Tests if the country code field is correctly classified by the heuristic when @@ -460,26 +486,32 @@ TEST_F(PhoneFieldTest, IsPhoneCountryCodeField) { {"0091", "0049", "001", "0020", "001242", "00593", "007"}}; for (size_t i = 0; i < augmented_field_options_list.size(); ++i) { + // TODO(crbug/1151473): The below CheckField() call fails in iteration 4. + if (i == 4) + continue; + + list_.clear(); SCOPED_TRACE(testing::Message() << "i = " << i); const auto& options_list = augmented_field_options_list[i]; CreateTestSelectField("PC", "PC", options_list, &field); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("countryCode"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); + FieldRendererId country_code = list_.back()->unique_renderer_id; field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("phonenumber"); field.max_length = 14; field.form_control_type = "text"; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phoneNumber"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassificationsForTesting(&field_candidates_map_); - CheckField("countryCode", PHONE_HOME_COUNTRY_CODE); + CheckField(country_code, PHONE_HOME_COUNTRY_CODE); } -} // namespace autofill +} // Tests that the month field is not classified as |PHONE_HOME_COUNTRY_CODE|. TEST_F(PhoneFieldTest, IsMonthField) { @@ -501,15 +533,15 @@ TEST_F(PhoneFieldTest, IsMonthField) { SCOPED_TRACE(testing::Message() << "i = " << i); const auto& options_list = augmented_field_options_list[i]; CreateTestSelectField("Month", "Month", options_list, &field); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("months"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("phonenumber"); field.max_length = 14; field.form_control_type = "text"; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phoneNumber"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); AutofillScanner scanner(list_); field_ = Parse(&scanner); @@ -567,15 +599,15 @@ TEST_F(PhoneFieldTest, IsDayField) { SCOPED_TRACE(testing::Message() << "i = " << i); const auto& options_list = augmented_field_options_list[i]; CreateTestSelectField("Field", "Field", options_list, &field); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("day"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("phonenumber"); field.max_length = 14; field.form_control_type = "text"; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phoneNumber"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); AutofillScanner scanner(list_); field_ = Parse(&scanner); @@ -619,15 +651,15 @@ TEST_F(PhoneFieldTest, IsYearField) { SCOPED_TRACE(testing::Message() << "i = " << i); const auto& options_list = augmented_field_options_list[i]; CreateTestSelectField("Field", "Field", options_list, &field); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("year"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("phonenumber"); field.max_length = 14; field.form_control_type = "text"; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phoneNumber"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); AutofillScanner scanner(list_); field_ = Parse(&scanner); @@ -657,15 +689,15 @@ TEST_F(PhoneFieldTest, IsTimeZoneField) { SCOPED_TRACE(testing::Message() << "i = " << i); const auto& options_list = augmented_field_options_list[i]; CreateTestSelectField("Time Zone", "TimeZone", options_list, &field); - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("timeZone"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("phonenumber"); field.max_length = 14; field.form_control_type = "text"; - list_.push_back( - std::make_unique<AutofillField>(field, ASCIIToUTF16("phoneNumber"))); + field.unique_renderer_id = MakeFieldRendererId(); + list_.push_back(std::make_unique<AutofillField>(field)); AutofillScanner scanner(list_); field_ = Parse(&scanner); diff --git a/chromium/components/autofill/core/browser/form_parsing/price_field.cc b/chromium/components/autofill/core/browser/form_parsing/price_field.cc index 595b759ad00..bc1fed93b86 100644 --- a/chromium/components/autofill/core/browser/form_parsing/price_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/price_field.cc @@ -13,16 +13,16 @@ namespace autofill { // static std::unique_ptr<FormField> PriceField::Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { AutofillField* field; - auto& patterns = + const std::vector<MatchingPattern>& price_patterns = PatternProvider::GetInstance().GetMatchPatterns("PRICE", page_language); if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kPriceRe), MATCH_DEFAULT | MATCH_NUMBER | MATCH_SELECT | MATCH_TEXT_AREA | MATCH_SEARCH, - patterns, &field, {log_manager, kPriceRe})) { + price_patterns, &field, {log_manager, kPriceRe})) { return std::make_unique<PriceField>(field); } diff --git a/chromium/components/autofill/core/browser/form_parsing/price_field.h b/chromium/components/autofill/core/browser/form_parsing/price_field.h index 7d05f460dae..99013c2203a 100644 --- a/chromium/components/autofill/core/browser/form_parsing/price_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/price_field.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "components/autofill/core/browser/form_parsing/form_field.h" #include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/common/language_code.h" namespace autofill { @@ -23,9 +24,9 @@ class LogManager; class PriceField : public FormField { public: static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); - PriceField(const AutofillField* field); + explicit PriceField(const AutofillField* field); protected: void AddClassifications(FieldCandidatesMap* field_candidates) const override; diff --git a/chromium/components/autofill/core/browser/form_parsing/price_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/price_field_unittest.cc index 464830b2852..5243ac9ffb8 100644 --- a/chromium/components/autofill/core/browser/form_parsing/price_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/price_field_unittest.cc @@ -4,81 +4,36 @@ #include "components/autofill/core/browser/form_parsing/price_field.h" -#include <memory> -#include <vector> - -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/browser/autofill_field.h" -#include "components/autofill/core/browser/form_parsing/autofill_scanner.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" -#include "components/autofill/core/common/form_field_data.h" -#include "testing/gtest/include/gtest/gtest.h" +#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h" using base::ASCIIToUTF16; namespace autofill { -class PriceFieldTest : public testing::Test { +class PriceFieldTest : public FormFieldTest { public: PriceFieldTest() = default; PriceFieldTest(const PriceFieldTest&) = delete; PriceFieldTest& operator=(const PriceFieldTest&) = delete; protected: - // Downcast for tests. - static std::unique_ptr<PriceField> Parse(AutofillScanner* scanner) { - // An empty page_language means the language is unknown and patterns of all - // languages are used. - std::unique_ptr<FormField> field = - PriceField::Parse(scanner, /*page_language=*/"", nullptr); - return std::unique_ptr<PriceField>( - static_cast<PriceField*>(field.release())); + std::unique_ptr<FormField> Parse( + AutofillScanner* scanner, + const LanguageCode& page_language = LanguageCode("en")) override { + return PriceField::Parse(scanner, page_language, nullptr); } - - std::vector<std::unique_ptr<AutofillField>> list_; - std::unique_ptr<PriceField> field_; - FieldCandidatesMap field_candidates_map_; - - // RAII object to mock the the PatternProvider. - TestPatternProvider test_pattern_provider_; }; TEST_F(PriceFieldTest, ParsePrice) { - FormFieldData price_field; - price_field.form_control_type = "text"; - - price_field.label = ASCIIToUTF16("name your price"); - price_field.name = ASCIIToUTF16("userPrice"); + AddTextFormFieldData("name your price", "userPrice", PRICE); - list_.push_back( - std::make_unique<AutofillField>(price_field, ASCIIToUTF16("price1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassifications(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("price1")) != - field_candidates_map_.end()); - EXPECT_EQ(PRICE, - field_candidates_map_[ASCIIToUTF16("price1")].BestHeuristicType()); + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(PriceFieldTest, ParseNonPrice) { - FormFieldData address_field; - address_field.form_control_type = "text"; - - address_field.label = ASCIIToUTF16("Name"); - address_field.name = ASCIIToUTF16("firstName"); - - list_.push_back( - std::make_unique<AutofillField>(address_field, ASCIIToUTF16("name1"))); + AddTextFormFieldData("firstName", "Name", UNKNOWN_TYPE); - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_EQ(nullptr, field_.get()); + ClassifyAndVerify(ParseResult::NOT_PARSED); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/search_field.cc b/chromium/components/autofill/core/browser/form_parsing/search_field.cc index a7564839f06..7ed0724daa4 100644 --- a/chromium/components/autofill/core/browser/form_parsing/search_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/search_field.cc @@ -13,7 +13,7 @@ namespace autofill { // static std::unique_ptr<FormField> SearchField::Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { AutofillField* field; auto& patterns = PatternProvider::GetInstance().GetMatchPatterns( diff --git a/chromium/components/autofill/core/browser/form_parsing/search_field.h b/chromium/components/autofill/core/browser/form_parsing/search_field.h index 99e70eb9a2c..c0709a9b773 100644 --- a/chromium/components/autofill/core/browser/form_parsing/search_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/search_field.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "components/autofill/core/browser/form_parsing/form_field.h" #include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/common/language_code.h" namespace autofill { @@ -23,7 +24,7 @@ class LogManager; class SearchField : public FormField { public: static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); SearchField(const AutofillField* field); diff --git a/chromium/components/autofill/core/browser/form_parsing/search_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/search_field_unittest.cc index a6be80cc7e0..7fc31841bb9 100644 --- a/chromium/components/autofill/core/browser/form_parsing/search_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/search_field_unittest.cc @@ -5,80 +5,36 @@ #include "components/autofill/core/browser/form_parsing/search_field.h" #include <memory> -#include <vector> -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/browser/autofill_field.h" -#include "components/autofill/core/browser/form_parsing/autofill_scanner.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" -#include "components/autofill/core/common/form_field_data.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::ASCIIToUTF16; +#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h" +#include "components/autofill/core/common/form_data.h" namespace autofill { -class SearchFieldTest : public testing::Test { +class SearchFieldTest : public FormFieldTest { public: SearchFieldTest() = default; SearchFieldTest(const SearchFieldTest&) = delete; SearchFieldTest& operator=(const SearchFieldTest&) = delete; protected: - // Downcast for tests. - static std::unique_ptr<SearchField> Parse(AutofillScanner* scanner) { - // An empty page_language means the language is unknown and patterns of all - // languages are used. - std::unique_ptr<FormField> field = - SearchField::Parse(scanner, /*page_language=*/"", nullptr); - return std::unique_ptr<SearchField>( - static_cast<SearchField*>(field.release())); + std::unique_ptr<FormField> Parse( + AutofillScanner* scanner, + const LanguageCode& page_language = LanguageCode("en")) override { + return SearchField::Parse(scanner, page_language, nullptr); } - - std::vector<std::unique_ptr<AutofillField>> list_; - std::unique_ptr<SearchField> field_; - FieldCandidatesMap field_candidates_map_; - - // RAII object to mock the the PatternProvider. - TestPatternProvider test_pattern_provider_; }; TEST_F(SearchFieldTest, ParseSearchTerm) { - FormFieldData search_field; - search_field.form_control_type = "text"; - - search_field.label = ASCIIToUTF16("Search"); - search_field.name = ASCIIToUTF16("search"); + AddTextFormFieldData("search", "Search", SEARCH_TERM); - list_.push_back( - std::make_unique<AutofillField>(search_field, ASCIIToUTF16("search1"))); - - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_NE(nullptr, field_.get()); - field_->AddClassifications(&field_candidates_map_); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("search1")) != - field_candidates_map_.end()); - EXPECT_EQ(SEARCH_TERM, - field_candidates_map_[ASCIIToUTF16("search1")].BestHeuristicType()); + ClassifyAndVerify(ParseResult::PARSED); } TEST_F(SearchFieldTest, ParseNonSearchTerm) { - FormFieldData address_field; - address_field.form_control_type = "text"; - - address_field.label = ASCIIToUTF16("Address"); - address_field.name = ASCIIToUTF16("address"); - - list_.push_back( - std::make_unique<AutofillField>(address_field, ASCIIToUTF16("address"))); + AddTextFormFieldData("address", "Address", UNKNOWN_TYPE); - AutofillScanner scanner(list_); - field_ = Parse(&scanner); - ASSERT_EQ(nullptr, field_.get()); + ClassifyAndVerify(ParseResult::NOT_PARSED); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/travel_field.cc b/chromium/components/autofill/core/browser/form_parsing/travel_field.cc index 2e1dc92a836..0817d6750f4 100644 --- a/chromium/components/autofill/core/browser/form_parsing/travel_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/travel_field.cc @@ -16,29 +16,33 @@ TravelField::~TravelField() = default; // static std::unique_ptr<FormField> TravelField::Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager) { - if (!scanner || scanner->IsEnd()) { + if (!scanner || scanner->IsEnd()) return nullptr; - } - auto& patternsP = PatternProvider::GetInstance().GetMatchPatterns( - "PASSPORT", page_language); - auto& patternsTO = PatternProvider::GetInstance().GetMatchPatterns( - "TRAVEL_ORIGIN", page_language); - auto& patternsTD = PatternProvider::GetInstance().GetMatchPatterns( - "TRAVEL_DESTINATION", page_language); - auto& patternsF = + + const std::vector<MatchingPattern>& passport_patterns = + PatternProvider::GetInstance().GetMatchPatterns("PASSPORT", + page_language); + const std::vector<MatchingPattern>& travel_origin_patterns = + PatternProvider::GetInstance().GetMatchPatterns("TRAVEL_ORIGIN", + page_language); + const std::vector<MatchingPattern>& travel_destination_patterns = + PatternProvider::GetInstance().GetMatchPatterns("TRAVEL_DESTINATION", + page_language); + const std::vector<MatchingPattern>& flight_patterns = PatternProvider::GetInstance().GetMatchPatterns("FLIGHT", page_language); auto travel_field = std::make_unique<TravelField>(); - if (ParseField(scanner, base::UTF8ToUTF16(kPassportRe), patternsP, + if (ParseField(scanner, base::UTF8ToUTF16(kPassportRe), passport_patterns, &travel_field->passport_, {log_manager, "kPassportRe"}) || - ParseField(scanner, base::UTF8ToUTF16(kTravelOriginRe), patternsTO, - &travel_field->origin_, {log_manager, "kTravelOriginRe"}) || - ParseField(scanner, base::UTF8ToUTF16(kTravelDestinationRe), patternsTD, - &travel_field->destination_, + ParseField(scanner, base::UTF8ToUTF16(kTravelOriginRe), + travel_origin_patterns, &travel_field->origin_, + {log_manager, "kTravelOriginRe"}) || + ParseField(scanner, base::UTF8ToUTF16(kTravelDestinationRe), + travel_destination_patterns, &travel_field->destination_, {log_manager, "kTravelDestinationRe"}) || - ParseField(scanner, base::UTF8ToUTF16(kFlightRe), patternsF, + ParseField(scanner, base::UTF8ToUTF16(kFlightRe), flight_patterns, &travel_field->flight_, {log_manager, "kFlightRe"})) { // If any regex matches, then we found a travel field. return std::move(travel_field); diff --git a/chromium/components/autofill/core/browser/form_parsing/travel_field.h b/chromium/components/autofill/core/browser/form_parsing/travel_field.h index 9b2d2e5e891..45d05511c10 100644 --- a/chromium/components/autofill/core/browser/form_parsing/travel_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/travel_field.h @@ -10,6 +10,7 @@ #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" #include "components/autofill/core/browser/form_parsing/form_field.h" #include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/common/language_code.h" namespace autofill { @@ -20,7 +21,7 @@ class TravelField : public FormField { ~TravelField() override; static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, - const std::string& page_language, + const LanguageCode& page_language, LogManager* log_manager); protected: diff --git a/chromium/components/autofill/core/browser/form_processing/label_processing_util.cc b/chromium/components/autofill/core/browser/form_processing/label_processing_util.cc new file mode 100644 index 00000000000..50e7e56a1b3 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_processing/label_processing_util.cc @@ -0,0 +1,108 @@ +// Copyright 2021 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 "components/autofill/core/browser/form_processing/label_processing_util.h" + +#include "base/ranges/algorithm.h" +#include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" + +namespace autofill { + +using LabelPieces = std::vector<base::StringPiece16>; + +// The maximum number of fields that can share a label. +const int kMaxNumberOfFieldsToShareALabel = 3; +// The maximum length of a label that can be shared among fields. +const int kMaxLengthOfShareableLabel = 40; + +base::Optional<std::vector<base::string16>> GetParseableLabels( + const LabelPieces& labels) { + // Make a copy of the labels. + LabelPieces shared_labels = labels; + + // Tracks if at least one shared label was found. + bool shared_labels_found = false; + + // The index of the current field that may be eligible to share its label with + // the subsequent fields. + size_t label_index = 0; + while (label_index < labels.size()) { + const auto& label = labels.at(label_index); + // If the label is empty or has a size that exceeds + // |kMaxLengthOfShareableLabel| it can not be shared with subsequent fields. + if (label.empty() || label.size() > kMaxLengthOfShareableLabel) { + ++label_index; + continue; + } + + // Otherwise search if the subsequent fields are empty. + size_t scan_index = label_index + 1; + while (scan_index < labels.size()) { + if (!labels.at(scan_index).empty()) { + break; + } + ++scan_index; + } + // After the loop, the |scan_index| points to the first subsequent field + // that does not have an empty label or is the first out-of-bound index. + + // Calculate the number of fields that may share a label. + size_t fields_to_share_label = scan_index - label_index; + + // Remember the current index and increment it to continue with the next + // non-empty field. + size_t shared_label_starting_index = label_index; + label_index = scan_index; + + // Determine if there is the correct number of fields that may share a + // label. + if (fields_to_share_label == 1 || + fields_to_share_label > kMaxNumberOfFieldsToShareALabel) { + continue; + } + + // Otherwise, try to split the label by single character separators. + LabelPieces label_components = base::SplitStringPiece( + label, base::ASCIIToUTF16("/,&-"), base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); + + // If the number of components does not match, try to split by common + // separating words. + if (label_components.size() != fields_to_share_label) { + for (const char* word : {" and ", " und ", " et ", " y "}) { + label_components = base::SplitStringPieceUsingSubstr( + label, base::ASCIIToUTF16(word), base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); + if (label_components.size() == fields_to_share_label) + break; + } + } + + // Continue to the next field if the right number of components has not + // been found. + if (label_components.size() != fields_to_share_label) + continue; + + shared_labels_found = true; + // Otherwise assign the label components to the fields. + for (size_t i = 0; i < label_components.size(); ++i) { + shared_labels[shared_label_starting_index + i] = label_components.at(i); + } + } + + if (!shared_labels_found) { + return base::nullopt; + } + + // Otherwise convert the shared label string pieces into strings for memory + // safety. + std::vector<base::string16> result; + result.reserve(shared_labels.size()); + base::ranges::transform(shared_labels, std::back_inserter(result), + [](auto& s) { return base::string16(s); }); + return base::make_optional(std::move(result)); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_processing/label_processing_util.h b/chromium/components/autofill/core/browser/form_processing/label_processing_util.h new file mode 100644 index 00000000000..31e581a5f32 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_processing/label_processing_util.h @@ -0,0 +1,24 @@ +// Copyright 2021 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PROCESSING_LABEL_PROCESSING_UTIL_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PROCESSING_LABEL_PROCESSING_UTIL_H_ + +#include <vector> +#include "base/optional.h" +#include "base/strings/string_piece.h" + +namespace autofill { + +// If parseable labels can be derived from |labels|, a vector of +// |base::string16| is return that is aligned with |labels|. +// Parseable labels can be derived by splitting one label between multiple +// adjacent fields. If there aren't any changes to the labels, |base::nullopt| +// is returned. +base::Optional<std::vector<base::string16>> GetParseableLabels( + const std::vector<base::StringPiece16>& labels); + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PROCESSING_LABEL_PROCESSING_UTIL_H_ diff --git a/chromium/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc b/chromium/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc new file mode 100644 index 00000000000..f72bdf6d788 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc @@ -0,0 +1,117 @@ +// Copyright 2021 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 "components/autofill/core/browser/form_processing/label_processing_util.h" + +#include "base/feature_list.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" +#include "components/autofill/core/common/autofill_features.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::ASCIIToUTF16; + +namespace { + +std::vector<base::StringPiece16> StringsToStringPieces( + const std::vector<base::string16>& strings) { + std::vector<base::StringPiece16> string_pieces; + for (const auto& s : strings) { + string_pieces.emplace_back(base::StringPiece16(s)); + } + return string_pieces; +} + +} // namespace + +namespace autofill { + +TEST(LabelProcessingUtil, GetParseableNameStringPieces) { + std::vector<base::string16> labels; + labels.push_back(ASCIIToUTF16("City")); + labels.push_back(ASCIIToUTF16("Street & House Number")); + labels.push_back(ASCIIToUTF16("")); + labels.push_back(ASCIIToUTF16("Zip")); + + auto expectation = base::make_optional(std::vector<base::string16>()); + expectation->push_back(ASCIIToUTF16("City")); + expectation->push_back(ASCIIToUTF16("Street")); + expectation->push_back(ASCIIToUTF16("House Number")); + expectation->push_back(ASCIIToUTF16("Zip")); + + EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); +} + +TEST(LabelProcessingUtil, GetParseableNameStringPieces_ThreeComponents) { + std::vector<base::string16> labels; + labels.push_back(ASCIIToUTF16("City")); + labels.push_back(ASCIIToUTF16("Street & House Number & Floor")); + labels.push_back(ASCIIToUTF16("")); + labels.push_back(ASCIIToUTF16("")); + labels.push_back(ASCIIToUTF16("Zip")); + + auto expectation = base::make_optional(std::vector<base::string16>()); + expectation->push_back(ASCIIToUTF16("City")); + expectation->push_back(ASCIIToUTF16("Street")); + expectation->push_back(ASCIIToUTF16("House Number")); + expectation->push_back(ASCIIToUTF16("Floor")); + expectation->push_back(ASCIIToUTF16("Zip")); + + EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); +} + +TEST(LabelProcessingUtil, GetParseableNameStringPieces_TooManyComponents) { + std::vector<base::string16> labels; + labels.push_back(ASCIIToUTF16("City")); + labels.push_back(ASCIIToUTF16("Street & House Number & Floor & Stairs")); + labels.push_back(ASCIIToUTF16("")); + labels.push_back(ASCIIToUTF16("")); + labels.push_back(ASCIIToUTF16("")); + labels.push_back(ASCIIToUTF16("Zip")); + + base::Optional<std::vector<base::string16>> expectation = base::nullopt; + ; + + EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); +} + +TEST(LabelProcessingUtil, GetParseableNameStringPieces_UnmachtingComponents) { + std::vector<base::string16> labels; + labels.push_back(ASCIIToUTF16("City")); + labels.push_back(ASCIIToUTF16("Street & House Number & Floor")); + labels.push_back(ASCIIToUTF16("")); + labels.push_back(ASCIIToUTF16("Zip")); + + base::Optional<std::vector<base::string16>> expectation = base::nullopt; + + EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); +} + +TEST(LabelProcessingUtil, GetParseableNameStringPieces_SplitableLabelAtEnd) { + std::vector<base::string16> labels; + labels.push_back(ASCIIToUTF16("City")); + labels.push_back(ASCIIToUTF16("")); + labels.push_back(ASCIIToUTF16("Zip")); + labels.push_back(ASCIIToUTF16("Street & House Number & Floor")); + + base::Optional<std::vector<base::string16>> expectation = base::nullopt; + + EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); +} + +TEST(LabelProcessingUtil, GetParseableNameStringPieces_TooLongLabel) { + std::vector<base::string16> labels; + labels.push_back(ASCIIToUTF16("City")); + labels.push_back( + ASCIIToUTF16("Street & House Number with a lot of additional text that " + "exceeds 40 characters by far")); + labels.push_back(ASCIIToUTF16("")); + labels.push_back(ASCIIToUTF16("Zip")); + + base::Optional<std::vector<base::string16>> expectation = base::nullopt; + + EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc b/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc new file mode 100644 index 00000000000..7df75933fd0 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc @@ -0,0 +1,239 @@ +// Copyright 2021 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 "components/autofill/core/browser/form_processing/name_processing_util.h" + +#include "base/feature_list.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/autofill_regexes.h" +#include "components/autofill/core/browser/form_structure.h" +#include "components/autofill/core/common/autofill_features.h" + +namespace autofill { + +// Only removing common name prefixes if we have a minimum number of fields and +// a minimum prefix length. These values are chosen to avoid cases such as two +// fields with "address1" and "address2" and be effective against web frameworks +// which prepend prefixes such as "ctl01$ctl00$MainContentRegion$" on all +// fields. +constexpr int kCommonNamePrefixRemovalFieldThreshold = 3; +// Minimum required length for prefixes common to a subset of the field names. +constexpr int kMinCommonNamePrefixLength = 16; +// Minimum required number of available fields for trying to remove affixes. +constexpr int kCommonNameAffixRemovalFieldNumberThreshold = 3; +// Minimum required length for affixes common to all field names. +constexpr int kMinCommonNameAffixLength = 3; +// Minimum required length for prefixes common to a subset of the field names. +constexpr int kMinCommonNameLongPrefixLength = 16; +// Regular expression for checking if |parseable_name| is valid after stripping +// affixes. +constexpr char kParseableNameValidationRe[] = "\\D"; + +using NamePieces = std::vector<base::StringPiece16>; +using OptionalNamePieces = base::Optional<NamePieces>; + +// Returns the length of the longest common prefix. +// If |findCommonSuffix| is set, the length of the longest common suffix is +// returned. +size_t FindLongestCommonAffixLength(const NamePieces& strings, + bool findCommonSuffix) { + if (strings.empty()) + return 0; + + // Go through each character of the first string until there is a mismatch at + // the same position in any other string. Adapted from http://goo.gl/YGukMM. + for (size_t affix_len = 0; affix_len < strings[0].size(); affix_len++) { + size_t base_string_index = + findCommonSuffix ? strings[0].size() - affix_len - 1 : affix_len; + for (size_t i = 1; i < strings.size(); i++) { + size_t compared_string_index = + findCommonSuffix ? strings[i].size() - affix_len - 1 : affix_len; + if (affix_len >= strings[i].size() || + strings[i][compared_string_index] != strings[0][base_string_index]) { + // Mismatch found. + return affix_len; + } + } + } + return strings[0].size(); +} + +// Find the longest prefix in |strings| when only considering entries with a +// |minimal_length|. +size_t FindLongestCommonPrefixLengthInStringsWithMinimalLength( + const NamePieces& strings, + size_t minimal_length) { + if (strings.empty()) + return 0; + + NamePieces filtered_strings; + + // Any strings less than |minimal_length| are not considered when processing + // for a common prefix. + std::copy_if( + strings.begin(), strings.end(), std::back_inserter(filtered_strings), + [&](base::StringPiece16 s) { return s.length() >= minimal_length; }); + + if (filtered_strings.empty()) + return 0; + + return FindLongestCommonAffixLength(filtered_strings, false); +} + +// Returns true if |parseable_name| is a valid parseable_name. Current criterion +// is the |autofill::kParseableNameValidationRe| regex. +bool IsValidParseableName(const base::StringPiece16 parseable_name) { + static const base::string16 kParseableNameValidationPattern = + base::UTF8ToUTF16(kParseableNameValidationRe); + return MatchesPattern(parseable_name, kParseableNameValidationPattern); +} + +// Tries to strip |offset_left| and |offset_right| from entriees in +// |field_names| and checks if the resulting names are still valid parseable +// names. If not possible, return |base::nullopt|. +OptionalNamePieces GetStrippedParseableNamesIfValid( + const NamePieces& field_names, + size_t offset_left, + size_t offset_right, + size_t minimal_string_length_to_strip) { + NamePieces stripped_names; + stripped_names.reserve(field_names.size()); + + for (auto& parseable_name : field_names) { + // This check allows to only strip affixes from long enough strings. + stripped_names.push_back( + parseable_name.size() > offset_right + offset_left && + parseable_name.size() >= minimal_string_length_to_strip + ? parseable_name.substr(offset_left, parseable_name.size() - + offset_right - offset_left) + : parseable_name); + + if (!IsValidParseableName(stripped_names.back())) + return base::nullopt; + } + + return base::make_optional(stripped_names); +} + +// Tries to remove common affixes from |field_names| and returns the result. If +// neither a common affix exists, or if one or more of the resulting strings is +// not a valid parseable name, |base::nullopt| is returned. +// The number of names in |field_names| must exceed +// |kCommonNameAffixRemovalFieldNumberThreshold| in order to make the affix +// removal possible. Also, the length of an affix must exceed +// |kMinCommonNameAffixLength| to be removed. +OptionalNamePieces RemoveCommonAffixesIfPossible( + const NamePieces& field_names) { + // Updates the field name parsed by heuristics if several criteria are met. + // Several fields must be present in the form. + if (field_names.size() < kCommonNameAffixRemovalFieldNumberThreshold) + return base::nullopt; + + size_t longest_prefix_length = + FindLongestCommonAffixLength(field_names, false); + size_t longest_suffix_length = + FindLongestCommonAffixLength(field_names, true); + + // Don't remove the common affix if it's not long enough. + if (longest_prefix_length < kMinCommonNameAffixLength) + longest_prefix_length = 0; + + if (longest_suffix_length < kMinCommonNameAffixLength) + longest_suffix_length = 0; + + // If neither a common prefix of suffix was found return false. + if (longest_prefix_length == 0 && longest_suffix_length == 0) { + return base::nullopt; + } + + // Otherwise try to reduce the names. + return GetStrippedParseableNamesIfValid(field_names, longest_prefix_length, + longest_suffix_length, + /*minimal_string_length_to_strip=*/0); +} + +// Tries to remove common prefixes from |field_names| and returns the result. If +// neither a common prefix exists, or if one or more of the resulting strings is +// not a valid parseable name, |base::nullopt| is returned. +// The number of names in |field_names| must exceed +// |kCommonNamePrefixRemovalFieldThreshold| in order to make the prefix +// removal possible. Also, the length of a prefix must exceed +// |kMinCommonNamePrefixLength| to be removed. +OptionalNamePieces RemoveCommonPrefixIfPossible(const NamePieces& field_names) { + // Updates the field name parsed by heuristics if several criteria are met. + // Several fields must be present in the form. + if (field_names.size() < kCommonNamePrefixRemovalFieldThreshold) + return base::nullopt; + + size_t longest_prefix_length = + FindLongestCommonAffixLength(field_names, false); + + // Don't remove the common affix if it's not long enough. + if (longest_prefix_length < kMinCommonNamePrefixLength) + return base::nullopt; + + // Otherwise try to reduce the names. + return GetStrippedParseableNamesIfValid( + field_names, longest_prefix_length, /*offset_right=*/0, + /*minimal_string_length_to_strip=*/kMinCommonNamePrefixLength); +} + +// If possible, returns field names with a removed common prefix that is common +// to the subset of names in |field_names| with a minimal length of +// |kMinCommonNameLongPrefixLength|. +// The number of names in |field_names| must exceed +// |kCommonNamePrefixRemovalFieldThreshold| in order to make the prefix +// removal possible. Also, the length of a prefix must exceed +// |kMinCommonNameLongPrefixLength| to be removed. +OptionalNamePieces RemoveCommonPrefixForNamesWithMinimalLengthIfPossible( + const NamePieces& field_names) { + // Update the field name parsed by heuristics if several criteria are met. + // Several fields must be present in the form. + if (field_names.size() < kCommonNamePrefixRemovalFieldThreshold) + return base::nullopt; + + const size_t longest_prefix = + FindLongestCommonPrefixLengthInStringsWithMinimalLength( + field_names, kMinCommonNameLongPrefixLength); + if (longest_prefix < kMinCommonNameLongPrefixLength) { + return base::nullopt; + } + + return GetStrippedParseableNamesIfValid(field_names, longest_prefix, 0, + kMinCommonNameLongPrefixLength); +} + +std::vector<base::string16> GetParseableNames(const NamePieces& field_names) { + OptionalNamePieces parseable_names = base::nullopt; + + std::vector<base::string16> result; + result.reserve(field_names.size()); + + // If the feature is enabled, try to remove a common affix. If this is not + // possible try to remove lengthy prefixes that may be missing in short names. + if (base::FeatureList::IsEnabled(features::kAutofillLabelAffixRemoval)) { + // Try to remove both common suffixes and prefixes that are common to all + // fields. + parseable_names = RemoveCommonAffixesIfPossible(field_names); + if (!parseable_names.has_value()) { + // Remove prefix common to string that have a minimum length given by + // |kMinCommonNamePrefixLength|. + parseable_names = + RemoveCommonPrefixForNamesWithMinimalLengthIfPossible(field_names); + } + + } else { + parseable_names = RemoveCommonPrefixIfPossible(field_names); + } + + // If there are no parseable names after affix removal, return the original + // field names. + base::ranges::transform( + parseable_names.has_value() ? parseable_names.value() : field_names, + std::back_inserter(result), [](auto& s) { return base::string16(s); }); + + return result; +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_processing/name_processing_util.h b/chromium/components/autofill/core/browser/form_processing/name_processing_util.h new file mode 100644 index 00000000000..9500b42796a --- /dev/null +++ b/chromium/components/autofill/core/browser/form_processing/name_processing_util.h @@ -0,0 +1,64 @@ +// Copyright 2021 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PROCESSING_NAME_PROCESSING_UTIL_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PROCESSING_NAME_PROCESSING_UTIL_H_ + +#include <vector> + +#include "base/optional.h" +#include "base/strings/string_piece.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/autofill_regexes.h" +#include "components/autofill/core/browser/form_structure.h" + +namespace autofill { + +#ifdef UNIT_TEST +size_t FindLongestCommonAffixLength( + const std::vector<base::StringPiece16>& strings, + bool findCommonSuffix); + +bool IsValidParseableName(const base::StringPiece16 parseable_name); + +base::Optional<std::vector<base::StringPiece16>> RemoveCommonAffixesIfPossible( + const std::vector<base::StringPiece16>& field_names); + +size_t FindLongestCommonPrefixLengthInStringsWithMinimalLength( + const std::vector<base::StringPiece16>& strings, + size_t minimal_length); + +base::Optional<std::vector<base::StringPiece16>> +GetStrippedParseableNamesIfValid( + const std::vector<base::StringPiece16>& field_names, + size_t offset_left, + size_t offset_right, + size_t minimal_string_length_to_strip); + +base::Optional<std::vector<base::StringPiece16>> RemoveCommonPrefixIfPossible( + const std::vector<base::StringPiece16>& field_names); + +base::Optional<std::vector<base::StringPiece16>> +RemoveCommonPrefixForNamesWithMinimalLengthIfPossible( + const std::vector<base::StringPiece16>& field_names); +#endif + +// Determines and returns the parseable names for |field_names|. +// With the |kAutofillLabelAffixRemoval| feature enabled, first it is tried to +// remove a common affix from all names in |field_names|. If this is not +// possible, it is attempted to remove long prefixes from a subset of names in +// |field_names| which exceed a given length. If the +// |kAutofillLabelAffixRemoval| is disabled, a prefix removal is attempted. In +// any case, if a affix/prefix removal is not possible, the original names in +// |field_names| are returned. +// +// Beware, this function works on string pieces and therefore, it should not be +// called with temporary objects. Also, the underlying strings should not be +// modified before the last usage of the result. +std::vector<base::string16> GetParseableNames( + const std::vector<base::StringPiece16>& field_names); + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PROCESSING_NAME_PROCESSING_UTIL_H_ diff --git a/chromium/components/autofill/core/browser/form_processing/name_processing_util_unittest.cc b/chromium/components/autofill/core/browser/form_processing/name_processing_util_unittest.cc new file mode 100644 index 00000000000..4481d9a0240 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_processing/name_processing_util_unittest.cc @@ -0,0 +1,338 @@ +// Copyright 2021 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 "components/autofill/core/browser/form_processing/name_processing_util.h" + +#include "base/feature_list.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" +#include "components/autofill/core/common/autofill_features.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::ASCIIToUTF16; + +namespace { +std::vector<base::StringPiece16> StringsToStringPieces( + const std::vector<base::string16>& strings) { + std::vector<base::StringPiece16> string_pieces; + for (const auto& s : strings) { + string_pieces.emplace_back(base::StringPiece16(s)); + } + return string_pieces; +} +} // namespace + +namespace autofill { + +// Tests that the validity of parseable names is determined correctly. +TEST(NameProcessingUtil, IsValidParseableName) { + // Parseable name should not be empty. + EXPECT_FALSE(IsValidParseableName(ASCIIToUTF16(""))); + // Parseable name should not be solely numerical. + EXPECT_FALSE(IsValidParseableName(ASCIIToUTF16("1265125"))); + + // Valid parseable name cases. + EXPECT_TRUE(IsValidParseableName(ASCIIToUTF16("a23"))); + EXPECT_TRUE(IsValidParseableName(ASCIIToUTF16("*)&%@"))); +} + +// Tests that the correct length of prefixes and suffixes are returned. +TEST(NameProcessingUtil, FindLongestCommonAffixLength) { + auto String16ToStringPiece16 = [](std::vector<base::string16>& vin, + std::vector<base::StringPiece16>& vout) { + vout.clear(); + for (auto& str : vin) + vout.push_back(str); + }; + + // Normal prefix case. + std::vector<base::string16> strings; + std::vector<base::StringPiece16> stringPieces; + strings.push_back(ASCIIToUTF16("123456XXX123456789")); + strings.push_back(ASCIIToUTF16("12345678XXX012345678_foo")); + strings.push_back(ASCIIToUTF16("1234567890123456")); + strings.push_back(ASCIIToUTF16("1234567XXX901234567890")); + String16ToStringPiece16(strings, stringPieces); + size_t affixLength = FindLongestCommonAffixLength(stringPieces, false); + EXPECT_EQ(ASCIIToUTF16("123456").size(), affixLength); + + // Normal suffix case. + strings.clear(); + strings.push_back(ASCIIToUTF16("black and gold dress")); + strings.push_back(ASCIIToUTF16("work_address")); + strings.push_back(ASCIIToUTF16("123456XXX1234_home_address")); + strings.push_back(ASCIIToUTF16("1234567890123456_city_address")); + String16ToStringPiece16(strings, stringPieces); + affixLength = FindLongestCommonAffixLength(stringPieces, true); + EXPECT_EQ(ASCIIToUTF16("dress").size(), affixLength); + + // Handles no common prefix. + strings.clear(); + strings.push_back(ASCIIToUTF16("1234567890123456")); + strings.push_back(ASCIIToUTF16("4567890123456789")); + strings.push_back(ASCIIToUTF16("7890123456789012")); + String16ToStringPiece16(strings, stringPieces); + affixLength = FindLongestCommonAffixLength(stringPieces, false); + EXPECT_EQ(ASCIIToUTF16("").size(), affixLength); + + // Handles no common suffix. + strings.clear(); + strings.push_back(ASCIIToUTF16("1234567890123456")); + strings.push_back(ASCIIToUTF16("4567890123456789")); + strings.push_back(ASCIIToUTF16("7890123456789012")); + String16ToStringPiece16(strings, stringPieces); + affixLength = FindLongestCommonAffixLength(stringPieces, true); + EXPECT_EQ(ASCIIToUTF16("").size(), affixLength); + + // Only one string, prefix case. + strings.clear(); + strings.push_back(ASCIIToUTF16("1234567890")); + String16ToStringPiece16(strings, stringPieces); + affixLength = FindLongestCommonAffixLength(stringPieces, false); + EXPECT_EQ(ASCIIToUTF16("1234567890").size(), affixLength); + + // Only one string, suffix case. + strings.clear(); + strings.push_back(ASCIIToUTF16("1234567890")); + String16ToStringPiece16(strings, stringPieces); + affixLength = FindLongestCommonAffixLength(stringPieces, true); + EXPECT_EQ(ASCIIToUTF16("1234567890").size(), affixLength); + + // Empty vector, prefix case. + strings.clear(); + String16ToStringPiece16(strings, stringPieces); + affixLength = FindLongestCommonAffixLength(stringPieces, false); + EXPECT_EQ(ASCIIToUTF16("").size(), affixLength); + + // Empty vector, suffix case. + strings.clear(); + String16ToStringPiece16(strings, stringPieces); + affixLength = FindLongestCommonAffixLength(stringPieces, true); + EXPECT_EQ(ASCIIToUTF16("").size(), affixLength); +} + +// Tests the determination of the length of the longest common prefix for +// strings with a minimal length. +TEST(NameProcessingUtil, + FindLongestCommonPrefixLengthForStringsWithMinimalLength) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("aabbccddeeff")); + strings.push_back(ASCIIToUTF16("aabbccddeeffgg")); + strings.push_back(ASCIIToUTF16("zzz")); + strings.push_back(ASCIIToUTF16("aabbc___")); + EXPECT_EQ(FindLongestCommonPrefixLengthInStringsWithMinimalLength( + StringsToStringPieces(strings), 4), + 5U); + EXPECT_EQ(FindLongestCommonPrefixLengthInStringsWithMinimalLength( + StringsToStringPieces(strings), 3), + 0U); +} + +// Tests that a |base::nullopt| is returned if no common affix was removed. +TEST(NameProcessingUtil, RemoveCommonAffixesIfPossible_NotPossible) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("abc")); + strings.push_back(ASCIIToUTF16("def")); + strings.push_back(ASCIIToUTF16("abcd")); + strings.push_back(ASCIIToUTF16("abcdef")); + + EXPECT_EQ(RemoveCommonAffixesIfPossible(StringsToStringPieces(strings)), + base::nullopt); +} + +// Tests that both the prefix and the suffix are removed. +TEST(NameProcessingUtil, RemoveCommonAffixesIfPossible) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("abcaazzz")); + strings.push_back(ASCIIToUTF16("abcbbzzz")); + strings.push_back(ASCIIToUTF16("abccczzz")); + + std::vector<base::string16> expectation; + expectation.push_back(ASCIIToUTF16("aa")); + expectation.push_back(ASCIIToUTF16("bb")); + expectation.push_back(ASCIIToUTF16("cc")); + + EXPECT_EQ(RemoveCommonAffixesIfPossible(StringsToStringPieces(strings)), + StringsToStringPieces(expectation)); +} + +// Tests that a |base::nullopt| is returned if no common prefix was removed. +TEST(NameProcessingUtil, RemoveCommonPrefixIfPossible_NotPossible) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("abc")); + strings.push_back(ASCIIToUTF16("def")); + strings.push_back(ASCIIToUTF16("abcd")); + strings.push_back(ASCIIToUTF16("abcdef")); + + EXPECT_EQ(RemoveCommonPrefixIfPossible(StringsToStringPieces(strings)), + base::nullopt); +} + +// Tests that prefix is removed correctly. +TEST(NameProcessingUtil, RemoveCommonPrefixIfPossible) { + std::vector<base::string16> strings; + // The strings contain a long common prefix that can be removed. + strings.push_back(ASCIIToUTF16("ccccccccccccccccaazzz")); + strings.push_back(ASCIIToUTF16("ccccccccccccccccbbzzz")); + strings.push_back(ASCIIToUTF16("cccccccccccccccccczzz")); + + std::vector<base::string16> expectation; + expectation.push_back(ASCIIToUTF16("aazzz")); + expectation.push_back(ASCIIToUTF16("bbzzz")); + expectation.push_back(ASCIIToUTF16("cczzz")); + + EXPECT_EQ(RemoveCommonPrefixIfPossible(StringsToStringPieces(strings)), + StringsToStringPieces(expectation)); +} + +// Tests that prefix is removed correctly for fields with a minimal length. +TEST(NameProcessingUtil, + RemoveCommonPrefixForFieldsWithMinimalLengthIfPossible) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("ccccccccccccccccaazzz")); + // This name is too short to be considered and is skipped both in the + // detection of prefixes as well as in the removal. + strings.push_back(ASCIIToUTF16("abc")); + strings.push_back(ASCIIToUTF16("cccccccccccccccccczzz")); + + std::vector<base::string16> expectation; + expectation.push_back(ASCIIToUTF16("aazzz")); + expectation.push_back(ASCIIToUTF16("abc")); + expectation.push_back(ASCIIToUTF16("cczzz")); + + EXPECT_EQ(RemoveCommonPrefixForNamesWithMinimalLengthIfPossible( + StringsToStringPieces(strings)), + StringsToStringPieces(expectation)); +} + +// Tests that prefix is not removed because it is too short. +TEST(NameProcessingUtil, RemoveCommonPrefixIfPossible_TooShort) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("abcaazzz")); + strings.push_back(ASCIIToUTF16("abcbbzzz")); + strings.push_back(ASCIIToUTF16("abccczzz")); + + EXPECT_EQ(RemoveCommonPrefixIfPossible(StringsToStringPieces(strings)), + base::nullopt); +} + +// Tests that the strings are correctly stripped. +TEST(NameProcessingUtil, GetStrippedParseableNamesIfValid) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("abcaazzz")); + strings.push_back(ASCIIToUTF16("abcbbzzz")); + strings.push_back(ASCIIToUTF16("abccczzz")); + + std::vector<base::string16> expectation; + expectation.push_back(ASCIIToUTF16("aaz")); + expectation.push_back(ASCIIToUTF16("bbz")); + expectation.push_back(ASCIIToUTF16("ccz")); + + EXPECT_EQ( + GetStrippedParseableNamesIfValid(StringsToStringPieces(strings), 3, 2, 1), + StringsToStringPieces(expectation)); +} + +// Tests that a |base::nullopt| is returned if one of stripped names is not +// valid. +TEST(NameProcessingUtil, GetStrippedParseableNamesIfValid_NotValid) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("abcaazzz")); + // This string is not valid because only the "1" is left after stripping. + strings.push_back(ASCIIToUTF16("abc1zz")); + strings.push_back(ASCIIToUTF16("abccczzz")); + + std::vector<base::string16> expectation; + expectation.push_back(ASCIIToUTF16("aaz")); + expectation.push_back(ASCIIToUTF16("bbz")); + expectation.push_back(ASCIIToUTF16("ccz")); + + EXPECT_EQ( + GetStrippedParseableNamesIfValid(StringsToStringPieces(strings), 3, 2, 1), + base::nullopt); +} + +// Tests that the parseable names are returned correctly. +TEST(NameProcessingUtil, GetParseableNames_OnlyPrefix) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("abcaazzz1")); + strings.push_back(ASCIIToUTF16("abcbbzzz2")); + strings.push_back(ASCIIToUTF16("abccczzz3")); + + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillLabelAffixRemoval); + + // With the feature turned on, the prefix is removed. + std::vector<base::string16> expectation; + expectation.push_back(ASCIIToUTF16("aazzz1")); + expectation.push_back(ASCIIToUTF16("bbzzz2")); + expectation.push_back(ASCIIToUTF16("cczzz3")); + + EXPECT_EQ(GetParseableNames(StringsToStringPieces(strings)), expectation); +} + +// Tests that the parseable names are returned correctly. +TEST(NameProcessingUtil, GetParseableNames_OnlySuffix) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("1aazzz")); + strings.push_back(ASCIIToUTF16("2bbzzz")); + strings.push_back(ASCIIToUTF16("3cczzz")); + + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillLabelAffixRemoval); + + // With the feature turned on, the suffix is removed. + std::vector<base::string16> expectation; + expectation.push_back(ASCIIToUTF16("1aa")); + expectation.push_back(ASCIIToUTF16("2bb")); + expectation.push_back(ASCIIToUTF16("3cc")); + + EXPECT_EQ(GetParseableNames(StringsToStringPieces(strings)), expectation); +} + +// Tests that the parseable names are returned correctly. +TEST(NameProcessingUtil, GetParseableNames_Affix) { + std::vector<base::string16> strings; + strings.push_back(ASCIIToUTF16("abcaazzz")); + strings.push_back(ASCIIToUTF16("abcbbzzz")); + strings.push_back(ASCIIToUTF16("abccczzz")); + + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillLabelAffixRemoval); + + // With the feature turned on, the prefix and affix is removed. + std::vector<base::string16> expectation; + expectation.push_back(ASCIIToUTF16("aa")); + expectation.push_back(ASCIIToUTF16("bb")); + expectation.push_back(ASCIIToUTF16("cc")); + + EXPECT_EQ(GetParseableNames(StringsToStringPieces(strings)), expectation); + + scoped_feature_list.Reset(); + scoped_feature_list.InitAndDisableFeature( + features::kAutofillLabelAffixRemoval); + + // With the feature turned off, the names are too short for a prefix removal. + expectation.clear(); + expectation.push_back(ASCIIToUTF16("abcaazzz")); + expectation.push_back(ASCIIToUTF16("abcbbzzz")); + expectation.push_back(ASCIIToUTF16("abccczzz")); + EXPECT_EQ(GetParseableNames(StringsToStringPieces(strings)), expectation); + + // But very long prefixes are still removed. + strings.clear(); + strings.push_back(ASCIIToUTF16("1234567890ABCDEFGabcaazzz")); + strings.push_back(ASCIIToUTF16("1234567890ABCDEFGabcbbzzz")); + strings.push_back(ASCIIToUTF16("1234567890ABCDEFGabccczzz")); + + expectation.clear(); + expectation.push_back(ASCIIToUTF16("aazzz")); + expectation.push_back(ASCIIToUTF16("bbzzz")); + expectation.push_back(ASCIIToUTF16("cczzz")); + EXPECT_EQ(GetParseableNames(StringsToStringPieces(strings)), expectation); +} +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc index 9bd72190efa..9a13c2ba9be 100644 --- a/chromium/components/autofill/core/browser/form_structure.cc +++ b/chromium/components/autofill/core/browser/form_structure.cc @@ -16,6 +16,7 @@ #include "base/base64.h" #include "base/command_line.h" +#include "base/containers/fixed_flat_map.h" #include "base/feature_list.h" #include "base/i18n/case_conversion.h" #include "base/logging.h" @@ -38,6 +39,8 @@ #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_parsing/field_candidates.h" #include "components/autofill/core/browser/form_parsing/form_field.h" +#include "components/autofill/core/browser/form_processing/label_processing_util.h" +#include "components/autofill/core/browser/form_processing/name_processing_util.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/browser/randomized_encoder.h" #include "components/autofill/core/browser/rationalization_util.h" @@ -49,6 +52,7 @@ #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/autofill_util.h" +#include "components/autofill/core/common/dense_set.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_data_predictions.h" #include "components/autofill/core/common/form_field_data.h" @@ -71,29 +75,6 @@ constexpr char kShippingMode[] = "shipping"; // Default section name for the fields. constexpr char kDefaultSection[] = "-default"; -// Only removing common name prefixes if we have a minimum number of fields and -// a minimum prefix length. These values are chosen to avoid cases such as two -// fields with "address1" and "address2" and be effective against web frameworks -// which prepend prefixes such as "ctl01$ctl00$MainContentRegion$" on all -// fields. -constexpr int kCommonNamePrefixRemovalFieldThreshold = 3; -constexpr int kMinCommonNamePrefixLength = 16; - -// Affix removal configuration. Only remove short affixes if they are common -// to all field names and there is at least the minimum number of fields. -// If no affix common to all field names is found, search for a long -// prefix common to a subset of the fields. This case helps include cases of -// prefixes prepended by web frameworks. -// -// Minimum required number of available fields for trying to remove affixes. -constexpr int kCommonNameAffixRemovalFieldNumberThreshold = 3; -// Minimum required length for affixes common to all field names. -constexpr int kMinCommonNameAffixLength = 3; -// Minimum required length for prefixes common to a subset of the field names. -constexpr int kMinCommonNameLongPrefixLength = 16; -// Regex for checking if |parseable_name| is valid after stripping affixes. -constexpr char kParseableNameValidationRe[] = "\\D"; - // Returns true if the scheme given by |url| is one for which autofill is // allowed to activate. By default this only returns true for HTTP and HTTPS. bool HasAllowedScheme(const GURL& url) { @@ -580,18 +561,65 @@ void EncodeFieldMetadataForQuery(const FormFieldData& field, // For example, for Autofill to support fields of type // "PHONE_HOME_COUNTRY_CODE", there would need to be at least one other field // of type "PHONE_HOME_NUMBER" or "PHONE_HOME_CITY_AND_NUMBER". -const std::unordered_map<ServerFieldType, ServerFieldTypeSet>& -GetTypeRelationshipMap() { - // Initialized and cached on first use. - static const auto* const rules = - new std::unordered_map<ServerFieldType, ServerFieldTypeSet>( +const auto& GetTypeRelationshipMap() { + static const auto rules = + base::MakeFixedFlatMap<ServerFieldType, ServerFieldTypeSet>( {{PHONE_HOME_COUNTRY_CODE, {PHONE_HOME_NUMBER, PHONE_HOME_CITY_AND_NUMBER}}}); - return *rules; + return rules; } } // namespace +class FormStructure::SectionedFieldsIndexes { + public: + SectionedFieldsIndexes() = default; + ~SectionedFieldsIndexes() = default; + + size_t LastFieldIndex() const { + if (sectioned_indexes_.empty()) + return std::numeric_limits<size_t>::max(); // Shouldn't happen. + return sectioned_indexes_.back().back(); + } + + void AddFieldIndex(const size_t index, bool is_new_section) { + if (is_new_section || Empty()) { + sectioned_indexes_.emplace_back(); + } + sectioned_indexes_.back().push_back(index); + } + + void WalkForwardToTheNextSection() { current_section_ptr_++; } + + bool IsFinished() const { + return current_section_ptr_ >= sectioned_indexes_.size(); + } + + size_t CurrentIndex() const { + return current_section_ptr_ < sectioned_indexes_.size() + ? sectioned_indexes_[current_section_ptr_].front() + : std::numeric_limits<size_t>::max(); + } + + const std::vector<size_t>* CurrentSection() const { + return current_section_ptr_ < sectioned_indexes_.size() + ? §ioned_indexes_[current_section_ptr_] + : nullptr; + } + + void Reset() { current_section_ptr_ = 0; } + + bool Empty() const { return sectioned_indexes_.empty(); } + + private: + // A vector of sections. Each section is a vector of some of the indexes + // that belong to the same section. The sections and indexes are sorted by + // their order of appearance on the form. + std::vector<std::vector<size_t>> sectioned_indexes_; + // Points to a vector of indexes that belong to the same section. + size_t current_section_ptr_ = 0; +}; + FormStructure::FormStructure(const FormData& form) : id_attribute_(form.id_attribute), name_attribute_(form.name_attribute), @@ -644,23 +672,24 @@ FormStructure::FormStructure( FormStructure::~FormStructure() = default; -void FormStructure::DetermineHeuristicTypes(LogManager* log_manager) { +void FormStructure::DetermineHeuristicTypes( + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger, + LogManager* log_manager) { const auto determine_heuristic_types_start_time = AutofillTickClock::NowTicks(); // First, try to detect field types based on each field's |autocomplete| // attribute value. - if (!was_parsed_for_autocomplete_attributes_) - ParseFieldTypesFromAutocompleteAttributes(); + ParseFieldTypesFromAutocompleteAttributes(); // Then if there are enough active fields, and if we are dealing with either a // proper <form> or a <form>-less checkout, run the heuristics and server // prediction routines. if (ShouldRunHeuristics()) { const FieldCandidatesMap field_type_map = FormField::ParseFormFields( - fields_, page_language_, is_form_tag_, log_manager); + fields_, current_page_language_, is_form_tag_, log_manager); for (const auto& field : fields_) { - const auto iter = field_type_map.find(field->unique_name()); + const auto iter = field_type_map.find(field->unique_renderer_id); if (iter != field_type_map.end()) { field->set_heuristic_type(iter->second.BestHeuristicType()); } @@ -687,6 +716,10 @@ void FormStructure::DetermineHeuristicTypes(LogManager* log_manager) { 1 << AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT; } + if (base::FeatureList::IsEnabled( + features::kAutofillParsingPatternsLanguageDetection)) { + RationalizeRepeatedFields(form_interactions_ukm_logger); + } RationalizeFieldTypePredictions(); AutofillMetrics::LogDetermineHeuristicTypesTiming( @@ -698,6 +731,7 @@ bool FormStructure::EncodeUploadRequest( bool form_was_autofilled, const std::string& login_form_signature, bool observed_submission, + bool is_raw_metadata_uploading_enabled, AutofillUploadContents* upload, std::vector<FormSignature>* encoded_signatures) const { DCHECK(AllTypesCaptured(*this, available_field_types)); @@ -711,8 +745,8 @@ bool FormStructure::EncodeUploadRequest( upload->set_data_present(EncodeFieldTypes(available_field_types)); upload->set_passwords_revealed(passwords_were_revealed_); upload->set_has_form_tag(is_form_tag_); - if (!page_language_.empty() && randomized_encoder_ != nullptr) { - upload->set_language(page_language_); + if (!current_page_language_->empty() && randomized_encoder_ != nullptr) { + upload->set_language(current_page_language_.value()); } auto triggering_event = (submission_event_ != SubmissionIndicatorEvent::NONE) @@ -730,7 +764,7 @@ bool FormStructure::EncodeUploadRequest( upload); } - if (IsAutofillFieldMetadataEnabled()) { + if (is_raw_metadata_uploading_enabled) { upload->set_action_signature(StrToHash64Bit(target_url_.host())); if (!form_name().empty()) upload->set_form_name(base::UTF16ToUTF8(form_name())); @@ -750,7 +784,8 @@ bool FormStructure::EncodeUploadRequest( if (IsMalformed()) return false; // Malformed form, skip it. - EncodeFormForUpload(upload, encoded_signatures); + EncodeFormForUpload(is_raw_metadata_uploading_enabled, upload, + encoded_signatures); return true; } @@ -864,6 +899,12 @@ void FormStructure::ProcessQueryResponse( std::vector<AutofillQueryResponse::FormSuggestion::FieldSuggestion:: FieldPrediction> server_predictions; + + if (current_field.has_primary_type_prediction()) { + field->set_server_type_prediction_is_override( + current_field.primary_type_prediction_is_override()); + } + if (current_field.predictions_size() == 0) { AutofillQueryResponse::FormSuggestion::FieldSuggestion::FieldPrediction field_prediction; @@ -890,6 +931,8 @@ void FormStructure::ProcessQueryResponse( form->UpdateAutofillCount(); form->RationalizeRepeatedFields(form_interactions_ukm_logger); form->RationalizeFieldTypePredictions(); + // TODO(crbug.com/1154080): By calling this with false, autocomplete section + // attributes will be ignored. form->IdentifySections(false); } @@ -935,13 +978,6 @@ std::vector<FormDataPredictions> FormStructure::GetFieldTypePredictions( return forms; } -// static -bool FormStructure::IsAutofillFieldMetadataEnabled() { - const std::string group_name = - base::FieldTrialList::FindFullName("AutofillFieldMetadata"); - return base::StartsWith(group_name, "Enabled", base::CompareCase::SENSITIVE); -} - std::unique_ptr<FormStructure> FormStructure::CreateForPasswordManagerUpload( FormSignature form_signature, const std::vector<FieldSignature>& field_signatures) { @@ -1123,7 +1159,17 @@ void FormStructure::RetrieveFromCache( } } field->set_server_type(cached_field->server_type()); + field->set_server_type_prediction_is_override( + cached_field->server_type_prediction_is_override()); field->set_previously_autofilled(cached_field->previously_autofilled()); + + // Only retrieve an overall prediction from cache if a server prediction + // is set. + if (base::FeatureList::IsEnabled( + features::kAutofillRetrieveOverallPredictionsFromCache) && + field->server_type() != NO_SERVER_DATA) { + field->SetTypeTo(cached_field->Type()); + } } } @@ -1153,6 +1199,8 @@ void FormStructure::LogQualityMetrics( size_t num_detected_field_types = 0; size_t num_edited_autofilled_fields = 0; + size_t num_of_accepted_autofilled_fields = 0; + size_t num_of_corrected_autofilled_fields = 0; bool did_autofill_all_possible_fields = true; bool did_autofill_some_possible_fields = false; bool is_for_credit_card = IsCompleteCreditCardForm(); @@ -1196,6 +1244,13 @@ void FormStructure::LogQualityMetrics( } ++num_detected_field_types; + + // Count the number of autofilled and corrected fields. + if (field->is_autofilled) + ++num_of_accepted_autofilled_fields; + else if (field->previously_autofilled()) + ++num_of_corrected_autofilled_fields; + if (field->is_autofilled) did_autofill_some_possible_fields = true; else if (!field->only_fill_when_focused()) @@ -1234,6 +1289,11 @@ void FormStructure::LogQualityMetrics( AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS; } + // Log the number of autofilled fields at submission time. + AutofillMetrics::LogNumberOfAutofilledFieldsAtSubmission( + num_of_accepted_autofilled_fields, + num_of_corrected_autofilled_fields); + // Unlike the other times, the |submission_time| should always be // available. DCHECK(!submission_time.is_null()); @@ -1284,6 +1344,9 @@ void FormStructure::LogQualityMetricsBasedOnAutocomplete( } void FormStructure::ParseFieldTypesFromAutocompleteAttributes() { + if (was_parsed_for_autocomplete_attributes_) + return; + has_author_specified_types_ = false; has_author_specified_sections_ = false; has_author_specified_upi_vpa_hint_ = false; @@ -1456,17 +1519,13 @@ FormData FormStructure::ToFormData() const { data.is_formless_checkout = is_formless_checkout_; data.unique_renderer_id = unique_renderer_id_; - for (size_t i = 0; i < fields_.size(); ++i) { - data.fields.push_back(FormFieldData(*fields_[i])); + for (const auto& field : fields_) { + data.fields.push_back(*field); } return data; } -FormStructure::SectionedFieldsIndexes::SectionedFieldsIndexes() {} - -FormStructure::SectionedFieldsIndexes::~SectionedFieldsIndexes() {} - void FormStructure::RationalizeCreditCardFieldPredictions() { bool cc_first_name_found = false; bool cc_last_name_found = false; @@ -1644,16 +1703,17 @@ void FormStructure::RationalizeAddressLineFields( for (sections_of_address_indexes->Reset(); !sections_of_address_indexes->IsFinished(); sections_of_address_indexes->WalkForwardToTheNextSection()) { - auto current_section = sections_of_address_indexes->CurrentSection(); + auto* current_section = sections_of_address_indexes->CurrentSection(); // The rationalization only applies to sections that have 2 or 3 visible // street address predictions. - if (current_section.size() != 2 && current_section.size() != 3) { + if (!current_section || + (current_section->size() != 2 && current_section->size() != 3)) { continue; } int nb_address_rationalized = 0; - for (auto field_index : current_section) { + for (auto field_index : *current_section) { switch (nb_address_rationalized) { case 0: ApplyRationalizationsToFieldAndLog(field_index, ADDRESS_HOME_LINE1, @@ -1758,7 +1818,7 @@ bool FormStructure::FieldShouldBeRationalizedToCountry(size_t upper_index) { for (int field_index = upper_index - 1; field_index >= 0; --field_index) { if (fields_[field_index]->IsVisible() && AutofillType(fields_[field_index]->Type().GetStorableType()).group() == - ADDRESS_HOME && + FieldTypeGroup::kAddressHome && fields_[field_index]->section == fields_[upper_index]->section) { return false; } @@ -1784,10 +1844,6 @@ void FormStructure::RationalizeAddressStateCountry( while (!sections_of_state_indexes->IsFinished() || !sections_of_country_indexes->IsFinished()) { - auto current_section_of_state_indexes = - sections_of_state_indexes->CurrentSection(); - auto current_section_of_country_indexes = - sections_of_country_indexes->CurrentSection(); // If there are still sections left with both country and state type, and // state and country current sections are equal, then that section has both // state and country. No rationalization needed. @@ -1800,22 +1856,33 @@ void FormStructure::RationalizeAddressStateCountry( continue; } - size_t upper_index = 0, lower_index = 0; + size_t upper_index = 0; + size_t lower_index = 0; + + auto* current_section_of_state_indexes = + sections_of_state_indexes->CurrentSection(); + auto* current_section_of_country_indexes = + sections_of_country_indexes->CurrentSection(); + DCHECK(current_section_of_state_indexes || + current_section_of_country_indexes); // If country section is before the state ones, it means that that section // misses states, and the other way around. - if (current_section_of_state_indexes < current_section_of_country_indexes) { + if (!current_section_of_country_indexes || + (current_section_of_state_indexes && + *current_section_of_state_indexes < + *current_section_of_country_indexes)) { // We only rationalize when we have exactly two visible fields of a kind. - if (current_section_of_state_indexes.size() == 2) { - upper_index = current_section_of_state_indexes[0]; - lower_index = current_section_of_state_indexes[1]; + if (current_section_of_state_indexes->size() == 2) { + upper_index = (*current_section_of_state_indexes)[0]; + lower_index = (*current_section_of_state_indexes)[1]; } sections_of_state_indexes->WalkForwardToTheNextSection(); } else { // We only rationalize when we have exactly two visible fields of a kind. - if (current_section_of_country_indexes.size() == 2) { - upper_index = current_section_of_country_indexes[0]; - lower_index = current_section_of_country_indexes[1]; + if (current_section_of_country_indexes->size() == 2) { + upper_index = (*current_section_of_country_indexes)[0]; + lower_index = (*current_section_of_country_indexes)[1]; } sections_of_country_indexes->WalkForwardToTheNextSection(); } @@ -1855,24 +1922,25 @@ void FormStructure::RationalizeRepeatedFields( // indexes of fields whose types are predicted as FULL_NAME by the server. SectionedFieldsIndexes sectioned_field_indexes_by_type[MAX_VALID_FIELD_TYPE]; - for (const auto& field : fields_) { + for (size_t i = 0; i < fields_.size(); ++i) { + const AutofillField& field = *fields_[i]; // The hidden fields are not considered when rationalizing. - if (!field->IsVisible()) + if (!field.IsVisible()) continue; // The billing and non-billing types are aggregated. - auto current_type = field->Type().GetStorableType(); + auto current_type = field.Type().GetStorableType(); if (current_type != UNKNOWN_TYPE && current_type < MAX_VALID_FIELD_TYPE) { // Look at the sectioned field indexes for the current type, if the // current field belongs to that section, then the field index should be // added to that same section, otherwise, start a new section. sectioned_field_indexes_by_type[current_type].AddFieldIndex( - &field - &fields_[0], + i, /*is_new_section*/ sectioned_field_indexes_by_type[current_type] .Empty() || fields_[sectioned_field_indexes_by_type[current_type] .LastFieldIndex()] - ->section != field->section); + ->section != field.section); } } @@ -1890,19 +1958,7 @@ void FormStructure::RationalizeRepeatedFields( void FormStructure::RationalizeFieldTypePredictions() { RationalizeCreditCardFieldPredictions(); for (const auto& field : fields_) { - if (base::FeatureList::IsEnabled(features::kAutofillOffNoServerData) && - !field->should_autocomplete && field->server_type() == NO_SERVER_DATA && - field->heuristic_type() != CREDIT_CARD_VERIFICATION_CODE) { - // When the field has autocomplete off, and the server returned no - // prediction, then assume Autofill is not useful for the current field. - // Special case for CVC (crbug.com/968036). We never send votes for CVC - // fields, but we still fill them when the user inputs them via the CVC - // prompt. Since Autofill doesn't trigger from a CVC field, we can keep - // the client-side predictions for this type. - field->SetTypeTo(AutofillType(UNKNOWN_TYPE)); - } else { - field->SetTypeTo(field->Type()); - } + field->SetTypeTo(field->Type()); } RationalizeTypeRelationships(); } @@ -1930,17 +1986,11 @@ void FormStructure::EncodeFormForQuery( if (is_rich_query_enabled_) { EncodeFieldMetadataForQuery(*field, added_field->mutable_metadata()); } - - if (IsAutofillFieldMetadataEnabled()) { - added_field->set_control_type(field->form_control_type); - - if (!field->name.empty()) - added_field->set_name(base::UTF16ToUTF8(field->name)); - } } } void FormStructure::EncodeFormForUpload( + bool is_raw_metadata_uploading_enabled, AutofillUploadContents* upload, std::vector<FormSignature>* encoded_signatures) const { DCHECK(!IsMalformed()); @@ -2003,7 +2053,7 @@ void FormStructure::EncodeFormForUpload( added_field->mutable_randomized_field_metadata()); } - if (IsAutofillFieldMetadataEnabled()) { + if (is_raw_metadata_uploading_enabled) { added_field->set_type(field->form_control_type); if (!field->name.empty()) @@ -2035,12 +2085,192 @@ bool FormStructure::IsMalformed() const { return false; } +void FormStructure::IdentifySectionsWithNewMethod() { + if (fields_.empty()) + return; + + const bool is_enabled_autofill_redundant_name_sectioning = + base::FeatureList::IsEnabled( + features::kAutofillSectionUponRedundantNameInfo); + + // Creates a unique name for the section that starts with |field|. + // TODO(crbug/896689): Cleanup once experiment is launched. + auto get_section_name = [](const AutofillField& field) { + if (base::FeatureList::IsEnabled( + features::kAutofillNameSectionsWithRendererIds)) { + return base::StrCat( + {field.name, base::ASCIIToUTF16("_"), + base::NumberToString16(field.unique_renderer_id.value())}); + } else { + return field.unique_name(); + } + }; + + base::string16 current_section = get_section_name(*fields_.front()); + + // Keep track of the types we've seen in this section. + ServerFieldTypeSet seen_types; + ServerFieldType previous_type = UNKNOWN_TYPE; + + // Boolean flag that is set to true when a field in the current section + // has the autocomplete-section attribute defined. + bool previous_autocomplete_section_present = false; + + bool is_hidden_section = false; + base::string16 last_visible_section; + for (const auto& field : fields_) { + const ServerFieldType current_type = field->Type().GetStorableType(); + // All credit card fields belong to the same section that's different + // from address sections. + if (AutofillType(current_type).group() == FieldTypeGroup::kCreditCard) { + field->section = "credit-card"; + continue; + } + + bool already_saw_current_type = seen_types.count(current_type) > 0; + + // Forms often ask for multiple phone numbers -- e.g. both a daytime and + // evening phone number. Our phone number detection is also generally a + // little off. Hence, ignore this field type as a signal here. + if (AutofillType(current_type).group() == FieldTypeGroup::kPhoneHome) + already_saw_current_type = false; + + if (is_enabled_autofill_redundant_name_sectioning) { + // Forms sometimes have a different format of inputting names in + // different sections. If we believe a new name is being entered, assume + // it is a new section (unless there are two identical inputs in a row). + if (current_type == NAME_FULL) + already_saw_current_type |= (seen_types.count(NAME_LAST) > 0); + } + + bool ignored_field = !field->IsVisible(); + + // This is the first visible field after a hidden section. Consider it as + // the continuation of the last visible section. + if (!ignored_field && is_hidden_section) { + current_section = last_visible_section; + } + + // Start a new section by an ignored field, only if the next field is also + // already seen. + size_t field_index = &field - &fields_[0]; + if (ignored_field && + (is_hidden_section || + !((field_index + 1) < fields_.size() && + seen_types.count( + fields_[field_index + 1]->Type().GetStorableType()) > 0))) { + already_saw_current_type = false; + } + + // Some forms have adjacent fields of the same type. Two common examples: + // * Forms with two email fields, where the second is meant to "confirm" + // the first. + // * Forms with a <select> menu for states in some countries, and a + // freeform <input> field for states in other countries. (Usually, + // only one of these two will be visible for any given choice of + // country.) + // Generally, adjacent fields of the same type belong in the same logical + // section. + if (current_type == previous_type) + already_saw_current_type = false; + + // Boolean flag that is set to true when the |field| has + // autocomplete-section attribute defined. + bool autocomplete_section_attribute_present = + (field->section != kDefaultSection); + + // Boolean flag that is set to true when the |field| has + // autocomplete-section attribute defined and is different that the + // previous field. + bool different_autocomplete_section_than_previous = + (autocomplete_section_attribute_present && + (!field_index || fields_[field_index - 1]->section != field->section)); + + // Start a new section if the |current_type| was already seen or the + // autocomplete-section attribute is defined for the |field| which is + // different than the previous field. + if (current_type != UNKNOWN_TYPE && + (already_saw_current_type || + different_autocomplete_section_than_previous)) { + // Keep track of seen_types if the new section is hidden. The next + // visible section might be the continuation of the previous visible + // section. + if (ignored_field) { + is_hidden_section = true; + last_visible_section = current_section; + } + + if (!is_hidden_section && (!autocomplete_section_attribute_present || + different_autocomplete_section_than_previous)) + seen_types.clear(); + + if (autocomplete_section_attribute_present && + !previous_autocomplete_section_present) { + // If this field is the first field within the section with a defined + // autocomplete section, then change the section attribute of all the + // parsed fields in the current section to |field->section|. + int i = static_cast<int>(field_index - 1); + while (i >= 0 && + base::UTF8ToUTF16(fields_[i]->section) == current_section) { + fields_[i]->section = field->section; + i--; + } + } + + // The end of a section, so start a new section. + current_section = get_section_name(*field); + + // The section described in the autocomplete section attribute + // overrides the value determined by the heuristic. + if (autocomplete_section_attribute_present) + current_section = base::UTF8ToUTF16(field->section); + + previous_autocomplete_section_present = + autocomplete_section_attribute_present; + } + + // Only consider a type "seen" if it was not ignored. Some forms have + // sections for different locales, only one of which is enabled at a + // time. Each section may duplicate some information (e.g. postal code) + // and we don't want that to cause section splits. + // Also only set |previous_type| when the field was not ignored. This + // prevents ignored fields from breaking up fields that are otherwise + // adjacent. + if (!ignored_field) { + seen_types.insert(current_type); + previous_type = current_type; + is_hidden_section = false; + } + + field->section = base::UTF16ToUTF8(current_section); + } + + // Ensure that credit card and address fields are in separate sections. + // This simplifies the section-aware logic in autofill_manager.cc. + for (const auto& field : fields_) { + FieldTypeGroup field_type_group = field->Type().group(); + if (field_type_group == FieldTypeGroup::kCreditCard) + field->section = field->section + "-cc"; + else + field->section = field->section + "-default"; + } + + // Since this function has changed the sections, subsequent calls to + // ParseFieldTypesFromAutocompleteAttributes(), which modifies the + // sections, too, should not be no-ops. + was_parsed_for_autocomplete_attributes_ = false; +} + +// TODO(crbug/1153539): Make sectioning less stateful, less std::string-based. void FormStructure::IdentifySections(bool has_author_specified_sections) { if (fields_.empty()) return; - const bool is_enabled_autofill_new_sectioning = - base::FeatureList::IsEnabled(features::kAutofillUseNewSectioningMethod); + if (base::FeatureList::IsEnabled(features::kAutofillUseNewSectioningMethod)) { + IdentifySectionsWithNewMethod(); + return; + } + const bool is_enabled_autofill_redundant_name_sectioning = base::FeatureList::IsEnabled( features::kAutofillSectionUponRedundantNameInfo); @@ -2058,24 +2288,20 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { } }; - if (!has_author_specified_sections || is_enabled_autofill_new_sectioning) { + if (!has_author_specified_sections) { base::string16 current_section = get_section_name(*fields_.front()); // Keep track of the types we've seen in this section. - std::set<ServerFieldType> seen_types; + ServerFieldTypeSet seen_types; ServerFieldType previous_type = UNKNOWN_TYPE; - // Boolean flag that is set to true when a field in the current section - // has the autocomplete-section attribute defined. - bool previous_autocomplete_section_present = false; - bool is_hidden_section = false; base::string16 last_visible_section; for (const auto& field : fields_) { const ServerFieldType current_type = field->Type().GetStorableType(); // All credit card fields belong to the same section that's different // from address sections. - if (AutofillType(current_type).group() == CREDIT_CARD) { + if (AutofillType(current_type).group() == FieldTypeGroup::kCreditCard) { field->section = "credit-card"; continue; } @@ -2085,7 +2311,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { // Forms often ask for multiple phone numbers -- e.g. both a daytime and // evening phone number. Our phone number detection is also generally a // little off. Hence, ignore this field type as a signal here. - if (AutofillType(current_type).group() == PHONE_HOME) + if (AutofillType(current_type).group() == FieldTypeGroup::kPhoneHome) already_saw_current_type = false; if (is_enabled_autofill_redundant_name_sectioning) { @@ -2127,31 +2353,10 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { if (current_type == previous_type) already_saw_current_type = false; - // Boolean flag that is set to true when the |field| has - // autocomplete-section attribute defined. - bool autocomplete_section_attribute_present = false; - if (is_enabled_autofill_new_sectioning) - autocomplete_section_attribute_present = - (field->section != kDefaultSection); - - // Boolean flag that is set to true when the |field| has - // autocomplete-section attribute defined and is different that the - // previous field. - bool different_autocomplete_section_than_previous = false; - if (is_enabled_autofill_new_sectioning) { - different_autocomplete_section_than_previous = - (autocomplete_section_attribute_present && - (!field_index || - fields_[field_index - 1]->section != field->section)); - } - // Start a new section if the |current_type| was already seen or the // autocomplete-section attribute is defined for the |field| which is // different than the previous field. - if (current_type != UNKNOWN_TYPE && - (already_saw_current_type || - (is_enabled_autofill_new_sectioning && - different_autocomplete_section_than_previous))) { + if (current_type != UNKNOWN_TYPE && already_saw_current_type) { // Keep track of seen_types if the new section is hidden. The next // visible section might be the continuation of the previous visible // section. @@ -2160,38 +2365,11 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { last_visible_section = current_section; } - if (!is_hidden_section && - (!is_enabled_autofill_new_sectioning || - !autocomplete_section_attribute_present || - different_autocomplete_section_than_previous)) + if (!is_hidden_section) seen_types.clear(); - if (is_enabled_autofill_new_sectioning && - autocomplete_section_attribute_present && - !previous_autocomplete_section_present) { - // If this field is the first field within the section with a defined - // autocomplete section, then change the section attribute of all the - // parsed fields in the current section to |field->section|. - int i = static_cast<int>(field_index - 1); - while (i >= 0 && - base::UTF8ToUTF16(fields_[i]->section) == current_section) { - fields_[i]->section = field->section; - i--; - } - } - // The end of a section, so start a new section. current_section = get_section_name(*field); - - if (is_enabled_autofill_new_sectioning) { - // The section described in the autocomplete section attribute - // overrides the value determined by the heuristic. - if (autocomplete_section_attribute_present) - current_section = base::UTF8ToUTF16(field->section); - - previous_autocomplete_section_present = - autocomplete_section_attribute_present; - } } // Only consider a type "seen" if it was not ignored. Some forms have @@ -2215,11 +2393,16 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { // This simplifies the section-aware logic in autofill_manager.cc. for (const auto& field : fields_) { FieldTypeGroup field_type_group = field->Type().group(); - if (field_type_group == CREDIT_CARD) + if (field_type_group == FieldTypeGroup::kCreditCard) field->section = field->section + "-cc"; else field->section = field->section + "-default"; } + + // Since this function has changed the sections, subsequent calls to + // ParseFieldTypesFromAutocompleteAttributes(), which modifies the + // sections, too, should not be no-ops. + was_parsed_for_autocomplete_attributes_ = false; } bool FormStructure::ShouldSkipField(const FormFieldData& field) const { @@ -2227,183 +2410,71 @@ bool FormStructure::ShouldSkipField(const FormFieldData& field) const { } void FormStructure::ProcessExtractedFields() { - if (base::FeatureList::IsEnabled( - autofill::features::kAutofillLabelAffixRemoval)) { - // Updates the field name parsed by heuristics if several criteria are met. - // Several fields must be present in the form. - if (field_count() < kCommonNameAffixRemovalFieldNumberThreshold) - return; - - std::vector<base::StringPiece16> names; - names.reserve(field_count()); - for (const auto& field : *this) - names.push_back(field->name); - - int longest_prefix_length = FindLongestCommonAffixLength(names, false); - int longest_suffix_length = FindLongestCommonAffixLength(names, true); - - // Don't remove the common affix if it's not long enough. - if (longest_prefix_length < kMinCommonNameAffixLength) - longest_prefix_length = 0; - - if (longest_suffix_length < kMinCommonNameAffixLength) - longest_suffix_length = 0; - - bool success = - SetStrippedParseableNames(longest_prefix_length, longest_suffix_length); - - // Don't search for inconsistent prefix if valid affixes are found. - if (success && longest_prefix_length + longest_suffix_length > 0) - return; + // Extracts the |parseable_name_| by removing common affixes from the + // field names. + ExtractParseableFieldNames(); - // Functionality for stripping a prefix only common to a subset - // of field names. - // This is needed because an exceptional field may be missing a prefix - // which is otherwise consistently applied--for instance, a framework - // may only apply a prefix to those fields which are bound when POSTing. - names.clear(); - for (const auto& field : *this) - if (field->name.size() > kMinCommonNameLongPrefixLength) - names.push_back(field->name); - - if (names.size() < kCommonNamePrefixRemovalFieldThreshold) - return; - - const int longest_long_prefix_length = - FindLongestCommonAffixLength(names, false); - - if (longest_long_prefix_length >= kMinCommonNameLongPrefixLength) - SetStrippedParseableNames(longest_long_prefix_length, 0); - - return; + // TODO(crbug/1165780): Remove once shared labels are launched. + if (base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForParsingWithSharedLabels)) { + // Extracts the |parsable_label_| for each field. + ExtractParseableFieldLabels(); } +} - // Update the field name parsed by heuristics if several criteria are met. - // Several fields must be present in the form. - if (field_count() < kCommonNamePrefixRemovalFieldThreshold) - return; - - // Find the longest common prefix within all the field names. - std::vector<base::string16> names; - names.reserve(field_count()); - for (const auto& field : *this) - names.push_back(field->name); +void FormStructure::ExtractParseableFieldLabels() { + std::vector<base::StringPiece16> field_labels; + field_labels.reserve(field_count()); + for (const auto& field : *this) { + // Skip fields that are not a text input or not visible. + if (!field->IsTextInputElement() || !field->IsVisible()) { + continue; + } + field_labels.push_back(field->label); + } - const base::string16 longest_prefix = FindLongestCommonPrefix(names); - if (longest_prefix.size() < kMinCommonNamePrefixLength) + // Determine the parsable labels and write them back. + base::Optional<std::vector<base::string16>> parsable_labels = + GetParseableLabels(field_labels); + // If not single label was split, the function can return, because the + // |parsable_label_| is assigned to |label| by default. + if (!parsable_labels.has_value()) { return; - - // The name without the prefix will be used for heuristics parsing. - for (auto& field : *this) { - if (field->name.size() > longest_prefix.size()) { - field->set_parseable_name( - field->name.substr(longest_prefix.size(), field->name.size())); - } } -} -bool FormStructure::SetStrippedParseableNames(size_t offset_left, - size_t offset_right) { - // Keeps track if all stripped strings are valid according to - // |IsValidParseableName()|. If at least one string is invalid, - // all |parseable_name| are reset to |name|. - bool should_keep = true; + size_t idx = 0; for (auto& field : *this) { - // This check allows to only strip affixes from long enough strings. - if (field->name.size() > offset_right + offset_left) { - field->set_parseable_name(field->name.substr( - offset_left, field->name.size() - offset_right - offset_left)); - } else { - field->set_parseable_name(field->name); + if (!field->IsTextInputElement() || !field->IsVisible()) { + // For those fields, set the original label. + field->set_parseable_label(field->label); + continue; } - - should_keep &= IsValidParseableName(field->parseable_name()); - if (!should_keep) - break; + DCHECK(idx < parsable_labels->size()); + field->set_parseable_label(parsable_labels->at(idx++)); } - - // Reset if some stripped string was invalid. - if (!should_keep) { - for (auto& field : *this) - field->set_parseable_name(field->name); - } - - return should_keep; } -bool FormStructure::IsValidParseableName( - base::string16 candidateParseableName) { - static const base::string16 kParseableNameValidationPattern = - base::UTF8ToUTF16(kParseableNameValidationRe); - if (MatchesPattern(candidateParseableName, kParseableNameValidationPattern)) - return true; - - return false; -} - -// static -size_t FormStructure::FindLongestCommonAffixLength( - const std::vector<base::StringPiece16>& strings, - bool findCommonSuffix) { - if (strings.empty()) - return 0; - - // Go through each character of the first string until there is a mismatch at - // the same position in any other string. Adapted from http://goo.gl/YGukMM. - for (size_t affix_len = 0; affix_len < strings[0].size(); affix_len++) { - size_t base_string_index = - findCommonSuffix ? strings[0].size() - affix_len - 1 : affix_len; - for (size_t i = 1; i < strings.size(); i++) { - size_t compared_string_index = - findCommonSuffix ? strings[i].size() - affix_len - 1 : affix_len; - if (affix_len >= strings[i].size() || - strings[i][compared_string_index] != strings[0][base_string_index]) { - // Mismatch found. - return affix_len; - } - } +void FormStructure::ExtractParseableFieldNames() { + // Create a vector of string pieces containing the field names. + std::vector<base::StringPiece16> names; + names.reserve(field_count()); + for (const auto& field : *this) { + names.push_back(base::StringPiece16(field->name)); } - return strings[0].size(); -} -// static -base::string16 FormStructure::FindLongestCommonPrefix( - const std::vector<base::string16>& strings) { - if (strings.empty()) - return base::string16(); - - std::vector<base::string16> filtered_strings; - - // Any strings less than kMinCommonNamePrefixLength are neither modified - // nor considered when processing for a common prefix. - std::copy_if( - strings.begin(), strings.end(), std::back_inserter(filtered_strings), - [](base::string16 s) { return s.size() >= kMinCommonNamePrefixLength; }); - - if (filtered_strings.empty()) - return base::string16(); - - // Go through each character of the first string until there is a mismatch at - // the same position in any other string. Adapted from http://goo.gl/YGukMM. - for (size_t prefix_len = 0; prefix_len < filtered_strings[0].size(); - prefix_len++) { - for (size_t i = 1; i < filtered_strings.size(); i++) { - if (prefix_len >= filtered_strings[i].size() || - filtered_strings[i].at(prefix_len) != - filtered_strings[0].at(prefix_len)) { - // Mismatch found. - return filtered_strings[i].substr(0, prefix_len); - } - } + // Determine the parseable names and write them into the corresponding field. + std::vector<base::string16> parseable_names = GetParseableNames(names); + DCHECK_EQ(parseable_names.size(), field_count()); + size_t idx = 0; + for (auto& field : *this) { + field->set_parseable_name(parseable_names.at(idx++)); } - return filtered_strings[0]; } -std::set<FormType> FormStructure::GetFormTypes() const { - std::set<FormType> form_types; +DenseSet<FormType> FormStructure::GetFormTypes() const { + DenseSet<FormType> form_types; for (const auto& field : fields_) { - form_types.insert( - FormTypes::FieldTypeGroupToFormType(field->Type().group())); + form_types.insert(FieldTypeGroupToFormType(field->Type().group())); } return form_types; } @@ -2425,7 +2496,7 @@ void FormStructure::set_randomized_encoder( void FormStructure::RationalizeTypeRelationships() { // Create a local set of all the types for faster lookup. - std::unordered_set<ServerFieldType> types; + ServerFieldTypeSet types; for (const auto& field : fields_) { types.insert(field->Type().GetStorableType()); } @@ -2434,21 +2505,11 @@ void FormStructure::RationalizeTypeRelationships() { for (const auto& field : fields_) { ServerFieldType field_type = field->Type().GetStorableType(); - const auto& ruleset_iterator = type_relationship_rules.find(field_type); + const auto* ruleset_iterator = type_relationship_rules.find(field_type); if (ruleset_iterator != type_relationship_rules.end()) { // We have relationship rules for this type. Verify that at least one of // the required related type is present. - bool found = false; - for (ServerFieldType required_type : ruleset_iterator->second) { - if (types.find(required_type) != types.end()) { - // Found a required type, we can break as we only need one required - // type to respect the rule. - found = true; - break; - } - } - - if (!found) { + if (!types.contains_any(ruleset_iterator->second)) { // No required type was found, the current field failed the relationship // requirements for its type. Disabling Autofill for this field. field->SetTypeTo(AutofillType(UNKNOWN_TYPE)); diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h index 58a8a8d48dc..8151d28eb09 100644 --- a/chromium/components/autofill/core/browser/form_structure.h +++ b/chromium/components/autofill/core/browser/form_structure.h @@ -24,6 +24,8 @@ #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_types.h" #include "components/autofill/core/browser/proto/api_v1.pb.h" +#include "components/autofill/core/common/dense_set.h" +#include "components/autofill/core/common/language_code.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" #include "components/autofill/core/common/renderer_id.h" #include "url/gurl.h" @@ -66,7 +68,9 @@ class FormStructure { // Runs several heuristics against the form fields to determine their possible // types. - void DetermineHeuristicTypes(LogManager* log_manager = nullptr); + void DetermineHeuristicTypes( + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger, + LogManager* log_manager); // Encodes the proto |upload| request from this FormStructure, and stores // the (single) FormSignature and the signatures of the fields to be uploaded @@ -78,6 +82,7 @@ class FormStructure { bool form_was_autofilled, const std::string& login_form_signature, bool observed_submission, + bool is_raw_metadata_uploading_enabled, autofill::AutofillUploadContents* upload, std::vector<FormSignature>* encoded_signatures) const; @@ -104,9 +109,6 @@ class FormStructure { static std::vector<FormDataPredictions> GetFieldTypePredictions( const std::vector<FormStructure*>& form_structures); - // Returns whether sending autofill field metadata to the server is enabled. - static bool IsAutofillFieldMetadataEnabled(); - // Creates FormStructure that has bare minimum information for uploading // votes, namely form and field signatures. Warning: do not use for Autofill // code, since it is likely missing some fields. @@ -281,7 +283,7 @@ class FormStructure { FormData ToFormData() const; // Returns the possible form types. - std::set<FormType> GetFormTypes() const; + DenseSet<FormType> GetFormTypes() const; bool passwords_were_revealed() const { return passwords_were_revealed_; } void set_passwords_were_revealed(bool passwords_were_revealed) { @@ -359,16 +361,20 @@ class FormStructure { // - Name for Autofill of first field base::string16 GetIdentifierForRefill() const; - int developer_engagement_metrics() { return developer_engagement_metrics_; } + int developer_engagement_metrics() const { + return developer_engagement_metrics_; + } void set_randomized_encoder(std::unique_ptr<RandomizedEncoder> encoder); void set_is_rich_query_enabled(bool v) { is_rich_query_enabled_ = v; } - const std::string& page_language() const { return page_language_; } + const LanguageCode& current_page_language() const { + return current_page_language_; + } - void set_page_language(std::string language) { - page_language_ = std::move(language); + void set_current_page_language(LanguageCode language) { + current_page_language_ = std::move(language); } bool value_from_dynamic_change_form() const { @@ -406,51 +412,9 @@ class FormStructure { FRIEND_TEST_ALL_PREFIXES(ParameterizedFormStructureTest, RationalizePhoneNumber_RunsOncePerSection); - class SectionedFieldsIndexes { - public: - SectionedFieldsIndexes(); - ~SectionedFieldsIndexes(); - - size_t LastFieldIndex() const { - if (sectioned_indexes.empty()) - return (size_t)-1; // Shouldn't happen. - return sectioned_indexes.back().back(); - } - - void AddFieldIndex(const size_t index, bool is_new_section) { - if (is_new_section || Empty()) { - sectioned_indexes.push_back(std::vector<size_t>(1, index)); - return; - } - sectioned_indexes.back().push_back(index); - } - - void WalkForwardToTheNextSection() { current_section_ptr++; } - - bool IsFinished() const { - return current_section_ptr >= sectioned_indexes.size(); - } - - size_t CurrentIndex() const { return CurrentSection()[0]; } - - std::vector<size_t> CurrentSection() const { - if (current_section_ptr < sectioned_indexes.size()) - return sectioned_indexes[current_section_ptr]; - return std::vector<size_t>(1, (size_t)-1); // To handle edge cases. - } - - void Reset() { current_section_ptr = 0; } - - bool Empty() const { return sectioned_indexes.empty(); } - - private: - // A vector of sections. Each section is a vector of some of the indexes - // that belong to the same section. The sections and indexes are sorted by - // their order of appearance on the form. - std::vector<std::vector<size_t>> sectioned_indexes; - // Points to a vector of indexes that belong to the same section. - size_t current_section_ptr = 0; - }; + // This class wraps a vector of vectors of field indices. The indices of a + // vector belong to the same group. + class SectionedFieldsIndexes; // Parses the field types from the server query response. |forms| must be the // same as the one passed to EncodeQueryRequest when constructing the query. @@ -531,6 +495,7 @@ class FormStructure { std::vector<FormSignature>* queried_form_signatures) const; void EncodeFormForUpload( + bool is_raw_metadata_uploading_enabled, autofill::AutofillUploadContents* upload, std::vector<FormSignature>* encoded_signatures) const; @@ -547,6 +512,7 @@ class FormStructure { // If |has_author_specified_sections| is true, only the second pass -- // distinguishing credit card sections from non-credit card ones -- is made. void IdentifySections(bool has_author_specified_sections); + void IdentifySectionsWithNewMethod(); // Returns true if field should be skipped when talking to Autofill server. bool ShouldSkipField(const FormFieldData& field) const; @@ -554,38 +520,16 @@ class FormStructure { // Further processes the extracted |fields_|. void ProcessExtractedFields(); - // Tries to set |parseable_name| fields by stripping the given offsets from - // both sides of the |name| fields. - // Sets |parseable_name| to |name| if the sum of offsets is bigger than - // |name|. - // Sets all |parseable_name| to |name| without modification and returns - // false if a name fails the |IsValidParseableName()| check after stripping. - bool SetStrippedParseableNames(size_t offset_left, size_t offset_right); - - // Returns true if |string| is a valid parseable_name. Current criterion - // is the |autofill::kParseableNameValidationRe| regex. - static bool IsValidParseableName(base::string16 string); - - // Returns the length of the longest common prefix found within |strings| - // if |findCommonSuffix| is false. Otherwise returns longest common suffix. - static size_t FindLongestCommonAffixLength( - const std::vector<base::StringPiece16>& strings, - bool findCommonSuffix = false); - - // Returns the longest common prefix found within |strings|. Strings below a - // threshold length defined by |kMinCommonNamePrefixLength| are excluded - // when performing this check; this is needed because an exceptional - // field may be missing a prefix which is otherwise consistently applied. - // For instance, a framework may only apply a prefix to those fields - // which are bound when POSTing. - // - // Soon to be replaced by FindLongestCommonPrefixLength - static base::string16 FindLongestCommonPrefix( - const std::vector<base::string16>& strings); - - // The language detected for this form's page, prior to any translations + // Extracts the parseable field name by removing a common affix. + void ExtractParseableFieldNames(); + + // Extract parseable field labels by potentially splitting labels between + // adjacent fields. + void ExtractParseableFieldLabels(); + + // The language detected for this form's page, before any translations // performed by Chrome. - std::string page_language_; + LanguageCode current_page_language_; // The id attribute of the form. base::string16 id_attribute_; diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc index 4e894075277..fc22851c9c7 100644 --- a/chromium/components/autofill/core/browser/form_structure_unittest.cc +++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc @@ -11,7 +11,6 @@ #include "base/base64.h" #include "base/command_line.h" #include "base/feature_list.h" -#include "base/metrics/field_trial.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -19,7 +18,6 @@ #include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_form_test_utils.h" #include "components/autofill/core/browser/autofill_test_utils.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" #include "components/autofill/core/browser/proto/api_v1.pb.h" #include "components/autofill/core/browser/randomized_encoder.h" #include "components/autofill/core/common/autofill_features.h" @@ -66,6 +64,19 @@ void AddFieldSuggestionToForm( field_suggestion->set_primary_type_prediction(field_type); } +void AddFieldOverrideToForm( + ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion, + autofill::FormFieldData field_data, + ServerFieldType field_type) { + AddFieldSuggestionToForm(form_suggestion, field_data, field_type); + + DCHECK_GT(form_suggestion->field_suggestions().size(), 0); + form_suggestion + ->mutable_field_suggestions(form_suggestion->field_suggestions().size() - + 1) + ->set_primary_type_prediction_is_override(true); +} + } // namespace class FormStructureTestImpl : public test::FormStructureTest { @@ -74,28 +85,14 @@ class FormStructureTestImpl : public test::FormStructureTest { return base::NumberToString(StrToHash64Bit(str)); } - void SetUp() override { - // By default this trial is enabled on tests. - EnableAutofillMetadataFieldTrial(); - } - protected: - void InitFeature(base::test::ScopedFeatureList* feature_list, - const base::Feature& feature, - bool is_enabled) { - if (is_enabled) - feature_list->InitAndEnableFeature(feature); - else - feature_list->InitAndDisableFeature(feature); - } - bool FormShouldBeParsed(const FormData form) { return FormStructure(form).ShouldBeParsed(); } bool FormIsAutofillable(const FormData& form) { FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); return form_structure.IsAutofillable(); } @@ -107,12 +104,6 @@ class FormStructureTestImpl : public test::FormStructureTest { return FormStructure(form).ShouldBeQueried(); } - void DisableAutofillMetadataFieldTrial() { - field_trial_ = nullptr; - scoped_feature_list_.Reset(); - scoped_feature_list_.Init(); - } - void SetUpForEncoder() { scoped_feature_list_.Reset(); scoped_feature_list_.InitWithFeatures( @@ -126,21 +117,9 @@ class FormStructureTestImpl : public test::FormStructureTest { return FieldRendererId(++id_counter_); } - protected: - TestPatternProvider test_pattern_provider_; - private: - void EnableAutofillMetadataFieldTrial() { - scoped_feature_list_.Reset(); - scoped_feature_list_.Init(); - field_trial_ = base::FieldTrialList::CreateFieldTrial( - "AutofillFieldMetadata", "Enabled"); - field_trial_->group(); - } - uint32_t id_counter_ = 10; base::test::ScopedFeatureList scoped_feature_list_; - scoped_refptr<base::FieldTrial> field_trial_; }; class ParameterizedFormStructureTest @@ -638,27 +617,32 @@ TEST_F(FormStructureTestImpl, StripCommonNameAffix) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("ctl01$ctl00$ShippingAddressCreditPhone$firstname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("ctl01$ctl00$ShippingAddressCreditPhone$lastname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("ctl01$ctl00$ShippingAddressCreditPhone$email"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); field.name = ASCIIToUTF16("ctl01$ctl00$ShippingAddressCreditPhone$phone"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = base::string16(); field.name = ASCIIToUTF16("ctl01$ctl00$ShippingAddressCreditPhone$submit"); field.form_control_type = "submit"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -1101,7 +1085,7 @@ TEST_F(FormStructureTestImpl, // Default configuration. { FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ASSERT_EQ(2U, form_structure.field_count()); ASSERT_EQ(0U, form_structure.autofill_count()); EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type()); @@ -1147,7 +1131,7 @@ TEST_F(FormStructureTestImpl, FormData form_copy = form; form_copy.fields.pop_back(); FormStructure form_structure(form_copy); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); ASSERT_EQ(1U, form_structure.field_count()); ASSERT_EQ(1U, form_structure.autofill_count()); EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type()); @@ -1192,7 +1176,7 @@ TEST_F(FormStructureTestImpl, PasswordFormShouldBeQueried) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure.has_password_field()); EXPECT_TRUE(form_structure.ShouldBeQueried()); EXPECT_TRUE(form_structure.ShouldBeUploaded()); @@ -1257,7 +1241,7 @@ TEST_F(FormStructureTestImpl, HeuristicsAutocompleteAttributeWithSections) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure.IsAutofillable()); // Expect the correct number of fields. @@ -1312,7 +1296,7 @@ TEST_F(FormStructureTestImpl, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); // Expect the correct number of fields. ASSERT_EQ(6U, form_structure.field_count()); @@ -1346,7 +1330,7 @@ TEST_F(FormStructureTestImpl, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); // Expect the correct number of fields. ASSERT_EQ(2U, form_structure.field_count()); @@ -1392,7 +1376,7 @@ TEST_F(FormStructureTestImpl, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); // Expect the correct number of fields. ASSERT_EQ(4U, form_structure.field_count()); @@ -1466,7 +1450,7 @@ TEST_F(FormStructureTestImpl, HeuristicsSample8) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(10U, form_structure->field_count()); ASSERT_EQ(9U, form_structure->autofill_count()); @@ -1540,7 +1524,7 @@ TEST_F(FormStructureTestImpl, HeuristicsSample6) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(7U, form_structure->field_count()); ASSERT_EQ(6U, form_structure->autofill_count()); @@ -1614,7 +1598,7 @@ TEST_F(FormStructureTestImpl, HeuristicsLabelsOnly) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(8U, form_structure->field_count()); ASSERT_EQ(7U, form_structure->autofill_count()); @@ -1678,7 +1662,7 @@ TEST_F(FormStructureTestImpl, HeuristicsCreditCardInfo) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(6U, form_structure->field_count()); ASSERT_EQ(5U, form_structure->autofill_count()); @@ -1746,7 +1730,7 @@ TEST_F(FormStructureTestImpl, HeuristicsCreditCardInfoWithUnknownCardField) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(7U, form_structure->field_count()); ASSERT_EQ(5U, form_structure->autofill_count()); @@ -1798,7 +1782,7 @@ TEST_F(FormStructureTestImpl, ThreeAddressLines) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(4U, form_structure->field_count()); ASSERT_EQ(4U, form_structure->autofill_count()); @@ -1843,7 +1827,7 @@ TEST_F(FormStructureTestImpl, SurplusAddressLinesIgnored) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); ASSERT_EQ(4U, form_structure->field_count()); ASSERT_EQ(3U, form_structure->autofill_count()); @@ -1891,7 +1875,7 @@ TEST_F(FormStructureTestImpl, ThreeAddressLinesExpedia) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(4U, form_structure->field_count()); EXPECT_EQ(4U, form_structure->autofill_count()); @@ -1933,7 +1917,7 @@ TEST_F(FormStructureTestImpl, TwoAddressLinesEbay) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(3U, form_structure->field_count()); ASSERT_EQ(3U, form_structure->autofill_count()); @@ -1970,7 +1954,7 @@ TEST_F(FormStructureTestImpl, HeuristicsStateWithProvince) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(3U, form_structure->field_count()); ASSERT_EQ(3U, form_structure->autofill_count()); @@ -2048,7 +2032,7 @@ TEST_F(FormStructureTestImpl, HeuristicsWithBilling) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(11U, form_structure->field_count()); ASSERT_EQ(11U, form_structure->autofill_count()); @@ -2102,7 +2086,7 @@ TEST_F(FormStructureTestImpl, ThreePartPhoneNumber) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(4U, form_structure->field_count()); ASSERT_EQ(4U, form_structure->autofill_count()); @@ -2151,7 +2135,7 @@ TEST_F(FormStructureTestImpl, HeuristicsInfernoCC) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -2212,7 +2196,7 @@ TEST_F(FormStructureTestImpl, HeuristicsInferCCNames_NamesNotFirst) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -2277,7 +2261,7 @@ TEST_F(FormStructureTestImpl, HeuristicsInferCCNames_NamesFirst) { form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -2355,16 +2339,11 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest) { AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); - test::FillQueryField(query_form->add_fields(), 412125936U, "name_on_card", - "text"); - test::FillQueryField(query_form->add_fields(), 1917667676U, "billing_address", - "text"); - test::FillQueryField(query_form->add_fields(), 2226358947U, "card_number", - "text"); - test::FillQueryField(query_form->add_fields(), 747221617U, "expiration_month", - "text"); - test::FillQueryField(query_form->add_fields(), 4108155786U, "expiration_year", - "text"); + query_form->add_fields()->set_signature(412125936U); + query_form->add_fields()->set_signature(1917667676U); + query_form->add_fields()->set_signature(2226358947U); + query_form->add_fields()->set_signature(747221617U); + query_form->add_fields()->set_signature(4108155786U); std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); @@ -2413,19 +2392,13 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest) { query_form = query.add_forms(); query_form->set_signature(form_structure3.form_signature().value()); - test::FillQueryField(query_form->add_fields(), 412125936U, "name_on_card", - "text"); - test::FillQueryField(query_form->add_fields(), 1917667676U, "billing_address", - "text"); - test::FillQueryField(query_form->add_fields(), 2226358947U, "card_number", - "text"); - test::FillQueryField(query_form->add_fields(), 747221617U, "expiration_month", - "text"); - test::FillQueryField(query_form->add_fields(), 4108155786U, "expiration_year", - "text"); + query_form->add_fields()->set_signature(412125936U); + query_form->add_fields()->set_signature(1917667676U); + query_form->add_fields()->set_signature(2226358947U); + query_form->add_fields()->set_signature(747221617U); + query_form->add_fields()->set_signature(4108155786U); for (int i = 0; i < 5; ++i) { - test::FillQueryField(query_form->add_fields(), 509334676U, "address", - "text"); + query_form->add_fields()->set_signature(509334676U); } ASSERT_TRUE(query.SerializeToString(&expected_query_string)); @@ -2580,7 +2553,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { form.is_form_tag = true; form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -2700,7 +2673,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload, + available_field_types, false, std::string(), true, true, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -2713,7 +2686,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { AutofillUploadContents encoded_upload2; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload2, + available_field_types, true, std::string(), true, true, &encoded_upload2, &signatures)); encoded_upload2.SerializeToString(&encoded_upload_string); @@ -2768,7 +2741,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { AutofillUploadContents encoded_upload3; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload3, + available_field_types, false, std::string(), true, true, &encoded_upload3, &signatures)); encoded_upload3.SerializeToString(&encoded_upload_string); @@ -2786,7 +2759,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { form.url = GURL("http://www.foo.com/"); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -2904,7 +2877,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload, + available_field_types, false, std::string(), true, true, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -2924,7 +2897,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { form.is_form_tag = true; form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -3045,7 +3018,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload, + available_field_types, false, std::string(), true, true, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -3062,7 +3035,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { form.is_form_tag = true; form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -3180,7 +3153,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload, + available_field_types, false, std::string(), true, true, &encoded_upload, &signatures)); EXPECT_EQ(signatures, expected_signatures); @@ -3194,7 +3167,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { AutofillUploadContents encoded_upload2; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload2, + available_field_types, true, std::string(), true, true, &encoded_upload2, &signatures)); EXPECT_EQ(signatures, expected_signatures); @@ -3252,7 +3225,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { AutofillUploadContents encoded_upload3; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload3, + available_field_types, false, std::string(), true, true, &encoded_upload3, &signatures)); EXPECT_EQ(signatures, expected_signatures); @@ -3283,7 +3256,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { AutofillUploadContents encoded_upload4; EXPECT_FALSE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload4, + available_field_types, false, std::string(), true, true, &encoded_upload4, &signatures)); } @@ -3297,7 +3270,7 @@ TEST_F(FormStructureTestImpl, form.is_form_tag = true; form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.label = ASCIIToUTF16("First Name"); @@ -3418,7 +3391,8 @@ TEST_F(FormStructureTestImpl, AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, "42", true, &encoded_upload, &signatures)); + available_field_types, true, "42", true, true, &encoded_upload, + &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3434,7 +3408,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithAutocomplete) { form.is_form_tag = true; form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -3507,7 +3481,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithAutocomplete) { AutofillUploadContents encoded_upload; std::vector<FormSignature> signatures; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload, + available_field_types, true, std::string(), true, true, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -3516,8 +3490,6 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithAutocomplete) { } TEST_F(FormStructureTestImpl, EncodeUploadRequestWithPropertiesMask) { - DisableAutofillMetadataFieldTrial(); - std::unique_ptr<FormStructure> form_structure; std::vector<ServerFieldTypeSet> possible_field_types; std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities; @@ -3526,7 +3498,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestWithPropertiesMask) { form.is_form_tag = true; form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -3617,7 +3589,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestWithPropertiesMask) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload, + available_field_types, true, std::string(), true, + /*is_raw_metadata_uploading_enabled=*/false, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -3634,7 +3607,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_ObservedSubmissionFalse) { form.is_form_tag = true; form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -3708,7 +3681,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_ObservedSubmissionFalse) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( available_field_types, true, std::string(), - /* observed_submission= */ false, &encoded_upload, &signatures)); + /* observed_submission= */ false, true, &encoded_upload, &signatures)); std::string encoded_upload_string; encoded_upload.SerializeToString(&encoded_upload_string); @@ -3724,7 +3697,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithLabels) { form.is_form_tag = true; form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -3790,7 +3763,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithLabels) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload, + available_field_types, true, std::string(), true, true, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -3878,7 +3851,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithCssClassesAndIds) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload, + available_field_types, true, std::string(), true, true, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -3898,7 +3871,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithFormName) { // Setting the form name which we expect to see in the upload. form.name = ASCIIToUTF16("myform"); form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -3963,7 +3936,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithFormName) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload, + available_field_types, true, std::string(), true, true, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -3980,7 +3953,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestPartialMetadata) { form.is_form_tag = true; form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -4053,7 +4026,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestPartialMetadata) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload, + available_field_types, true, std::string(), true, true, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -4062,9 +4035,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestPartialMetadata) { } // Sending field metadata to the server is disabled. -TEST_F(FormStructureTestImpl, EncodeUploadRequest_DisabledMetadataTrial) { - DisableAutofillMetadataFieldTrial(); - +TEST_F(FormStructureTestImpl, EncodeUploadRequest_DisabledMetadata) { + // Metadata uploading is disabled by a parameter of |EncodeUploadRequest|. std::unique_ptr<FormStructure> form_structure; std::vector<ServerFieldTypeSet> possible_field_types; std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities; @@ -4073,7 +4045,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_DisabledMetadataTrial) { form.is_form_tag = true; form_structure = std::make_unique<FormStructure>(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); FormFieldData field; field.form_control_type = "text"; @@ -4154,7 +4126,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_DisabledMetadataTrial) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, true, std::string(), true, &encoded_upload, + available_field_types, true, std::string(), true, + /*is_raw_metadata_uploading_enabled=*/false, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -4235,7 +4208,7 @@ TEST_F(FormStructureTestImpl, CheckDataPresence) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure.EncodeUploadRequest(available_field_types, false, - std::string(), true, + std::string(), true, true, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -4266,7 +4239,7 @@ TEST_F(FormStructureTestImpl, CheckDataPresence) { AutofillUploadContents encoded_upload2; EXPECT_TRUE(form_structure.EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload2, + available_field_types, false, std::string(), true, true, &encoded_upload2, &signatures)); encoded_upload2.SerializeToString(&encoded_upload_string); @@ -4320,7 +4293,7 @@ TEST_F(FormStructureTestImpl, CheckDataPresence) { AutofillUploadContents encoded_upload3; EXPECT_TRUE(form_structure.EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload3, + available_field_types, false, std::string(), true, true, &encoded_upload3, &signatures)); encoded_upload3.SerializeToString(&encoded_upload_string); @@ -4352,7 +4325,7 @@ TEST_F(FormStructureTestImpl, CheckDataPresence) { AutofillUploadContents encoded_upload4; EXPECT_TRUE(form_structure.EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload4, + available_field_types, false, std::string(), true, true, &encoded_upload4, &signatures)); encoded_upload4.SerializeToString(&encoded_upload_string); @@ -4420,7 +4393,7 @@ TEST_F(FormStructureTestImpl, CheckDataPresence) { AutofillUploadContents encoded_upload5; EXPECT_TRUE(form_structure.EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload5, + available_field_types, false, std::string(), true, true, &encoded_upload5, &signatures)); encoded_upload5.SerializeToString(&encoded_upload_string); @@ -4530,7 +4503,7 @@ TEST_F(FormStructureTestImpl, CheckMultipleTypes) { AutofillUploadContents encoded_upload; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload, + available_field_types, false, std::string(), true, true, &encoded_upload, &signatures)); std::string encoded_upload_string; @@ -4554,7 +4527,7 @@ TEST_F(FormStructureTestImpl, CheckMultipleTypes) { AutofillUploadContents encoded_upload2; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload2, + available_field_types, false, std::string(), true, true, &encoded_upload2, &signatures)); encoded_upload2.SerializeToString(&encoded_upload_string); @@ -4572,7 +4545,7 @@ TEST_F(FormStructureTestImpl, CheckMultipleTypes) { AutofillUploadContents encoded_upload3; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload3, + available_field_types, false, std::string(), true, true, &encoded_upload3, &signatures)); encoded_upload3.SerializeToString(&encoded_upload_string); @@ -4598,7 +4571,7 @@ TEST_F(FormStructureTestImpl, CheckMultipleTypes) { AutofillUploadContents encoded_upload4; EXPECT_TRUE(form_structure->EncodeUploadRequest( - available_field_types, false, std::string(), true, &encoded_upload4, + available_field_types, false, std::string(), true, true, &encoded_upload4, &signatures)); encoded_upload4.SerializeToString(&encoded_upload_string); @@ -4633,7 +4606,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_PasswordsRevealed) { EXPECT_TRUE(form_structure.EncodeUploadRequest( {{}} /* available_field_types */, false /* form_was_autofilled */, std::string() /* login_form_signature */, true /* observed_submission */, - &upload, &signatures)); + true /* is_raw_metadata_uploading_enabled */, &upload, &signatures)); EXPECT_EQ(true, upload.passwords_revealed()); } @@ -4657,7 +4630,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_IsFormTag) { EXPECT_TRUE(form_structure.EncodeUploadRequest( {{}} /* available_field_types */, false /* form_was_autofilled */, std::string() /* login_form_signature */, - true /* observed_submission */, &upload, &signatures)); + true /* observed_submission */, + false /* is_raw_metadata_uploading_enabled */, &upload, &signatures)); EXPECT_EQ(is_form_tag, upload.has_form_tag()); } } @@ -4714,7 +4688,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_RichMetadata) { ASSERT_TRUE(form_structure.EncodeUploadRequest( {{}} /* available_field_types */, false /* form_was_autofilled */, std::string() /* login_form_signature */, true /* observed_submission */, - &upload, &signatures)); + false /* is_raw_metadata_uploading_enabled */, &upload, &signatures)); const auto form_signature = form_structure.form_signature(); @@ -4857,7 +4831,7 @@ TEST_F(FormStructureTestImpl, Metadata_OnlySendFullUrlWithUserConsent) { form_structure.set_randomized_encoder(RandomizedEncoder::Create(&prefs)); AutofillUploadContents upload = AutofillUploadContents(); std::vector<FormSignature> signatures; - form_structure.EncodeUploadRequest({}, true, "", true, &upload, + form_structure.EncodeUploadRequest({}, true, "", true, true, &upload, &signatures); EXPECT_EQ(has_consent, upload.randomized_form_metadata().has_url()); @@ -5010,9 +4984,8 @@ TEST_F(FormStructureTestImpl, SkipFieldTest) { AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); - test::FillQueryField(query_form->add_fields(), 239111655U, "username", - "text"); - test::FillQueryField(query_form->add_fields(), 420638584U, "email", "text"); + query_form->add_fields()->set_signature(239111655U); + query_form->add_fields()->set_signature(420638584U); std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); @@ -5066,11 +5039,9 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_WithLabels) { AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); - test::FillQueryField(query_form->add_fields(), 239111655U, "username", - "text"); - test::FillQueryField(query_form->add_fields(), 420638584U, "email", "text"); - test::FillQueryField(query_form->add_fields(), 2051817934U, "password", - "password"); + query_form->add_fields()->set_signature(239111655U); + query_form->add_fields()->set_signature(420638584U); + query_form->add_fields()->set_signature(2051817934U); std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); @@ -5125,11 +5096,9 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_WithLongLabels) { AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); - test::FillQueryField(query_form->add_fields(), 239111655U, "username", - "text"); - test::FillQueryField(query_form->add_fields(), 420638584U, "email", "text"); - test::FillQueryField(query_form->add_fields(), 2051817934U, "password", - "password"); + query_form->add_fields()->set_signature(239111655U); + query_form->add_fields()->set_signature(420638584U); + query_form->add_fields()->set_signature(2051817934U); std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); @@ -5177,9 +5146,8 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_MissingNames) { AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); - test::FillQueryField(query_form->add_fields(), 239111655U, "username", - "text"); - test::FillQueryField(query_form->add_fields(), 1318412689U, nullptr, "text"); + query_form->add_fields()->set_signature(239111655U); + query_form->add_fields()->set_signature(1318412689U); std::string expected_query_string; ASSERT_TRUE(query.SerializeToString(&expected_query_string)); @@ -5196,59 +5164,6 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_MissingNames) { EXPECT_EQ(expected_query_string, encoded_query_string); } -// Sending field metadata to the server is disabled. -TEST_F(FormStructureTestImpl, EncodeQueryRequest_DisabledMetadataTrial) { - DisableAutofillMetadataFieldTrial(); - - FormData form; - // No name set for the form. - form.url = GURL("http://cool.com"); - form.action = form.url.Resolve("/login"); - - FormFieldData field; - field.label = ASCIIToUTF16("username"); - field.name = ASCIIToUTF16("username"); - field.form_control_type = "text"; - field.unique_renderer_id = MakeFieldRendererId(); - form.fields.push_back(field); - - field.label = base::string16(); - field.name = ASCIIToUTF16("country"); - field.form_control_type = "text"; - field.check_status = FormFieldData::CheckStatus::kNotCheckable; - field.unique_renderer_id = MakeFieldRendererId(); - form.fields.push_back(field); - - FormStructure form_structure(form); - std::vector<FormStructure*> forms; - forms.push_back(&form_structure); - std::vector<FormSignature> encoded_signatures; - AutofillPageQueryRequest encoded_query; - - // Create the expected query and serialize it to a string. - AutofillPageQueryRequest query; - query.set_client_version(GetProductNameAndVersionForUserAgent()); - AutofillPageQueryRequest::Form* query_form = query.add_forms(); - query_form->set_signature(form_structure.form_signature().value()); - - test::FillQueryField(query_form->add_fields(), 239111655U, nullptr, nullptr); - test::FillQueryField(query_form->add_fields(), 3654076265U, nullptr, nullptr); - - std::string expected_query_string; - ASSERT_TRUE(query.SerializeToString(&expected_query_string)); - - const FormSignature kExpectedSignature(7635954436925888745UL); - - ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_query, - &encoded_signatures)); - ASSERT_EQ(1U, encoded_signatures.size()); - EXPECT_EQ(kExpectedSignature, encoded_signatures.front()); - - std::string encoded_query_string; - encoded_query.SerializeToString(&encoded_query_string); - EXPECT_EQ(expected_query_string, encoded_query_string); -} - TEST_F(FormStructureTestImpl, PossibleValues) { FormData form_data; form_data.url = GURL("http://www.foo.com/"); @@ -5294,6 +5209,110 @@ TEST_F(FormStructureTestImpl, PossibleValues) { EXPECT_EQ(0U, form_structure2.PossibleValues(ADDRESS_BILLING_COUNTRY).size()); } +// Test that server predictions get precedence over htmll types if they are +// overrides. +TEST_F(FormStructureTestImpl, ParseQueryResponse_ServerPredictionIsOverride) { + FormData form_data; + FormFieldData field; + form_data.url = GURL("http://foo.com"); + field.form_control_type = "text"; + + // Just some field. + field.label = ASCIIToUTF16("some field"); + field.name = ASCIIToUTF16("some_field"); + // But this field has an autocomplete attribute. + field.autocomplete_attribute = "name"; + field.unique_renderer_id = MakeFieldRendererId(); + form_data.fields.push_back(field); + + // Some other field. + field.label = ASCIIToUTF16("some other field"); + field.name = ASCIIToUTF16("some_other_field"); + // Which has the same attribute. + field.autocomplete_attribute = "name"; + field.unique_renderer_id = MakeFieldRendererId(); + form_data.fields.push_back(field); + + // Setup the query response with an override for the name field to be a first + // name. + AutofillQueryResponse response; + auto* form_suggestion = response.add_form_suggestions(); + AddFieldOverrideToForm(form_suggestion, form_data.fields[0], NAME_FIRST); + AddFieldSuggestionToForm(form_suggestion, form_data.fields[1], NAME_LAST); + + std::string response_string = SerializeAndEncode(response); + + // Disable the feature which gives overrides precedence. + { + base::test::ScopedFeatureList scoped_feature; + scoped_feature.InitAndDisableFeature( + features::kAutofillServerTypeTakesPrecedence); + + // Parse the response and update the field type predictions. + FormStructure form(form_data); + form.DetermineHeuristicTypes(nullptr, nullptr); + std::vector<FormStructure*> forms{&form}; + FormStructure::ParseApiQueryResponse( + response_string, forms, test::GetEncodedSignatures(forms), nullptr); + ASSERT_EQ(form.field_count(), 2U); + + // Validate the type predictions. + EXPECT_EQ(UNKNOWN_TYPE, form.field(0)->heuristic_type()); + EXPECT_EQ(HTML_TYPE_NAME, form.field(0)->html_type()); + EXPECT_EQ(NAME_FIRST, form.field(0)->server_type()); + EXPECT_EQ(UNKNOWN_TYPE, form.field(1)->heuristic_type()); + EXPECT_EQ(HTML_TYPE_NAME, form.field(1)->html_type()); + EXPECT_EQ(NAME_LAST, form.field(1)->server_type()); + + // Validate that the overrides are set correctly. + EXPECT_TRUE(form.field(0)->server_type_prediction_is_override()); + EXPECT_FALSE(form.field(1)->server_type_prediction_is_override()); + + // Validate that the html prediction won. + EXPECT_EQ(form.field(0)->Type().GetStorableType(), NAME_FULL); + EXPECT_EQ(form.field(1)->Type().GetStorableType(), NAME_FULL); + } + + // Enable the feature to give overrides precedence. + { + base::test::ScopedFeatureList scoped_feature; + scoped_feature.InitAndEnableFeature( + features::kAutofillServerTypeTakesPrecedence); + + // Parse the response and update the field type predictions. + FormStructure form(form_data); + form.DetermineHeuristicTypes(nullptr, nullptr); + std::vector<FormStructure*> forms{&form}; + FormStructure::ParseApiQueryResponse( + response_string, forms, test::GetEncodedSignatures(forms), nullptr); + ASSERT_EQ(form.field_count(), 2U); + + // Validate the type predictions. + EXPECT_EQ(UNKNOWN_TYPE, form.field(0)->heuristic_type()); + EXPECT_EQ(HTML_TYPE_NAME, form.field(0)->html_type()); + EXPECT_EQ(NAME_FIRST, form.field(0)->server_type()); + EXPECT_EQ(UNKNOWN_TYPE, form.field(1)->heuristic_type()); + EXPECT_EQ(HTML_TYPE_NAME, form.field(1)->html_type()); + EXPECT_EQ(NAME_LAST, form.field(1)->server_type()); + + // Validate that the overrides are set correctly. + EXPECT_TRUE(form.field(0)->server_type_prediction_is_override()); + EXPECT_FALSE(form.field(1)->server_type_prediction_is_override()); + + // Validate that the server prediction won for the first field. + EXPECT_EQ(form.field(0)->Type().GetStorableType(), NAME_FIRST); + EXPECT_EQ(form.field(1)->Type().GetStorableType(), NAME_FULL); + + // Validate that the server override cannot be altered. + form.field(0)->SetTypeTo(AutofillType(NAME_FULL)); + EXPECT_EQ(form.field(0)->Type().GetStorableType(), NAME_FIRST); + + // Validate that that the non-override can be altered. + form.field(1)->SetTypeTo(AutofillType(NAME_FIRST)); + EXPECT_EQ(form.field(1)->Type().GetStorableType(), NAME_FIRST); + } +} + // Test the heuristic prediction for NAME_LAST_SECOND overrides server // predictions. TEST_F(FormStructureTestImpl, @@ -5310,22 +5329,25 @@ TEST_F(FormStructureTestImpl, // First name field. field.label = ASCIIToUTF16("Nombre"); field.name = ASCIIToUTF16("Nombre"); + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); // First last name field. // Should be identified by local heuristics. field.label = ASCIIToUTF16("Apellido Paterno"); field.name = ASCIIToUTF16("apellido_paterno"); + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); // Second last name field. // Should be identified by local heuristics. field.label = ASCIIToUTF16("Apellido Materno"); field.name = ASCIIToUTF16("apellido materno"); + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); FormStructure form(form_data); - form.DetermineHeuristicTypes(); + form.DetermineHeuristicTypes(nullptr, nullptr); // Setup the query response. AutofillQueryResponse response; @@ -5393,25 +5415,29 @@ TEST_F(FormStructureTestImpl, // Field for the name. field.label = ASCIIToUTF16("Name"); field.name = ASCIIToUTF16("Name"); + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); // Field for the street name. field.label = ASCIIToUTF16("Street Name"); field.name = ASCIIToUTF16("street_name"); + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); // Field for the house number. field.label = ASCIIToUTF16("House Number"); field.name = ASCIIToUTF16("house_number"); + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); // Field for the postal code. field.label = ASCIIToUTF16("ZIP"); field.name = ASCIIToUTF16("ZIP"); + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); FormStructure form(form_data); - form.DetermineHeuristicTypes(); + form.DetermineHeuristicTypes(nullptr, nullptr); // Setup the query response. AutofillQueryResponse response; @@ -5474,19 +5500,22 @@ TEST_F(FormStructureTestImpl, ParseQueryResponse_TooManyTypes) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("fname"); + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lname"); + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); field.label = ASCIIToUTF16("email"); field.name = ASCIIToUTF16("email"); field.autocomplete_attribute = "address-level2"; + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); FormStructure form(form_data); - form.DetermineHeuristicTypes(); + form.DetermineHeuristicTypes(nullptr, nullptr); // Setup the query response. AutofillQueryResponse response; @@ -5560,7 +5589,7 @@ TEST_F(FormStructureTestImpl, ParseQueryResponse_UnknownType) { form_data.fields.push_back(field); FormStructure form(form_data); - form.DetermineHeuristicTypes(); + form.DetermineHeuristicTypes(nullptr, nullptr); // Setup the query response. AutofillQueryResponse response; @@ -5788,7 +5817,7 @@ TEST_F(FormStructureTestImpl, ParseQueryResponse_AuthorDefinedTypes) { FormStructure form_structure(form); std::vector<FormStructure*> forms; forms.push_back(&form_structure); - forms.front()->DetermineHeuristicTypes(); + forms.front()->DetermineHeuristicTypes(nullptr, nullptr); AutofillQueryResponse response; auto* form_suggestion = response.add_form_suggestions(); @@ -6025,140 +6054,6 @@ TEST_F(FormStructureTestImpl, ParseQueryResponse_RationalizeMultiMonth_2) { EXPECT_EQ(UNKNOWN_TYPE, forms[0]->field(3)->Type().GetStorableType()); } -TEST_F(FormStructureTestImpl, SetStrippedParseableNames) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeature(kAutofillLabelAffixRemoval); -} - -TEST_F(FormStructureTestImpl, IsValidParseableName) { - // Parseable name should not be empty. - EXPECT_FALSE(FormStructure::IsValidParseableName(ASCIIToUTF16(""))); - // Parseable name should not be solely numerical. - EXPECT_FALSE(FormStructure::IsValidParseableName(ASCIIToUTF16("1265125"))); - - // Valid parseable name cases. - EXPECT_TRUE(FormStructure::IsValidParseableName(ASCIIToUTF16("a23"))); - EXPECT_TRUE(FormStructure::IsValidParseableName(ASCIIToUTF16("*)&%@"))); -} - -TEST_F(FormStructureTestImpl, FindLongestCommonAffixLength) { - auto String16ToStringPiece16 = [](std::vector<base::string16>& vin, - std::vector<base::StringPiece16>& vout) { - vout.clear(); - for (auto& str : vin) - vout.push_back(str); - }; - - // Normal prefix case. - std::vector<base::string16> strings; - std::vector<base::StringPiece16> stringPieces; - strings.push_back(ASCIIToUTF16("123456XXX123456789")); - strings.push_back(ASCIIToUTF16("12345678XXX012345678_foo")); - strings.push_back(ASCIIToUTF16("1234567890123456")); - strings.push_back(ASCIIToUTF16("1234567XXX901234567890")); - String16ToStringPiece16(strings, stringPieces); - size_t affixLength = - FormStructure::FindLongestCommonAffixLength(stringPieces, false); - EXPECT_EQ(ASCIIToUTF16("123456").size(), affixLength); - - // Normal suffix case. - strings.clear(); - strings.push_back(ASCIIToUTF16("black and gold dress")); - strings.push_back(ASCIIToUTF16("work_address")); - strings.push_back(ASCIIToUTF16("123456XXX1234_home_address")); - strings.push_back(ASCIIToUTF16("1234567890123456_city_address")); - String16ToStringPiece16(strings, stringPieces); - affixLength = FormStructure::FindLongestCommonAffixLength(stringPieces, true); - EXPECT_EQ(ASCIIToUTF16("dress").size(), affixLength); - - // Handles no common prefix. - strings.clear(); - strings.push_back(ASCIIToUTF16("1234567890123456")); - strings.push_back(ASCIIToUTF16("4567890123456789")); - strings.push_back(ASCIIToUTF16("7890123456789012")); - String16ToStringPiece16(strings, stringPieces); - affixLength = - FormStructure::FindLongestCommonAffixLength(stringPieces, false); - EXPECT_EQ(ASCIIToUTF16("").size(), affixLength); - - // Handles no common suffix. - strings.clear(); - strings.push_back(ASCIIToUTF16("1234567890123456")); - strings.push_back(ASCIIToUTF16("4567890123456789")); - strings.push_back(ASCIIToUTF16("7890123456789012")); - String16ToStringPiece16(strings, stringPieces); - affixLength = FormStructure::FindLongestCommonAffixLength(stringPieces, true); - EXPECT_EQ(ASCIIToUTF16("").size(), affixLength); - - // Only one string, prefix case. - strings.clear(); - strings.push_back(ASCIIToUTF16("1234567890")); - String16ToStringPiece16(strings, stringPieces); - affixLength = - FormStructure::FindLongestCommonAffixLength(stringPieces, false); - EXPECT_EQ(ASCIIToUTF16("1234567890").size(), affixLength); - - // Only one string, suffix case. - strings.clear(); - strings.push_back(ASCIIToUTF16("1234567890")); - String16ToStringPiece16(strings, stringPieces); - affixLength = FormStructure::FindLongestCommonAffixLength(stringPieces, true); - EXPECT_EQ(ASCIIToUTF16("1234567890").size(), affixLength); - - // Empty vector, prefix case. - strings.clear(); - String16ToStringPiece16(strings, stringPieces); - affixLength = - FormStructure::FindLongestCommonAffixLength(stringPieces, false); - EXPECT_EQ(ASCIIToUTF16("").size(), affixLength); - - // Empty vector, suffix case. - strings.clear(); - String16ToStringPiece16(strings, stringPieces); - affixLength = FormStructure::FindLongestCommonAffixLength(stringPieces, true); - EXPECT_EQ(ASCIIToUTF16("").size(), affixLength); -} - -TEST_F(FormStructureTestImpl, FindLongestCommonPrefix) { - // Normal case: All strings are longer than threshold; some are common. - std::vector<base::string16> strings; - strings.push_back(ASCIIToUTF16("1234567890123456789")); - strings.push_back(ASCIIToUTF16("123456789012345678_foo")); - strings.push_back(ASCIIToUTF16("1234567890123456")); - strings.push_back(ASCIIToUTF16("12345678901234567890")); - base::string16 prefix = FormStructure::FindLongestCommonPrefix(strings); - EXPECT_EQ(ASCIIToUTF16("1234567890123456"), prefix); - - // Handles no common prefix. - strings.clear(); - strings.push_back(ASCIIToUTF16("1234567890123456")); - strings.push_back(ASCIIToUTF16("4567890123456789")); - strings.push_back(ASCIIToUTF16("7890123456789012")); - prefix = FormStructure::FindLongestCommonPrefix(strings); - EXPECT_EQ(ASCIIToUTF16(""), prefix); - - // Some strings less than threshold length. - strings.clear(); - strings.push_back(ASCIIToUTF16("12345678901234567890")); - strings.push_back(ASCIIToUTF16("1234567890123456")); - strings.push_back(ASCIIToUTF16("")); - strings.push_back(ASCIIToUTF16("12345")); - strings.push_back(ASCIIToUTF16("12345678")); - prefix = FormStructure::FindLongestCommonPrefix(strings); - EXPECT_EQ(ASCIIToUTF16("1234567890123456"), prefix); - - // Only one string. - strings.clear(); - strings.push_back(ASCIIToUTF16("1234567890123456")); - prefix = FormStructure::FindLongestCommonPrefix(strings); - EXPECT_EQ(ASCIIToUTF16("1234567890123456"), prefix); - - // Empty vector. - strings.clear(); - prefix = FormStructure::FindLongestCommonPrefix(strings); - EXPECT_EQ(ASCIIToUTF16(""), prefix); -} - TEST_P(ParameterizedFormStructureTest, RationalizePhoneNumber_RunsOncePerSection) { bool section_with_renderer_ids = GetParam(); @@ -6781,7 +6676,7 @@ TEST_F(FormStructureTestImpl, std::vector<FormStructure*> forms; forms.push_back(&form_structure); // Will identify the sections based on the heuristics types. - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); AutofillQueryResponse response; auto* form_suggestion = response.add_form_suggestions(); @@ -6878,7 +6773,7 @@ TEST_F( std::vector<FormStructure*> forms; forms.push_back(&form_structure); // Will identify the sections based on the heuristics types. - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); AutofillQueryResponse response; auto* form_suggestion = response.add_form_suggestions(); @@ -6984,7 +6879,7 @@ TEST_F(FormStructureTestImpl, forms.push_back(&form_structure); // Will identify the sections based on the heuristics types. - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); AutofillQueryResponse response; auto* form_suggestion = response.add_form_suggestions(); @@ -7276,7 +7171,7 @@ TEST_F(FormStructureTestImpl, forms.push_back(&form_structure); // Will identify the sections based on the heuristics types. - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); AutofillQueryResponse response; auto* form_suggestion = response.add_form_suggestions(); AddFieldSuggestionToForm(form_suggestion, form.fields[0], NAME_FULL); @@ -7490,14 +7385,8 @@ INSTANTIATE_TEST_SUITE_P(All, ParameterizedFormStructureTest, testing::Bool()); // Tests that, when the flag is off, we will not set the predicted type to // unknown for fields that have no server data and autocomplete off, and when // the flag is ON, we will overwrite the predicted type. -TEST_P(ParameterizedFormStructureTest, +TEST_F(ParameterizedFormStructureTest, NoServerData_AutocompleteOff_FlagDisabled_NoOverwrite) { - base::test::ScopedFeatureList scoped_features; - - bool flag_enabled = GetParam(); - scoped_features.InitWithFeatureState(features::kAutofillOffNoServerData, - flag_enabled); - FormData form; form.url = GURL("http://foo.com"); FormFieldData field; @@ -7541,7 +7430,7 @@ TEST_P(ParameterizedFormStructureTest, FormStructure form_structure(form); // Will identify the sections based on the heuristics types. - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::vector<FormStructure*> forms; forms.push_back(&form_structure); @@ -7554,8 +7443,7 @@ TEST_P(ParameterizedFormStructureTest, ASSERT_EQ(4U, forms[0]->field_count()); // Only NAME_LAST should be affected by the flag. - EXPECT_EQ(flag_enabled ? UNKNOWN_TYPE : NAME_LAST, - forms[0]->field(1)->Type().GetStorableType()); + EXPECT_EQ(NAME_LAST, forms[0]->field(1)->Type().GetStorableType()); EXPECT_EQ(NAME_FIRST, forms[0]->field(0)->Type().GetStorableType()); EXPECT_EQ(ADDRESS_HOME_LINE1, forms[0]->field(2)->Type().GetStorableType()); @@ -7564,13 +7452,7 @@ TEST_P(ParameterizedFormStructureTest, // Tests that we never overwrite the CVC heuristic-predicted type, even if there // is no server data (votes) for every CC fields. -TEST_P(ParameterizedFormStructureTest, NoServerDataCCFields_CVC_NoOverwrite) { - base::test::ScopedFeatureList scoped_features; - - bool flag_enabled = GetParam(); - scoped_features.InitWithFeatureState(features::kAutofillOffNoServerData, - flag_enabled); - +TEST_F(ParameterizedFormStructureTest, NoServerDataCCFields_CVC_NoOverwrite) { FormData form; form.url = GURL("http://foo.com"); FormFieldData field; @@ -7611,7 +7493,7 @@ TEST_P(ParameterizedFormStructureTest, NoServerDataCCFields_CVC_NoOverwrite) { FormStructure form_structure(form); // Will identify the sections based on the heuristics types. - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::vector<FormStructure*> forms; forms.push_back(&form_structure); @@ -7623,18 +7505,11 @@ TEST_P(ParameterizedFormStructureTest, NoServerDataCCFields_CVC_NoOverwrite) { ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); - // If flag is enabled, fields should have been overwritten to Unknown. - if (flag_enabled) { - EXPECT_EQ(UNKNOWN_TYPE, forms[0]->field(0)->Type().GetStorableType()); - EXPECT_EQ(UNKNOWN_TYPE, forms[0]->field(1)->Type().GetStorableType()); - EXPECT_EQ(UNKNOWN_TYPE, forms[0]->field(2)->Type().GetStorableType()); - } else { - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - forms[0]->field(0)->Type().GetStorableType()); - EXPECT_EQ(CREDIT_CARD_NUMBER, forms[0]->field(1)->Type().GetStorableType()); - EXPECT_EQ(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, - forms[0]->field(2)->Type().GetStorableType()); - } + EXPECT_EQ(CREDIT_CARD_NAME_FULL, + forms[0]->field(0)->Type().GetStorableType()); + EXPECT_EQ(CREDIT_CARD_NUMBER, forms[0]->field(1)->Type().GetStorableType()); + EXPECT_EQ(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, + forms[0]->field(2)->Type().GetStorableType()); // Regardless of the flag, the CVC field should not have been overwritten. EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE, @@ -7643,13 +7518,7 @@ TEST_P(ParameterizedFormStructureTest, NoServerDataCCFields_CVC_NoOverwrite) { // Tests that we never overwrite the CVC heuristic-predicted type, even if there // is server data (votes) for every other CC fields. -TEST_P(ParameterizedFormStructureTest, WithServerDataCCFields_CVC_NoOverwrite) { - base::test::ScopedFeatureList scoped_features; - - bool flag_enabled = GetParam(); - scoped_features.InitWithFeatureState(features::kAutofillOffNoServerData, - flag_enabled); - +TEST_F(ParameterizedFormStructureTest, WithServerDataCCFields_CVC_NoOverwrite) { FormData form; form.url = GURL("http://foo.com"); FormFieldData field; @@ -7692,7 +7561,7 @@ TEST_P(ParameterizedFormStructureTest, WithServerDataCCFields_CVC_NoOverwrite) { FormStructure form_structure(form); // Will identify the sections based on the heuristics types. - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::vector<FormStructure*> forms; forms.push_back(&form_structure); @@ -7785,7 +7654,7 @@ TEST_P(RationalizationFieldTypeFilterTest, Rationalization_Rules_Filter_Out) { FormStructure form_structure(form); // Will identify the sections based on the heuristics types. - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::vector<FormStructure*> forms; forms.push_back(&form_structure); @@ -7853,7 +7722,7 @@ TEST_P(RationalizationFieldTypeRelationshipsTest, FormStructure form_structure(form); // Will identify the sections based on the heuristics types. - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr, nullptr); std::vector<FormStructure*> forms; forms.push_back(&form_structure); @@ -7926,12 +7795,12 @@ TEST_F(FormStructureTestImpl, CreateForPasswordManagerUpload) { ASSERT_EQ(FieldSignature(100u), form->field(2)->GetFieldSignature()); EXPECT_TRUE(form->EncodeUploadRequest( {} /* available_field_types */, false /* form_was_autofilled */, - "" /*login_form_signature*/, true /*observed_submission*/, &upload, - &signatures)); + "" /*login_form_signature*/, true /*observed_submission*/, + true /* is_raw_metadata_uploading_enabled */, &upload, &signatures)); } // Tests if a new logical form is started with the second appearance of a field -// of type |NAME|. +// of type |FieldTypeGroup::kName|. TEST_P(ParameterizedFormStructureTest, NoAutocompleteSectionNames) { bool section_with_renderer_ids = GetParam(); base::test::ScopedFeatureList scoped_features; diff --git a/chromium/components/autofill/core/browser/form_types.cc b/chromium/components/autofill/core/browser/form_types.cc index 8d19644088b..ebea4c71017 100644 --- a/chromium/components/autofill/core/browser/form_types.cc +++ b/chromium/components/autofill/core/browser/form_types.cc @@ -7,27 +7,26 @@ namespace autofill { -// static -FormType FormTypes::FieldTypeGroupToFormType(FieldTypeGroup field_type_group) { +FormType FieldTypeGroupToFormType(FieldTypeGroup field_type_group) { switch (field_type_group) { - case NAME: - case NAME_BILLING: - case EMAIL: - case COMPANY: - case ADDRESS_HOME: - case ADDRESS_BILLING: - case PHONE_HOME: - case PHONE_BILLING: - return ADDRESS_FORM; - case CREDIT_CARD: - return CREDIT_CARD_FORM; - case USERNAME_FIELD: - case PASSWORD_FIELD: - return PASSWORD_FORM; - case NO_GROUP: - case TRANSACTION: - case UNFILLABLE: - return UNKNOWN_FORM_TYPE; + case FieldTypeGroup::kName: + case FieldTypeGroup::kNameBilling: + case FieldTypeGroup::kEmail: + case FieldTypeGroup::kCompany: + case FieldTypeGroup::kAddressHome: + case FieldTypeGroup::kAddressBilling: + case FieldTypeGroup::kPhoneHome: + case FieldTypeGroup::kPhoneBilling: + return FormType::kAddressForm; + case FieldTypeGroup::kCreditCard: + return FormType::kCreditCardForm; + case FieldTypeGroup::kUsernameField: + case FieldTypeGroup::kPasswordField: + return FormType::kPasswordForm; + case FieldTypeGroup::kNoGroup: + case FieldTypeGroup::kTransaction: + case FieldTypeGroup::kUnfillable: + return FormType::kUnknownFormType; } } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_types.h b/chromium/components/autofill/core/browser/form_types.h index 2c36914c826..516ffec3fd3 100644 --- a/chromium/components/autofill/core/browser/form_types.h +++ b/chromium/components/autofill/core/browser/form_types.h @@ -9,17 +9,15 @@ namespace autofill { -enum FormType : int { - UNKNOWN_FORM_TYPE, - ADDRESS_FORM, - CREDIT_CARD_FORM, - PASSWORD_FORM +enum class FormType : int { + kUnknownFormType, + kAddressForm, + kCreditCardForm, + kPasswordForm, + kMaxValue = kPasswordForm }; -class FormTypes { - public: - static FormType FieldTypeGroupToFormType(FieldTypeGroup field_type_group); -}; +FormType FieldTypeGroupToFormType(FieldTypeGroup field_type_group); } // namespace autofill diff --git a/chromium/components/autofill/core/browser/geo/alternative_state_name_map.cc b/chromium/components/autofill/core/browser/geo/alternative_state_name_map.cc index 1e5ed929936..614000521de 100644 --- a/chromium/components/autofill/core/browser/geo/alternative_state_name_map.cc +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map.cc @@ -27,8 +27,6 @@ AlternativeStateNameMap* AlternativeStateNameMap::GetInstance() { return g_alternative_state_name_map.get(); } -AlternativeStateNameMap::AlternativeStateNameMap() = default; - // static AlternativeStateNameMap::StateName AlternativeStateNameMap::NormalizeStateName( const StateName& text) { @@ -38,6 +36,18 @@ AlternativeStateNameMap::StateName AlternativeStateNameMap::NormalizeStateName( return StateName(normalized_text); } +// static +base::Optional<AlternativeStateNameMap::CanonicalStateName> +AlternativeStateNameMap::GetCanonicalStateName( + const std::string& country_code, + const base::string16& state_name) { + return AlternativeStateNameMap::GetInstance()->GetCanonicalStateName( + AlternativeStateNameMap::CountryCode(country_code), + AlternativeStateNameMap::StateName(state_name)); +} + +AlternativeStateNameMap::AlternativeStateNameMap() = default; + base::Optional<AlternativeStateNameMap::CanonicalStateName> AlternativeStateNameMap::GetCanonicalStateName( const CountryCode& country_code, @@ -82,17 +92,13 @@ base::Optional<StateEntry> AlternativeStateNameMap::GetEntry( GetCanonicalStateName(country_code, normalized_state_string_from_profile, /*is_state_name_normalized=*/true); - if (!canonical_state_name) { - canonical_state_name = - CanonicalStateName(normalized_state_string_from_profile.value()); + if (canonical_state_name) { + auto it = localized_state_names_map_.find( + {country_code, canonical_state_name.value()}); + if (it != localized_state_names_map_.end()) + return it->second; } - DCHECK(canonical_state_name); - auto it = localized_state_names_map_.find( - {country_code, canonical_state_name.value()}); - if (it != localized_state_names_map_.end()) - return it->second; - return base::nullopt; } @@ -101,7 +107,7 @@ void AlternativeStateNameMap::AddEntry( const StateName& normalized_state_value_from_profile, const StateEntry& state_entry, const std::vector<StateName>& normalized_alternative_state_names, - CanonicalStateName* normalized_canonical_state_name) { + const CanonicalStateName& normalized_canonical_state_name) { DCHECK_CALLED_ON_VALID_SEQUENCE(alternative_state_name_map_sequence_checker_); // Example: @@ -120,25 +126,17 @@ void AlternativeStateNameMap::AddEntry( // ("DE", "Bayern") -> "Bayern" // ("DE", "BY") -> "Bayern" // ("DE", "Bavaria") -> "Bayern" - if (localized_state_names_map_.size() == kMaxMapSize || GetCanonicalStateName(country_code, normalized_state_value_from_profile, /*is_state_name_normalized=*/true)) { return; } - if (normalized_canonical_state_name) { - localized_state_names_map_[{ - country_code, *normalized_canonical_state_name}] = state_entry; - for (const auto& alternative_name : normalized_alternative_state_names) { - localized_state_names_reverse_lookup_map_[{ - country_code, alternative_name}] = *normalized_canonical_state_name; - } - } else { - localized_state_names_map_[{ - country_code, - CanonicalStateName(normalized_state_value_from_profile.value())}] = - state_entry; + localized_state_names_map_[{country_code, normalized_canonical_state_name}] = + state_entry; + for (const auto& alternative_name : normalized_alternative_state_names) { + localized_state_names_reverse_lookup_map_[{ + country_code, alternative_name}] = normalized_canonical_state_name; } } diff --git a/chromium/components/autofill/core/browser/geo/alternative_state_name_map.h b/chromium/components/autofill/core/browser/geo/alternative_state_name_map.h index 1ed11eaa6c6..5af1556d6ec 100644 --- a/chromium/components/autofill/core/browser/geo/alternative_state_name_map.h +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map.h @@ -12,7 +12,7 @@ #include "base/optional.h" #include "base/sequence_checker.h" #include "base/strings/string16.h" -#include "base/util/type_safety/strong_alias.h" +#include "base/types/strong_alias.h" namespace autofill { // AlternativeStateNameMap encapsulates mappings from state names in the @@ -62,39 +62,44 @@ namespace autofill { // b. ("US", "CA") -> "California" // c. ("US", "TheGoldenState") -> "California" // -// Example: Assuming the user creates an unknown state in the profile -// "Random State". -// 1. Entries added to the |localized_state_names_map_| are: -// ("RandomState", Empty StateEntry object) -// 2. Nothing is added to the -// |localized_state_names_reverse_lookup_map_| in this case +// In case, the user has an unknown state in the profile, nothing is added to +// the AlternativeStateNameMap; class AlternativeStateNameMap { public: // Represents ISO 3166-1 alpha-2 codes (always uppercase ASCII). - using CountryCode = util::StrongAlias<class CountryCodeTag, std::string>; + using CountryCode = base::StrongAlias<class CountryCodeTag, std::string>; // Represents either a canonical state name, or an abbreviation, or an // alternative name or normalized state name from the profile. - using StateName = util::StrongAlias<class StateNameTag, base::string16>; + using StateName = base::StrongAlias<class StateNameTag, base::string16>; // States can be represented as different strings (different spellings, // translations, abbreviations). All representations of a single state in a // single country are mapped to the same canonical name. using CanonicalStateName = - util::StrongAlias<class CanonicalStateNameTag, base::string16>; + base::StrongAlias<class CanonicalStateNameTag, base::string16>; static AlternativeStateNameMap* GetInstance(); + // Removes |kCharsToStrip| from |text| and returns the normalized text. + static StateName NormalizeStateName(const StateName& text); + + // Calls |GetCanonicalStateName()| member method of AlternativeStateNameMap + // and returns the canonical state name corresponding to |country_code| and + // |state_name| if present. + static base::Optional<AlternativeStateNameMap::CanonicalStateName> + GetCanonicalStateName(const std::string& country_code, + const base::string16& state_name); + ~AlternativeStateNameMap() = delete; AlternativeStateNameMap(const AlternativeStateNameMap&) = delete; AlternativeStateNameMap& operator=(const AlternativeStateNameMap&) = delete; - // Removes |kCharsToStrip| from |text| and returns the normalized text. - static StateName NormalizeStateName(const StateName& text); - // Returns the canonical name (StateEntry::canonical_name) from the // |localized_state_names_map_| based on // (|country_code|, |state_name|). + // |is_state_name_normalized| denotes whether the |state_name| has been + // normalized or not. base::Optional<CanonicalStateName> GetCanonicalStateName( const CountryCode& country_code, const StateName& state_name, @@ -109,18 +114,16 @@ class AlternativeStateNameMap { // Adds ((|country_code|, state key), |state_entry|) to the // |localized_state_names_map_|, where state key corresponds to - // |normalized_canonical_state_name| if it is not null, or to - // |normalized_state_value_from_profile| otherwise. - // If |normalized_canonical_state_name| is not null, each entry from - // |normalized_alternative_state_names| is added as a tuple - // ((|country_code|, entry), |normalized_canonical_state_name|) to the + // |normalized_canonical_state_name|. + // Also, each entry from |normalized_alternative_state_names| is added as a + // tuple ((|country_code|, |entry|), |normalized_canonical_state_name|) to the // |localized_state_names_reverse_lookup_map_|. void AddEntry( const CountryCode& country_code, const StateName& normalized_state_value_from_profile, const StateEntry& state_entry, const std::vector<StateName>& normalized_alternative_state_names, - CanonicalStateName* normalized_canonical_state_name); + const CanonicalStateName& normalized_canonical_state_name); // Returns true if the |localized_state_names_map_| is empty. bool IsLocalisedStateNamesMapEmpty() const; diff --git a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_test_utils.cc b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_test_utils.cc index 9435c583dbf..54ba574c204 100644 --- a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_test_utils.cc +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_test_utils.cc @@ -5,7 +5,6 @@ #include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h" #include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/browser/geo/alternative_state_name_map.h" namespace autofill { @@ -25,6 +24,12 @@ void ClearAlternativeStateNameMapForTesting() { ->ClearAlternativeStateNameMapForTesting(); } +AlternativeStateNameMap::StateName NormalizeAndConvertToUTF16( + const std::string& text) { + return AlternativeStateNameMap::NormalizeStateName( + AlternativeStateNameMap::StateName(base::UTF8ToUTF16(text))); +} + void PopulateAlternativeStateNameMapForTesting( const std::string& country_code, const std::string& key, @@ -33,22 +38,24 @@ void PopulateAlternativeStateNameMapForTesting( StateEntry state_entry; PopulateStateEntry(test_state_entry, &state_entry); std::vector<AlternativeStateNameMap::StateName> alternatives; - AlternativeStateNameMap::CanonicalStateName canonical_state_name = - AlternativeStateNameMap::CanonicalStateName( - base::ASCIIToUTF16(test_state_entry.canonical_name)); alternatives.emplace_back( - AlternativeStateNameMap::StateName(canonical_state_name.value())); + NormalizeAndConvertToUTF16(test_state_entry.canonical_name)); + AlternativeStateNameMap::CanonicalStateName canonical_state_name; + if (!alternatives.empty()) { + canonical_state_name = AlternativeStateNameMap::CanonicalStateName( + alternatives.back().value()); + } + for (const auto& abbr : test_state_entry.abbreviations) - alternatives.emplace_back( - AlternativeStateNameMap::StateName(base::ASCIIToUTF16(abbr))); - for (const auto& alternative_name : test_state_entry.alternative_names) - alternatives.emplace_back(AlternativeStateNameMap::StateName( - base::ASCIIToUTF16(alternative_name))); + alternatives.emplace_back(NormalizeAndConvertToUTF16(abbr)); + for (const auto& alternative_name : test_state_entry.alternative_names) { + alternatives.emplace_back(NormalizeAndConvertToUTF16(alternative_name)); + } AlternativeStateNameMap::GetInstance()->AddEntry( AlternativeStateNameMap::CountryCode(country_code), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16(key)), - state_entry, alternatives, &canonical_state_name); + AlternativeStateNameMap::StateName(base::UTF8ToUTF16(key)), state_entry, + alternatives, canonical_state_name); } } diff --git a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h index 0c86be629a8..29cfa66220a 100644 --- a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h @@ -6,6 +6,7 @@ #define COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_ALTERNATIVE_STATE_NAME_MAP_TEST_UTILS_H_ #include "base/optional.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map.h" #include "components/autofill/core/browser/proto/states.pb.h" namespace autofill { @@ -30,6 +31,10 @@ void PopulateStateEntry(const TestStateEntry& test_state_entry, // Clears the map for testing purposes. void ClearAlternativeStateNameMapForTesting(); +// Normalizes the text using |AlternativeStateNameMap::NormalizeStateName()|. +AlternativeStateNameMap::StateName NormalizeAndConvertToUTF16( + const std::string& text); + // Inserts a StateEntry instance into AlternativeStateNameMap for testing. void PopulateAlternativeStateNameMapForTesting( const std::string& country_code = "DE", diff --git a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_unittest.cc b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_unittest.cc index 268b41cec02..04523a93b18 100644 --- a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_unittest.cc +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_unittest.cc @@ -24,30 +24,24 @@ TEST(AlternativeStateNameMapTest, IsEntryAddedToMap) { TEST(AlternativeStateNameMapTest, StateCanonicalString) { test::ClearAlternativeStateNameMapForTesting(); test::PopulateAlternativeStateNameMapForTesting(); - AlternativeStateNameMap* alternative_state_name_map = - AlternativeStateNameMap::GetInstance(); const char* const kValidMatches[] = {"Bavaria", "BY", "Bayern", "by", "BAVARIA", "B.Y", "BAYern", "B-Y"}; + for (const char* valid_match : kValidMatches) { SCOPED_TRACE(valid_match); - EXPECT_NE(alternative_state_name_map->GetCanonicalStateName( - AlternativeStateNameMap::CountryCode("DE"), - AlternativeStateNameMap::StateName( - base::ASCIIToUTF16(valid_match))), + EXPECT_NE(AlternativeStateNameMap::GetCanonicalStateName( + "DE", base::ASCIIToUTF16(valid_match)), base::nullopt); } - EXPECT_EQ( - alternative_state_name_map->GetCanonicalStateName( - AlternativeStateNameMap::CountryCode("US"), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bavaria"))), - base::nullopt); - EXPECT_EQ(alternative_state_name_map->GetCanonicalStateName( - AlternativeStateNameMap::CountryCode("DE"), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16(""))), + + EXPECT_EQ(AlternativeStateNameMap::GetCanonicalStateName( + "US", base::ASCIIToUTF16("Bavaria")), + base::nullopt); + EXPECT_EQ(AlternativeStateNameMap::GetCanonicalStateName( + "DE", base::ASCIIToUTF16("")), base::nullopt); - EXPECT_EQ(alternative_state_name_map->GetCanonicalStateName( - AlternativeStateNameMap::CountryCode(""), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16(""))), + EXPECT_EQ(AlternativeStateNameMap::GetCanonicalStateName( + "", base::ASCIIToUTF16("")), base::nullopt); } @@ -57,18 +51,12 @@ TEST(AlternativeStateNameMapTest, SeparateEntryForDifferentCounties) { test::ClearAlternativeStateNameMapForTesting(); test::PopulateAlternativeStateNameMapForTesting("DE"); test::PopulateAlternativeStateNameMapForTesting("US"); - AlternativeStateNameMap* alternative_state_name_map = - AlternativeStateNameMap::GetInstance(); - EXPECT_NE( - alternative_state_name_map->GetCanonicalStateName( - AlternativeStateNameMap::CountryCode("DE"), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bavaria"))), - base::nullopt); - EXPECT_NE( - alternative_state_name_map->GetCanonicalStateName( - AlternativeStateNameMap::CountryCode("US"), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bavaria"))), - base::nullopt); + EXPECT_NE(AlternativeStateNameMap::GetCanonicalStateName( + "DE", base::ASCIIToUTF16("Bavaria")), + base::nullopt); + EXPECT_NE(AlternativeStateNameMap::GetCanonicalStateName( + "US", base::ASCIIToUTF16("Bavaria")), + base::nullopt); } // Tests that |AlternativeStateNameMap::NormalizeStateName()| removes "-", " " diff --git a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.cc b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.cc index 3c85158b61e..715556cd211 100644 --- a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.cc +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/containers/contains.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" @@ -22,6 +23,8 @@ #include "base/task/thread_pool.h" #include "base/task_runner_util.h" #include "components/autofill/core/browser/geo/country_data.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_l10n_util.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/prefs/pref_service.h" @@ -50,9 +53,11 @@ std::string LoadDataFromFile(const base::FilePath& file) { } // namespace -AlternativeStateNameMapUpdater::AlternativeStateNameMapUpdater() - : task_runner_(base::ThreadPool::CreateSequencedTaskRunner( - {base::MayBlock(), base::TaskPriority::BEST_EFFORT})) {} +AlternativeStateNameMapUpdater::AlternativeStateNameMapUpdater( + PrefService* local_state, + PersonalDataManager* personal_data_manager) + : personal_data_manager_(personal_data_manager), + local_state_(local_state) {} AlternativeStateNameMapUpdater::~AlternativeStateNameMapUpdater() = default; @@ -61,7 +66,7 @@ bool AlternativeStateNameMapUpdater::ContainsState( const std::vector<AlternativeStateNameMap::StateName>& stripped_alternative_state_names, const AlternativeStateNameMap::StateName& - stripped_state_values_from_profile) { + stripped_state_value_from_profile) { l10n::CaseInsensitiveCompare compare; // Returns true if |str1| is same as |str2| in a case-insensitive comparison. @@ -69,32 +74,91 @@ bool AlternativeStateNameMapUpdater::ContainsState( stripped_alternative_state_names, [&](const AlternativeStateNameMap::StateName& text) { return compare.StringsEqual(text.value(), - stripped_state_values_from_profile.value()); + stripped_state_value_from_profile.value()); }); } +void AlternativeStateNameMapUpdater::OnPersonalDataFinishedProfileTasks() { + if (base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap)) { + PopulateAlternativeStateNameMap(); + } +} + +void AlternativeStateNameMapUpdater::PopulateAlternativeStateNameMap( + base::OnceClosure callback) { + DCHECK(personal_data_manager_); + std::vector<AutofillProfile*> profiles = + personal_data_manager_->GetProfiles(); + + CountryToStateNamesListMapping country_to_state_names_map; + for (AutofillProfile* profile : profiles) { + const AutofillType country_code_type(HTML_TYPE_COUNTRY_CODE, + HTML_MODE_NONE); + const AlternativeStateNameMap::CountryCode country( + base::UTF16ToUTF8(profile->GetInfo( + country_code_type, personal_data_manager_->app_locale()))); + + const AlternativeStateNameMap::StateName state_name( + profile->GetInfo(AutofillType(ADDRESS_HOME_STATE), + personal_data_manager_->app_locale())); + const AlternativeStateNameMap::StateName normalized_state = + AlternativeStateNameMap::NormalizeStateName(state_name); + + if (country.value().empty() || normalized_state.value().empty()) + continue; + + if (parsed_state_values_.find({country, normalized_state}) != + parsed_state_values_.end()) { + continue; + } + + country_to_state_names_map[country].push_back(normalized_state); + parsed_state_values_.insert({country, normalized_state}); + } + + LoadStatesData(std::move(country_to_state_names_map), local_state_, + std::move(callback)); +} + void AlternativeStateNameMapUpdater::LoadStatesData( - const CountryToStateNamesListMapping& country_to_state_names_map, + CountryToStateNamesListMapping country_to_state_names_map, PrefService* pref_service, base::OnceClosure done_callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // Get the states data installation path from |pref_service|. - const std::string data_download_path = - pref_service->GetString(prefs::kAutofillStatesDataDir); + DCHECK(pref_service); + + // Get the states data installation path from |pref_service| which is set by + // the component updater once it downloads the states data and should be safe + // to use. + const base::FilePath data_download_path = + pref_service->GetFilePath(prefs::kAutofillStatesDataDir); + + const std::vector<std::string>& country_codes = + CountryDataMap::GetInstance()->country_codes(); + + // Remove all invalid country names. + base::EraseIf(country_to_state_names_map, + [&country_codes]( + const CountryToStateNamesListMapping::value_type& entry) { + return !base::Contains(country_codes, entry.first.value()); + }); // If the installed directory path is empty, it means that the component is // not ready for use yet. - if (data_download_path.empty()) { + if (data_download_path.empty() || country_to_state_names_map.empty()) { + is_alternative_state_name_map_populated_ = true; std::move(done_callback).Run(); return; } - const std::vector<std::string>& country_codes = - CountryDataMap::GetInstance()->country_codes(); + pending_init_done_callbacks_.push_back(std::move(done_callback)); // The |country_to_state_names_map| maps country_code names to a vector of // state names that are associated with this corresponding country. for (const auto& entry : country_to_state_names_map) { + // country_code is used as the filename. + // Example -> File "DE" contains the geographical states data of Germany. const AlternativeStateNameMap::CountryCode& country_code = entry.first; const std::vector<AlternativeStateNameMap::StateName>& states = entry.second; @@ -104,20 +168,12 @@ void AlternativeStateNameMapUpdater::LoadStatesData( if (!base::Contains(country_codes, country_code.value())) continue; - // country_code is used as the filename. - // Example -> File "DE" contains the geographical states data of Germany. - // |data_download_path| is set by the component updater once it downloads - // the states data and should be safe to use. - const base::FilePath file_path = - base::FilePath::FromUTF8Unsafe(data_download_path) - .AppendASCII(country_code.value()); - ++number_pending_init_tasks_; - pending_init_done_callbacks_.push_back(std::move(done_callback)); base::PostTaskAndReplyWithResult( - task_runner_.get(), FROM_HERE, - base::BindOnce(&LoadDataFromFile, file_path), + GetTaskRunner().get(), FROM_HERE, + base::BindOnce(&LoadDataFromFile, + data_download_path.AppendASCII(country_code.value())), base::BindOnce( &AlternativeStateNameMapUpdater::ProcessLoadedStateFileContent, weak_ptr_factory_.GetWeakPtr(), states)); @@ -145,6 +201,9 @@ void AlternativeStateNameMapUpdater::ProcessLoadedStateFileContent( std::vector<bool> match_found(stripped_state_values_from_profiles.size(), false); + AlternativeStateNameMap* alternative_state_name_map = + AlternativeStateNameMap::GetInstance(); + // Iterates over the states data loaded from the file and builds a list of // the state names and its variations. For each value v in // |stripped_state_values_from_profiles|, v is compared with the values in @@ -152,47 +211,37 @@ void AlternativeStateNameMapUpdater::ProcessLoadedStateFileContent( // comparison results in a match, the corresponding entry is added to the // |AlternativeStateNameMap|. for (const auto& state_entry : states_data.states()) { - DCHECK(state_entry.has_canonical_name()); - AlternativeStateNameMap::CanonicalStateName state_canonical_name = - AlternativeStateNameMap::CanonicalStateName( - base::UTF8ToUTF16(state_entry.canonical_name())); - // Build a list of all the names of the state (including its // abbreviations) in |state_names|. const std::vector<AlternativeStateNameMap::StateName> state_names = ExtractAllStateNames(state_entry); + // Canonical name is always the first entry in the |state_names|. + DCHECK(!state_names.empty()); + AlternativeStateNameMap::CanonicalStateName + normalized_canonical_state_name(state_names[0].value()); + for (size_t i = 0; i < stripped_state_values_from_profiles.size(); i++) { if (match_found[i]) continue; - // If |stripped_state_values_from_profile[i] is in the set of names of + // If |stripped_state_values_from_profile[i]| is in the set of names of // the state under consideration, add it to the AlternativeStateNameMap. if (ContainsState(state_names, stripped_state_values_from_profiles[i])) { - AlternativeStateNameMap::GetInstance()->AddEntry( + alternative_state_name_map->AddEntry( country_code, stripped_state_values_from_profiles[i], state_entry, - state_names, &state_canonical_name); + state_names, normalized_canonical_state_name); match_found[i] = true; } } } - - for (size_t i = 0; i < stripped_state_values_from_profiles.size(); i++) { - // In case, no match is found, insert an |empty_state_entry| object - // to the map. - if (!match_found[i]) { - StateEntry empty_state_entry; - AlternativeStateNameMap::GetInstance()->AddEntry( - country_code, stripped_state_values_from_profiles[i], - empty_state_entry, {}, nullptr); - } - } } // When all pending tasks are completed, trigger and clear the pending // callbacks. if (number_pending_init_tasks_ == 0) { + is_alternative_state_name_map_populated_ = true; for (auto& callback : std::exchange(pending_init_done_callbacks_, {})) std::move(callback).Run(); } @@ -223,4 +272,13 @@ AlternativeStateNameMapUpdater::ExtractAllStateNames( return state_names; } +scoped_refptr<base::SequencedTaskRunner>& +AlternativeStateNameMapUpdater::GetTaskRunner() { + if (!task_runner_) { + task_runner_ = base::ThreadPool::CreateSequencedTaskRunner( + {base::MayBlock(), base::TaskPriority::BEST_EFFORT}); + } + return task_runner_; +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.h b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.h index ab1caabaf66..9e1868dd570 100644 --- a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.h +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.h @@ -10,44 +10,59 @@ #include <vector> #include "base/callback.h" +#include "base/callback_helpers.h" #include "base/memory/weak_ptr.h" #include "base/sequence_checker.h" #include "components/autofill/core/browser/geo/alternative_state_name_map.h" +#include "components/autofill/core/browser/personal_data_manager_observer.h" class PrefService; namespace autofill { +class PersonalDataManager; + using CountryToStateNamesListMapping = std::map<AlternativeStateNameMap::CountryCode, std::vector<AlternativeStateNameMap::StateName>>; // The AlternativeStateNameMap is a singleton to map between canonical state -// names and alternative representations. This class encapsulates all aspects -// about loading state data from disk and adding it to the -// AlternativeStateNameMap. -class AlternativeStateNameMapUpdater { +// names and alternative representations. This class acts as an observer to the +// PersonalDataManager and encapsulates all aspects about loading state data +// from disk and adding it to the AlternativeStateNameMap. +class AlternativeStateNameMapUpdater : public PersonalDataManagerObserver { public: - AlternativeStateNameMapUpdater(); - ~AlternativeStateNameMapUpdater(); + AlternativeStateNameMapUpdater(PrefService* local_state, + PersonalDataManager* personal_data_manager); + ~AlternativeStateNameMapUpdater() override; AlternativeStateNameMapUpdater(const AlternativeStateNameMapUpdater&) = delete; AlternativeStateNameMapUpdater& operator=( const AlternativeStateNameMapUpdater&) = delete; - // Creates and posts jobs to the |task_runner_| for reading the state data - // files and populating AlternativeStateNameMap. Once all files are read and - // the data is incorporated into AlternativeStateNameMap, |done_callback| is - // fired. |country_to_state_names_map| specifies which state data of which - // countries to load. - // Each call to LoadStatesData triggers loading state data files, so requests - // should be batched up. - void LoadStatesData( - const CountryToStateNamesListMapping& country_to_state_names_map, - PrefService* pref_service, - base::OnceClosure done_callback); + // PersonalDataManagerObserver: + void OnPersonalDataFinishedProfileTasks() override; + + // Extracts the country and state values from the profiles and adds them to + // the AlternativeStateNameMap. + void PopulateAlternativeStateNameMap( + base::OnceClosure callback = base::DoNothing()); + + // Getter method for |is_alternative_state_name_map_populated_|. + bool is_alternative_state_name_map_populated() const { + return is_alternative_state_name_map_populated_; + } #if defined(UNIT_TEST) + // A wrapper around |LoadStatesData| used for testing purposes. + void LoadStatesDataForTesting( + CountryToStateNamesListMapping country_to_state_names_map, + PrefService* pref_service, + base::OnceClosure done_callback) { + LoadStatesData(std::move(country_to_state_names_map), pref_service, + std::move(done_callback)); + } + // A wrapper around |ProcessLoadedStateFileContent| used for testing purposes. void ProcessLoadedStateFileContentForTesting( const std::vector<AlternativeStateNameMap::StateName>& @@ -64,11 +79,11 @@ class AlternativeStateNameMapUpdater { const std::vector<AlternativeStateNameMap::StateName>& stripped_alternative_state_names, const AlternativeStateNameMap::StateName& - stripped_state_values_from_profile) { + stripped_state_value_from_profile) { return ContainsState(stripped_alternative_state_names, - stripped_state_values_from_profile); + stripped_state_value_from_profile); } -#endif +#endif // defined(UNIT_TEST) private: // Compares |stripped_state_value_from_profile| with the entries in @@ -77,7 +92,18 @@ class AlternativeStateNameMapUpdater { const std::vector<AlternativeStateNameMap::StateName>& stripped_alternative_state_names, const AlternativeStateNameMap::StateName& - stripped_state_values_from_profile); + stripped_state_value_from_profile); + + // Creates and posts jobs to the |task_runner_| for reading the state data + // files and populating AlternativeStateNameMap. Once all files are read and + // the data is incorporated into AlternativeStateNameMap, |done_callback| is + // fired. |country_to_state_names_map| specifies which state data of which + // countries to load. + // Each call to LoadStatesData triggers loading state data files, so requests + // should be batched up. + void LoadStatesData(CountryToStateNamesListMapping country_to_state_names_map, + PrefService* pref_service, + base::OnceClosure done_callback); // Each entry in |state_values_from_profiles| is compared with the states // |data| read from the files and then inserted into the @@ -92,15 +118,34 @@ class AlternativeStateNameMapUpdater { std::vector<AlternativeStateNameMap::StateName> ExtractAllStateNames( const StateEntry& state_entry); + // Lazily initializes and returns |task_runner_|. + scoped_refptr<base::SequencedTaskRunner>& GetTaskRunner(); + // TaskRunner for reading files from disk. scoped_refptr<base::SequencedTaskRunner> task_runner_; + // A pointer to an instance of PersonalDataManager used to fetch the profiles + // data and register this class as an obsever. + PersonalDataManager* const personal_data_manager_ = nullptr; + + // The browser local_state that stores the states data installation path. + PrefService* const local_state_ = nullptr; + // In case of concurrent requests to load states data, the callbacks are // queued in |pending_init_done_callbacks_| and triggered once the // |number_pending_init_tasks_| returns to 0. std::vector<base::OnceClosure> pending_init_done_callbacks_; int number_pending_init_tasks_ = 0; + // False, if the AlternativeStateNameMap has not been populated yet. + bool is_alternative_state_name_map_populated_ = false; + + // Keeps track of all the state values from the current profile that have been + // parsed. + std::set<std::pair<AlternativeStateNameMap::CountryCode, + AlternativeStateNameMap::StateName>> + parsed_state_values_; + SEQUENCE_CHECKER(sequence_checker_); // base::WeakPtr ensures that the callback bound to the object is canceled diff --git a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc index 97a6eb1038a..7633ea97630 100644 --- a/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc @@ -8,10 +8,15 @@ #include "base/files/scoped_temp_dir.h" #include "base/optional.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/geo/alternative_state_name_map.h" #include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h" +#include "components/autofill/core/browser/test_autofill_client.h" +#include "components/autofill/core/browser/test_personal_data_manager.h" +#include "components/autofill/core/browser/webdata/autofill_webdata_service.h" +#include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/prefs/testing_pref_service.h" #include "testing/gmock/include/gmock/gmock.h" @@ -22,27 +27,66 @@ using base::UTF8ToUTF16; namespace autofill { +class MockAlternativeStateNameMapUpdater + : public AlternativeStateNameMapUpdater { + public: + MockAlternativeStateNameMapUpdater(base::OnceClosure callback, + PrefService* local_state, + PersonalDataManager* personal_data_manager) + : AlternativeStateNameMapUpdater(local_state, personal_data_manager), + callback_(std::move(callback)) {} + + // PersonalDataManagerObserver: + void OnPersonalDataFinishedProfileTasks() override { + if (base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap)) { + PopulateAlternativeStateNameMap(std::move(callback_)); + } + } + + private: + base::OnceClosure callback_; +}; + class AlternativeStateNameMapUpdaterTest : public ::testing::Test { public: - AlternativeStateNameMapUpdaterTest() - : pref_service_(test::PrefServiceForTesting()) {} + AlternativeStateNameMapUpdaterTest() = default; void SetUp() override { + feature_.InitAndEnableFeature( + features::kAutofillUseAlternativeStateNameMap); + + autofill_client_.SetPrefs(test::PrefServiceForTesting()); ASSERT_TRUE(data_install_dir_.CreateUniqueTempDir()); + personal_data_manager_.Init(/*profile_database=*/database_, + /*account_database=*/nullptr, + /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), + /*identity_manager=*/nullptr, + /*client_profile_validator=*/nullptr, + /*history_service=*/nullptr, + /*is_off_the_record=*/false); + alternative_state_name_map_updater_ = + std::make_unique<AlternativeStateNameMapUpdater>( + autofill_client_.GetPrefs(), &personal_data_manager_); } const base::FilePath& GetPath() const { return data_install_dir_.GetPath(); } void WritePathToPref(const base::FilePath& file_path) { - pref_service_->SetFilePath(autofill::prefs::kAutofillStatesDataDir, - file_path); + autofill_client_.GetPrefs()->SetFilePath( + autofill::prefs::kAutofillStatesDataDir, file_path); } protected: base::test::TaskEnvironment task_environment_; - AlternativeStateNameMapUpdater alternative_state_name_map_updater; - std::unique_ptr<PrefService> pref_service_; + TestAutofillClient autofill_client_; + scoped_refptr<AutofillWebDataService> database_; + std::unique_ptr<AlternativeStateNameMapUpdater> + alternative_state_name_map_updater_; base::ScopedTempDir data_install_dir_; + base::test::ScopedFeatureList feature_; + TestPersonalDataManager personal_data_manager_; }; // Tests that the states data is added to AlternativeStateNameMap. @@ -61,17 +105,15 @@ TEST_F(AlternativeStateNameMapUpdaterTest, EntryAddedToStateMap) { std::vector<bool> state_data_present = {true, true, true, true, false, false, false, false}; - alternative_state_name_map_updater.ProcessLoadedStateFileContentForTesting( + alternative_state_name_map_updater_->ProcessLoadedStateFileContentForTesting( test_strings, states_data, base::DoNothing()); AlternativeStateNameMap* alternative_state_name_map = AlternativeStateNameMap::GetInstance(); DCHECK(!alternative_state_name_map->IsLocalisedStateNamesMapEmpty()); - for (size_t i = 0; i < test_strings.size(); i++) { SCOPED_TRACE(test_strings[i]); - EXPECT_EQ(alternative_state_name_map->GetCanonicalStateName( - AlternativeStateNameMap::CountryCode("DE"), - test_strings[i]) != base::nullopt, + EXPECT_EQ(AlternativeStateNameMap::GetCanonicalStateName( + "DE", test_strings[i].value()) != base::nullopt, state_data_present[i]); } } @@ -84,19 +126,41 @@ TEST_F(AlternativeStateNameMapUpdaterTest, TestLoadStatesData) { base::WriteFile(GetPath().AppendASCII("DE"), test::CreateStatesProtoAsString()); WritePathToPref(GetPath()); + CountryToStateNamesListMapping country_to_state_names_list_mapping = { + {AlternativeStateNameMap::CountryCode("DE"), + {AlternativeStateNameMap::StateName(ASCIIToUTF16("Bavaria"))}}}; + base::RunLoop run_loop; + alternative_state_name_map_updater_->LoadStatesDataForTesting( + country_to_state_names_list_mapping, autofill_client_.GetPrefs(), + run_loop.QuitClosure()); + run_loop.Run(); + + EXPECT_NE(AlternativeStateNameMap::GetCanonicalStateName( + "DE", ASCIIToUTF16("Bavaria")), + base::nullopt); +} + +// Tests that there is no insertion in the AlternativeStateNameMap when a +// garbage country code is supplied to the LoadStatesData for which the states +// data file does not exist. +TEST_F(AlternativeStateNameMapUpdaterTest, NoTaskIsPosted) { + test::ClearAlternativeStateNameMapForTesting(); + base::WriteFile(GetPath().AppendASCII("DE"), + test::CreateStatesProtoAsString()); + WritePathToPref(GetPath()); + + CountryToStateNamesListMapping country_to_state_names_list_mapping = { + {AlternativeStateNameMap::CountryCode("DEE"), + {AlternativeStateNameMap::StateName(ASCIIToUTF16("Bavaria"))}}}; base::RunLoop run_loop; - alternative_state_name_map_updater.LoadStatesData( - {{AlternativeStateNameMap::CountryCode("DE"), - {AlternativeStateNameMap::StateName(ASCIIToUTF16("Bavaria"))}}}, - pref_service_.get(), run_loop.QuitClosure()); + alternative_state_name_map_updater_->LoadStatesDataForTesting( + country_to_state_names_list_mapping, autofill_client_.GetPrefs(), + run_loop.QuitClosure()); run_loop.Run(); - EXPECT_NE( - AlternativeStateNameMap::GetInstance()->GetCanonicalStateName( - AlternativeStateNameMap::CountryCode("DE"), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bavaria"))), - base::nullopt); + EXPECT_TRUE( + AlternativeStateNameMap::GetInstance()->IsLocalisedStateNamesMapEmpty()); } // Tests that the AlternativeStateNameMap is populated when @@ -112,17 +176,20 @@ TEST_F(AlternativeStateNameMapUpdaterTest, TestLoadStatesDataUTF8) { .alternative_names = {"Parana", "State of Parana"}})); WritePathToPref(GetPath()); + CountryToStateNamesListMapping country_to_state_names_list_mapping = { + {AlternativeStateNameMap::CountryCode("ES"), + {AlternativeStateNameMap::StateName(ASCIIToUTF16("Parana"))}}}; + base::RunLoop run_loop; - alternative_state_name_map_updater.LoadStatesData( - {{AlternativeStateNameMap::CountryCode("ES"), - {AlternativeStateNameMap::StateName(ASCIIToUTF16("Parana"))}}}, - pref_service_.get(), run_loop.QuitClosure()); + alternative_state_name_map_updater_->LoadStatesDataForTesting( + country_to_state_names_list_mapping, autofill_client_.GetPrefs(), + run_loop.QuitClosure()); run_loop.Run(); base::Optional<StateEntry> entry1 = AlternativeStateNameMap::GetInstance()->GetEntry( AlternativeStateNameMap::CountryCode("ES"), - AlternativeStateNameMap::StateName(base::UTF8ToUTF16("Paraná"))); + AlternativeStateNameMap::StateName(UTF8ToUTF16("Paraná"))); EXPECT_NE(entry1, base::nullopt); EXPECT_EQ(entry1->canonical_name(), "Paraná"); EXPECT_THAT(entry1->abbreviations(), @@ -133,7 +200,7 @@ TEST_F(AlternativeStateNameMapUpdaterTest, TestLoadStatesDataUTF8) { base::Optional<StateEntry> entry2 = AlternativeStateNameMap::GetInstance()->GetEntry( AlternativeStateNameMap::CountryCode("ES"), - AlternativeStateNameMap::StateName(base::UTF8ToUTF16("Parana"))); + AlternativeStateNameMap::StateName(UTF8ToUTF16("Parana"))); EXPECT_NE(entry2, base::nullopt); EXPECT_EQ(entry2->canonical_name(), "Paraná"); EXPECT_THAT(entry2->abbreviations(), @@ -142,18 +209,101 @@ TEST_F(AlternativeStateNameMapUpdaterTest, TestLoadStatesDataUTF8) { {"Parana", "State of Parana"})); } +// Tests that the AlternativeStateNameMap is populated when +// |StateNameMapUpdater::LoadStatesData()| is called for states data of +// multiple countries simultaneously. +TEST_F(AlternativeStateNameMapUpdaterTest, + TestLoadStatesDataOfMultipleCountriesSimultaneously) { + test::ClearAlternativeStateNameMapForTesting(); + + base::WriteFile(GetPath().AppendASCII("DE"), + test::CreateStatesProtoAsString()); + base::WriteFile( + GetPath().AppendASCII("ES"), + test::CreateStatesProtoAsString( + "ES", {.canonical_name = "Paraná", + .abbreviations = {"PR"}, + .alternative_names = {"Parana", "State of Parana"}})); + WritePathToPref(GetPath()); + + CountryToStateNamesListMapping country_to_state_names = { + {AlternativeStateNameMap::CountryCode("ES"), + {AlternativeStateNameMap::StateName(ASCIIToUTF16("Parana"))}}, + {AlternativeStateNameMap::CountryCode("DE"), + {AlternativeStateNameMap::StateName(ASCIIToUTF16("Bavaria"))}}}; + + base::RunLoop run_loop; + alternative_state_name_map_updater_->LoadStatesDataForTesting( + country_to_state_names, autofill_client_.GetPrefs(), + run_loop.QuitClosure()); + run_loop.Run(); + + base::Optional<StateEntry> entry1 = + AlternativeStateNameMap::GetInstance()->GetEntry( + AlternativeStateNameMap::CountryCode("ES"), + AlternativeStateNameMap::StateName(UTF8ToUTF16("Paraná"))); + EXPECT_NE(entry1, base::nullopt); + EXPECT_EQ(entry1->canonical_name(), "Paraná"); + EXPECT_THAT(entry1->abbreviations(), + testing::UnorderedElementsAreArray({"PR"})); + EXPECT_THAT(entry1->alternative_names(), testing::UnorderedElementsAreArray( + {"Parana", "State of Parana"})); + + base::Optional<StateEntry> entry2 = + AlternativeStateNameMap::GetInstance()->GetEntry( + AlternativeStateNameMap::CountryCode("DE"), + AlternativeStateNameMap::StateName(UTF8ToUTF16("Bavaria"))); + EXPECT_NE(entry2, base::nullopt); + EXPECT_EQ(entry2->canonical_name(), "Bavaria"); + EXPECT_THAT(entry2->abbreviations(), + testing::UnorderedElementsAreArray({"BY"})); + EXPECT_THAT(entry2->alternative_names(), + testing::UnorderedElementsAreArray({"Bayern"})); +} + // Tests the |StateNameMapUpdater::ContainsState()| functionality. TEST_F(AlternativeStateNameMapUpdaterTest, ContainsState) { EXPECT_TRUE(AlternativeStateNameMapUpdater::ContainsStateForTesting( - {AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bavaria")), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bayern")), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16("BY"))}, - AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bavaria")))); + {AlternativeStateNameMap::StateName(ASCIIToUTF16("Bavaria")), + AlternativeStateNameMap::StateName(ASCIIToUTF16("Bayern")), + AlternativeStateNameMap::StateName(ASCIIToUTF16("BY"))}, + AlternativeStateNameMap::StateName(ASCIIToUTF16("Bavaria")))); EXPECT_FALSE(AlternativeStateNameMapUpdater::ContainsStateForTesting( - {AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bavaria")), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bayern")), - AlternativeStateNameMap::StateName(base::ASCIIToUTF16("BY"))}, - AlternativeStateNameMap::StateName(base::ASCIIToUTF16("California")))); + {AlternativeStateNameMap::StateName(ASCIIToUTF16("Bavaria")), + AlternativeStateNameMap::StateName(ASCIIToUTF16("Bayern")), + AlternativeStateNameMap::StateName(ASCIIToUTF16("BY"))}, + AlternativeStateNameMap::StateName(ASCIIToUTF16("California")))); +} + +// Tests that the |AlternativeStateNameMap| is populated with the help of the +// |MockAlternativeStateNameMapUpdater| observer when a new profile is added to +// the PDM. +TEST_F(AlternativeStateNameMapUpdaterTest, + PopulateAlternativeStateNameUsingObserver) { + test::ClearAlternativeStateNameMapForTesting(); + WritePathToPref(GetPath()); + base::WriteFile(GetPath().AppendASCII("DE"), + test::CreateStatesProtoAsString()); + + AutofillProfile profile; + profile.SetInfo(ADDRESS_HOME_STATE, base::ASCIIToUTF16("Bavaria"), "en-US"); + profile.SetInfo(ADDRESS_HOME_COUNTRY, base::ASCIIToUTF16("DE"), "en-US"); + + base::RunLoop run_loop; + MockAlternativeStateNameMapUpdater mock_alternative_state_name_updater( + run_loop.QuitClosure(), autofill_client_.GetPrefs(), + &personal_data_manager_); + personal_data_manager_.AddObserver(&mock_alternative_state_name_updater); + personal_data_manager_.AddProfile(profile); + run_loop.Run(); + personal_data_manager_.RemoveObserver(&mock_alternative_state_name_updater); + + EXPECT_FALSE( + AlternativeStateNameMap::GetInstance()->IsLocalisedStateNamesMapEmpty()); + EXPECT_NE(AlternativeStateNameMap::GetCanonicalStateName( + "DE", base::ASCIIToUTF16("Bavaria")), + AlternativeStateNameMap::CanonicalStateName( + base::ASCIIToUTF16("Bayern"))); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/geo/autofill_country.cc b/chromium/components/autofill/core/browser/geo/autofill_country.cc index 121868e521a..b415b59908b 100644 --- a/chromium/components/autofill/core/browser/geo/autofill_country.cc +++ b/chromium/components/autofill/core/browser/geo/autofill_country.cc @@ -6,7 +6,7 @@ #include <stddef.h> -#include "base/stl_util.h" +#include "base/containers/contains.h" #include "base/strings/string_util.h" #include "components/autofill/core/browser/geo/country_data.h" #include "components/autofill/core/browser/geo/country_names.h" diff --git a/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc b/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc index 487d46e1990..39892b620ea 100644 --- a/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc +++ b/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc @@ -5,7 +5,7 @@ #include <set> #include <string> -#include "base/stl_util.h" +#include "base/containers/contains.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/geo/autofill_country.h" #include "components/autofill/core/browser/geo/country_data.h" diff --git a/chromium/components/autofill/core/browser/geo/subkey_requester.cc b/chromium/components/autofill/core/browser/geo/subkey_requester.cc index 0eb5da274bc..11c8fdae233 100644 --- a/chromium/components/autofill/core/browser/geo/subkey_requester.cc +++ b/chromium/components/autofill/core/browser/geo/subkey_requester.cc @@ -38,8 +38,8 @@ class SubKeyRequest : public SubKeyRequester::Request { address_validator_(address_validator), on_subkeys_received_(std::move(on_subkeys_received)), has_responded_(false), - on_timeout_(base::BindRepeating(&SubKeyRequest::OnRulesLoaded, - base::Unretained(this))) { + on_timeout_(base::BindOnce(&SubKeyRequest::OnRulesLoaded, + base::Unretained(this))) { base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, on_timeout_.callback(), base::TimeDelta::FromSeconds(timeout_seconds)); @@ -74,7 +74,7 @@ class SubKeyRequest : public SubKeyRequester::Request { SubKeyReceiverCallback on_subkeys_received_; bool has_responded_; - base::CancelableCallback<void()> on_timeout_; + base::CancelableOnceClosure on_timeout_; DISALLOW_COPY_AND_ASSIGN(SubKeyRequest); }; diff --git a/chromium/components/autofill/core/browser/logging/log_router.cc b/chromium/components/autofill/core/browser/logging/log_router.cc index f31dd7cb997..320e235458b 100644 --- a/chromium/components/autofill/core/browser/logging/log_router.cc +++ b/chromium/components/autofill/core/browser/logging/log_router.cc @@ -36,7 +36,7 @@ void LogRouter::ProcessLog(const std::string& text) { void LogRouter::ProcessLog(base::Value&& node) { // This may not be called when there are no receivers (i.e., the router is // inactive), because in that case the logs cannot be displayed. - DCHECK(receivers_.might_have_observers()); + DCHECK(!receivers_.empty()); accumulated_logs_.emplace_back(std::move(node)); for (LogReceiver& receiver : receivers_) receiver.LogEntry(accumulated_logs_.back()); @@ -45,7 +45,7 @@ void LogRouter::ProcessLog(base::Value&& node) { bool LogRouter::RegisterManager(LogManager* manager) { DCHECK(manager); managers_.AddObserver(manager); - return receivers_.might_have_observers(); + return !receivers_.empty(); } void LogRouter::UnregisterManager(LogManager* manager) { @@ -56,9 +56,9 @@ void LogRouter::UnregisterManager(LogManager* manager) { const std::vector<base::Value>& LogRouter::RegisterReceiver( LogReceiver* receiver) { DCHECK(receiver); - DCHECK(accumulated_logs_.empty() || receivers_.might_have_observers()); + DCHECK(accumulated_logs_.empty() || !receivers_.empty()); - if (!receivers_.might_have_observers()) { + if (receivers_.empty()) { for (LogManager& manager : managers_) manager.OnLogRouterAvailabilityChanged(true); } @@ -69,7 +69,7 @@ const std::vector<base::Value>& LogRouter::RegisterReceiver( void LogRouter::UnregisterReceiver(LogReceiver* receiver) { DCHECK(receivers_.HasObserver(receiver)); receivers_.RemoveObserver(receiver); - if (!receivers_.might_have_observers()) { + if (receivers_.empty()) { // |accumulated_logs_| can become very long; use the swap instead of clear() // to ensure that the memory is freed. std::vector<base::Value>().swap(accumulated_logs_); diff --git a/chromium/components/autofill/core/browser/pattern_provider/default_regex_patterns.h b/chromium/components/autofill/core/browser/pattern_provider/default_regex_patterns.h new file mode 100644 index 00000000000..3c8607d079c --- /dev/null +++ b/chromium/components/autofill/core/browser/pattern_provider/default_regex_patterns.h @@ -0,0 +1,16 @@ +// Copyright 2020 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_DEFAULT_REGEX_PATTERNS_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_DEFAULT_REGEX_PATTERNS_H_ + +#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" + +namespace autofill { + +PatternProvider::Map CreateDefaultRegexPatterns(); + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_DEFAULT_REGEX_PATTERNS_H_ diff --git a/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc b/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc index 3aaeb3d97a9..b7818c26d0c 100644 --- a/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc +++ b/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc @@ -5,11 +5,15 @@ #include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h" #include "base/bind.h" +#include "base/feature_list.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "base/values.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/language_code.h" #include "components/grit/components_resources.h" #include "ui/base/resource/resource_bundle.h" @@ -19,7 +23,6 @@ namespace field_type_parsing { namespace { -const char kPatternIdentifierKey[] = "pattern_identifier"; const char kPositivePatternKey[] = "positive_pattern"; const char kNegativePatternKey[] = "negative_pattern"; const char kPositiveScoreKey[] = "positive_score"; @@ -29,13 +32,11 @@ const char kVersionKey[] = "version"; bool ParseMatchingPattern(PatternProvider::Map& patterns, const std::string& field_type, - const std::string& language, + const LanguageCode& language, const base::Value& value) { if (!value.is_dict()) return false; - const std::string* pattern_identifier = - value.FindStringKey(kPatternIdentifierKey); const std::string* positive_pattern = value.FindStringKey(kPositivePatternKey); const std::string* negative_pattern = @@ -47,18 +48,17 @@ bool ParseMatchingPattern(PatternProvider::Map& patterns, base::Optional<int> match_field_input_types = value.FindIntKey(kMatchFieldInputTypesKey); - if (!pattern_identifier || !positive_pattern || !positive_score || - !match_field_attributes || !match_field_input_types) + if (!positive_pattern || !positive_score || !match_field_attributes || + !match_field_input_types) return false; autofill::MatchingPattern new_pattern; - new_pattern.pattern_identifier = *pattern_identifier; new_pattern.positive_pattern = *positive_pattern; new_pattern.positive_score = *positive_score; if (negative_pattern != nullptr) { new_pattern.negative_pattern = *negative_pattern; } else { - new_pattern.negative_pattern = base::nullopt; + new_pattern.negative_pattern = ""; } new_pattern.match_field_attributes = match_field_attributes.value(); new_pattern.match_field_input_types = match_field_input_types.value(); @@ -71,24 +71,25 @@ bool ParseMatchingPattern(PatternProvider::Map& patterns, std::vector<MatchingPattern>* pattern_list = &patterns[field_type][language]; pattern_list->push_back(new_pattern); - DVLOG(2) << "Correctly parsed MatchingPattern with identifier |" - << new_pattern.pattern_identifier << "|."; + DVLOG(2) << "Correctly parsed MatchingPattern with with type " << field_type + << ", language " << language.value() << ", pattern " + << new_pattern.positive_pattern << "."; return true; } -// Callback which is used once the JSON is parsed. -// |overwrite_equal_version| should be true when loading a remote -// configuration. If the configuration versions are equal or -// both unspecified (i.e. set to 0) this prioritizes the remote +// Callback which is used once the JSON is parsed. If the configuration versions +// are equal or both unspecified (i.e. set to 0) this prioritizes the remote // configuration over the local one. -void OnJsonParsed(bool overwrite_equal_version, - base::OnceClosure done_callback, - data_decoder::DataDecoder::ValueOrError result) { - // Skip any processing in case of an error. +void OnJsonParsed(data_decoder::DataDecoder::ValueOrError result) { + if (!base::FeatureList::IsEnabled( + features::kAutofillParsingPatternsFromRemote)) { + DVLOG(1) << "Remote patterns are disabled."; + return; + } + if (!result.value) { DVLOG(1) << "Failed to parse PatternProvider configuration JSON string."; - std::move(done_callback).Run(); return; } @@ -98,15 +99,12 @@ void OnJsonParsed(bool overwrite_equal_version, if (patterns && version.IsValid()) { DVLOG(1) << "Successfully parsed PatternProvider configuration."; - PatternProvider& pattern_provider = PatternProvider::GetInstance(); pattern_provider.SetPatterns(std::move(patterns.value()), - std::move(version), overwrite_equal_version); + std::move(version)); } else { DVLOG(1) << "Failed to parse PatternProvider configuration JSON object."; } - - std::move(done_callback).Run(); } } // namespace @@ -130,7 +128,7 @@ base::Optional<PatternProvider::Map> GetConfigurationFromJsonObject( } for (const auto& value : field_type_dict->DictItems()) { - const std::string& language = value.first; + LanguageCode language(value.first); const base::Value* inner_list = &value.second; if (!inner_list->is_list()) { @@ -144,7 +142,7 @@ base::Optional<PatternProvider::Map> GetConfigurationFromJsonObject( matchingPatternObj); if (!success) { DVLOG(1) << "Found incorrect |MatchingPattern| object in list |" - << field_type << "|, language |" << language << "|."; + << field_type << "|, language |" << language.value() << "|."; return base::nullopt; } } @@ -170,35 +168,8 @@ base::Version ExtractVersionFromJsonObject(base::Value& root) { } void PopulateFromJsonString(std::string json_string) { - data_decoder::DataDecoder::ParseJsonIsolated( - std::move(json_string), - base::BindOnce(&OnJsonParsed, true, base::DoNothing::Once())); -} - -void PopulateFromResourceBundle(base::OnceClosure done_callback) { - if (!ui::ResourceBundle::HasSharedInstance()) { - VLOG(1) << "Resource Bundle unavailable to load Autofill Matching Pattern " - "definitions."; - std::move(done_callback).Run(); - return; - } - - ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); - - // Load the string from the Resource Bundle on a worker thread, then - // securely parse the JSON in a separate process and call |OnJsonParsed| - // with the result. - base::ThreadPool::PostTaskAndReplyWithResult( - FROM_HERE, {base::MayBlock()}, - base::BindOnce(&ui::ResourceBundle::LoadDataResourceString, - base::Unretained(&bundle), IDR_AUTOFILL_REGEX_JSON), - base::BindOnce( - [](base::OnceClosure done_callback, std::string resource_string) { - data_decoder::DataDecoder::ParseJsonIsolated( - std::move(resource_string), - base::BindOnce(&OnJsonParsed, false, std::move(done_callback))); - }, - std::move(done_callback))); + data_decoder::DataDecoder::ParseJsonIsolated(std::move(json_string), + base::BindOnce(&OnJsonParsed)); } base::Optional<PatternProvider::Map> diff --git a/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc b/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc index a9cd570a644..de78bc26736 100644 --- a/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc +++ b/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc @@ -9,6 +9,8 @@ #include "base/json/json_reader.h" #include "base/test/gtest_util.h" #include "base/version.h" +#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "components/autofill/core/common/language_code.h" #include "components/grit/components_resources.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/resource/resource_bundle.h" @@ -21,11 +23,11 @@ namespace field_type_parsing { // parsed to the map structure used by |PatternProvider| as // expected, given the input is valid. TEST(PatternConfigurationParserTest, WellFormedParsedCorrectly) { - std::string JSON_message = R"( + std::string json_message = R"( { "version": "1.0", "FULL_NAME": { - "en_us": [ + "en": [ { "pattern_identifier": "Name_en", "positive_pattern": "name|full name", @@ -47,7 +49,7 @@ TEST(PatternConfigurationParserTest, WellFormedParsedCorrectly) { ] }, "ADDRESS": { - "en_us": [ + "en": [ { "pattern_identifier": "Address", "positive_pattern": "address", @@ -59,14 +61,14 @@ TEST(PatternConfigurationParserTest, WellFormedParsedCorrectly) { ] } })"; - base::Optional<base::Value> JSON_object = - base::JSONReader::Read(JSON_message); + base::Optional<base::Value> json_object = + base::JSONReader::Read(json_message); - ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + ASSERT_TRUE(json_object) << "Incorrectly formatted JSON string."; - base::Version version = ExtractVersionFromJsonObject(JSON_object.value()); + base::Version version = ExtractVersionFromJsonObject(json_object.value()); base::Optional<PatternProvider::Map> optional_patterns = - GetConfigurationFromJsonObject(JSON_object.value()); + GetConfigurationFromJsonObject(json_object.value()); ASSERT_TRUE(version.IsValid()); ASSERT_TRUE(optional_patterns); @@ -78,20 +80,19 @@ TEST(PatternConfigurationParserTest, WellFormedParsedCorrectly) { ASSERT_EQ(2U, patterns.size()); ASSERT_TRUE(patterns.count("FULL_NAME")); ASSERT_EQ(2U, patterns["FULL_NAME"].size()); - ASSERT_TRUE(patterns["FULL_NAME"].count("en_us")); - ASSERT_TRUE(patterns["FULL_NAME"].count("fr")); + ASSERT_TRUE(patterns["FULL_NAME"].count(LanguageCode("en"))); + ASSERT_TRUE(patterns["FULL_NAME"].count(LanguageCode("fr"))); ASSERT_TRUE(patterns.count("ADDRESS")); ASSERT_EQ(1U, patterns["ADDRESS"].size()); - ASSERT_TRUE(patterns["ADDRESS"].count("en_us")); + ASSERT_TRUE(patterns["ADDRESS"].count(LanguageCode("en"))); // Test one |MatchingPattern| to check that they are parsed correctly. - MatchingPattern* pattern = &patterns["FULL_NAME"]["fr"][0]; + MatchingPattern* pattern = &patterns["FULL_NAME"][LanguageCode("fr")][0]; - ASSERT_EQ("Name_fr", pattern->pattern_identifier); ASSERT_EQ("nom|prenom", pattern->positive_pattern); ASSERT_EQ("compagne", pattern->negative_pattern); - ASSERT_EQ("fr", pattern->language); + ASSERT_EQ(LanguageCode("fr"), pattern->language); ASSERT_NEAR(2.0, pattern->positive_score, 1e-6); ASSERT_EQ(2, pattern->match_field_attributes); ASSERT_EQ(3 << 2, pattern->match_field_input_types); @@ -100,11 +101,11 @@ TEST(PatternConfigurationParserTest, WellFormedParsedCorrectly) { // Test that the parser does not return anything if some |MatchingPattern| // object is missing a property. TEST(PatternConfigurationParserTest, MalformedMissingProperty) { - std::string JSON_message = R"( + std::string json_message = R"( { "version": "1.0", "FULL_NAME": { - "en_us": [ + "en": [ { "pattern_identifier": "Name_en", "positive_pattern": "name|full name", @@ -125,13 +126,13 @@ TEST(PatternConfigurationParserTest, MalformedMissingProperty) { ] } })"; - base::Optional<base::Value> JSON_object = - base::JSONReader::Read(JSON_message); + base::Optional<base::Value> json_object = + base::JSONReader::Read(json_message); - ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + ASSERT_TRUE(json_object) << "Incorrectly formatted JSON string."; base::Optional<PatternProvider::Map> optional_patterns = - GetConfigurationFromJsonObject(JSON_object.value()); + GetConfigurationFromJsonObject(json_object.value()); ASSERT_FALSE(optional_patterns); } @@ -139,12 +140,11 @@ TEST(PatternConfigurationParserTest, MalformedMissingProperty) { // Test that the parser correctly sets the default version if // it is not present in the configuration. TEST(PatternConfigurationParserTest, MalformedMissingVersion) { - std::string JSON_message = R"( + std::string json_message = R"( { "FULL_NAME": { - "en_us": [ + "en": [ { - "pattern_identifier": "Name_en", "positive_pattern": "name|full name", "positive_score": 2.0, "negative_pattern": "company", @@ -154,12 +154,12 @@ TEST(PatternConfigurationParserTest, MalformedMissingVersion) { ] } })"; - base::Optional<base::Value> JSON_object = - base::JSONReader::Read(JSON_message); + base::Optional<base::Value> json_object = + base::JSONReader::Read(json_message); - ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + ASSERT_TRUE(json_object) << "Incorrectly formatted JSON string."; - base::Version version = ExtractVersionFromJsonObject(JSON_object.value()); + base::Version version = ExtractVersionFromJsonObject(json_object.value()); ASSERT_EQ(base::Version("0"), version); } @@ -167,11 +167,10 @@ TEST(PatternConfigurationParserTest, MalformedMissingVersion) { // Test that the parser does not return anything if the inner key points // to a single object instead of a list. TEST(PatternConfigurationParserTest, MalformedNotList) { - std::string JSON_message = R"( + std::string json_message = R"( { "FULL_NAME": { - "en_us": { - "pattern_identifier": "Name_en", + "en": { "positive_pattern": "name|full name", "positive_score": 2.0, "negative_pattern": "company", @@ -180,13 +179,13 @@ TEST(PatternConfigurationParserTest, MalformedNotList) { } } })"; - base::Optional<base::Value> JSON_object = - base::JSONReader::Read(JSON_message); + base::Optional<base::Value> json_object = + base::JSONReader::Read(json_message); - ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + ASSERT_TRUE(json_object) << "Incorrectly formatted JSON string."; base::Optional<PatternProvider::Map> optional_patterns = - GetConfigurationFromJsonObject(JSON_object.value()); + GetConfigurationFromJsonObject(json_object.value()); ASSERT_FALSE(optional_patterns); } diff --git a/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.cc b/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.cc index 349c0d8c39a..f1e0d9090f4 100644 --- a/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.cc +++ b/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.cc @@ -13,6 +13,7 @@ #include "base/no_destructor.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h" +#include "components/autofill/core/browser/pattern_provider/default_regex_patterns.h" #include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h" #include "components/autofill/core/common/autofill_features.h" @@ -27,10 +28,10 @@ void EnrichPatternsWithEnVersion( PatternProvider::Map* type_and_lang_to_patterns) { DCHECK(type_and_lang_to_patterns); for (auto& p : *type_and_lang_to_patterns) { - std::map<std::string, std::vector<MatchingPattern>>& lang_to_patterns = + std::map<LanguageCode, std::vector<MatchingPattern>>& lang_to_patterns = p.second; - auto it = lang_to_patterns.find(kSourceCodeLanguage); + auto it = lang_to_patterns.find(LanguageCode(kSourceCodeLanguage)); if (it == lang_to_patterns.end()) continue; std::vector<MatchingPattern> en_patterns = it->second; @@ -39,10 +40,10 @@ void EnrichPatternsWithEnVersion( } for (auto& q : lang_to_patterns) { - const std::string& page_language = q.first; + const LanguageCode& page_language = q.first; std::vector<MatchingPattern>& patterns = q.second; - if (page_language != kSourceCodeLanguage) { + if (page_language != LanguageCode(kSourceCodeLanguage)) { patterns.insert(patterns.end(), en_patterns.begin(), en_patterns.end()); } } @@ -52,7 +53,7 @@ void EnrichPatternsWithEnVersion( // Sorts patterns in descending order by their score. void SortPatternsByScore(PatternProvider::Map* type_and_lang_to_patterns) { for (auto& p : *type_and_lang_to_patterns) { - std::map<std::string, std::vector<MatchingPattern>>& lang_to_patterns = + std::map<LanguageCode, std::vector<MatchingPattern>>& lang_to_patterns = p.second; for (auto& q : lang_to_patterns) { std::vector<MatchingPattern>& patterns = q.second; @@ -65,44 +66,27 @@ void SortPatternsByScore(PatternProvider::Map* type_and_lang_to_patterns) { } } -PatternProvider* PatternProvider::g_pattern_provider = nullptr; - // static PatternProvider& PatternProvider::GetInstance() { - if (!g_pattern_provider) { - static base::NoDestructor<PatternProvider> instance; - g_pattern_provider = instance.get(); - // TODO(crbug/1147608) This is an ugly hack to avoid loading the JSON. The - // motivation is that some Android unit tests fail because a dependency is - // missing. Instead of fixing this dependency, we'll go for an alternative - // solution that avoids the whole async/sync problem. - if (base::FeatureList::IsEnabled( - features::kAutofillUsePageLanguageToSelectFieldParsingPatterns) || - base::FeatureList::IsEnabled( - features:: - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics)) { - field_type_parsing::PopulateFromResourceBundle(); - } + static base::NoDestructor<PatternProvider> instance; + static bool initialized = false; + if (!initialized) { + instance->SetPatterns(CreateDefaultRegexPatterns(), base::Version()); + initialized = true; } - return *g_pattern_provider; -} - -// static -void PatternProvider::ResetPatternProvider() { - g_pattern_provider = nullptr; + return *instance; } PatternProvider::PatternProvider() = default; PatternProvider::~PatternProvider() = default; void PatternProvider::SetPatterns(PatternProvider::Map patterns, - const base::Version version, - const bool overwrite_equal_version) { + const base::Version& version) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!pattern_version_.IsValid() || pattern_version_ < version || - (overwrite_equal_version && pattern_version_ == version)) { - patterns_ = patterns; + if (!pattern_version_.IsValid() || + (version.IsValid() && pattern_version_ <= version)) { + patterns_ = std::move(patterns); pattern_version_ = version; EnrichPatternsWithEnVersion(&patterns_); SortPatternsByScore(&patterns_); @@ -111,15 +95,15 @@ void PatternProvider::SetPatterns(PatternProvider::Map patterns, const std::vector<MatchingPattern> PatternProvider::GetMatchPatterns( const std::string& pattern_name, - const std::string& page_language) const { + const LanguageCode& page_language) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // TODO(crbug.com/1134496): Remove feature check once launched. if (base::FeatureList::IsEnabled( - features::kAutofillUsePageLanguageToSelectFieldParsingPatterns)) { + features::kAutofillParsingPatternsLanguageDependent)) { auto outer_it = patterns_.find(pattern_name); if (outer_it != patterns_.end()) { - const std::map<std::string, std::vector<MatchingPattern>>& + const std::map<LanguageCode, std::vector<MatchingPattern>>& lang_to_pattern = outer_it->second; auto inner_it = lang_to_pattern.find(page_language); if (inner_it != lang_to_pattern.end()) { @@ -130,10 +114,8 @@ const std::vector<MatchingPattern> PatternProvider::GetMatchPatterns( } } return GetAllPatternsByType(pattern_name); - } else if ( - base::FeatureList::IsEnabled( - features:: - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics)) { + } else if (base::FeatureList::IsEnabled( + features::kAutofillParsingPatternsNegativeMatching)) { return GetAllPatternsByType(pattern_name); } else { return {}; @@ -142,15 +124,14 @@ const std::vector<MatchingPattern> PatternProvider::GetMatchPatterns( const std::vector<MatchingPattern> PatternProvider::GetMatchPatterns( ServerFieldType type, - const std::string& page_language) const { - std::string pattern_name = AutofillType(type).ToString(); - return GetMatchPatterns(pattern_name, page_language); + const LanguageCode& page_language) const { + return GetMatchPatterns(AutofillType::ServerFieldTypeToString(type), + page_language); } const std::vector<MatchingPattern> PatternProvider::GetAllPatternsByType( ServerFieldType type) const { - std::string type_str = AutofillType(type).ToString(); - return GetAllPatternsByType(type_str); + return GetAllPatternsByType(AutofillType::ServerFieldTypeToString(type)); } const std::vector<MatchingPattern> PatternProvider::GetAllPatternsByType( @@ -158,16 +139,16 @@ const std::vector<MatchingPattern> PatternProvider::GetAllPatternsByType( auto it = patterns_.find(type); if (it == patterns_.end()) return {}; - const std::map<std::string, std::vector<MatchingPattern>>& type_patterns = + const std::map<LanguageCode, std::vector<MatchingPattern>>& type_patterns = it->second; std::vector<MatchingPattern> all_language_patterns; for (const auto& p : type_patterns) { - const std::string& page_language = p.first; + const LanguageCode& page_language = p.first; const std::vector<MatchingPattern>& language_patterns = p.second; for (const MatchingPattern& mp : language_patterns) { - if (page_language == kSourceCodeLanguage || - mp.language != kSourceCodeLanguage) { + if (page_language == LanguageCode(kSourceCodeLanguage) || + mp.language != LanguageCode(kSourceCodeLanguage)) { all_language_patterns.push_back(mp); } } diff --git a/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.h b/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.h index 4ddd9cf6469..767ad35788e 100644 --- a/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.h +++ b/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.h @@ -11,10 +11,12 @@ #include "base/macros.h" #include "base/no_destructor.h" #include "base/sequence_checker.h" +#include "base/types/strong_alias.h" #include "base/version.h" #include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h" +#include "components/autofill/core/common/language_code.h" namespace autofill { @@ -25,28 +27,25 @@ class PatternProvider { public: // The outer keys are field types or other pattern names. The inner keys are // page languages in lower case. - // TODO(crbug/1142413): decide on uppercase or lowercase. using Map = std::map<std::string, - std::map<std::string, std::vector<MatchingPattern>>>; + std::map<LanguageCode, std::vector<MatchingPattern>>>; // Returns a reference to the global Pattern Provider. static PatternProvider& GetInstance(); // Setter for loading patterns from external storage. - void SetPatterns(const Map patterns, - const base::Version version, - const bool overwrite_equal_version); + void SetPatterns(const Map patterns, const base::Version& version); // Find the patterns for a given ServerFieldType and for a given // |page_language|. const std::vector<MatchingPattern> GetMatchPatterns( ServerFieldType type, - const std::string& page_language) const; + const LanguageCode& page_language) const; // Find the patterns for a given |pattern_name| and a given |page_language|. const std::vector<MatchingPattern> GetMatchPatterns( const std::string& pattern_name, - const std::string& page_language) const; + const LanguageCode& page_language) const; // Find all patterns, across all languages, for a given server field |type|. const std::vector<MatchingPattern> GetAllPatternsByType( @@ -57,30 +56,14 @@ class PatternProvider { const std::string& type) const; protected: - // Sets a provider to be used for tests. - static void SetPatternProviderForTesting(PatternProvider* pattern_provider) { - DCHECK(pattern_provider); - g_pattern_provider = pattern_provider; - } - - // Resets the provider pointer if the object behind it gets deleted. - static void ResetPatternProvider(); - PatternProvider(); ~PatternProvider(); - const Map& patterns() const { return patterns_; } - private: - FRIEND_TEST_ALL_PREFIXES(AutofillPatternProviderPipelineTest, - TestParsingEquivalent); - FRIEND_TEST_ALL_PREFIXES(AutofillPatternProviderPipelineTest, - DefaultPatternProviderLoads); + FRIEND_TEST_ALL_PREFIXES(AutofillPatternProviderTest, TestDefaultEqualsJson); friend class base::NoDestructor<PatternProvider>; - static PatternProvider* g_pattern_provider; - // Sequence checker to ensure thread-safety for pattern swapping. // All functions accessing the |patterns_| member variable are // expected to be called from the UI thread. diff --git a/chromium/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc b/chromium/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc index 5222b67d903..c3faf37c79f 100644 --- a/chromium/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc +++ b/chromium/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc @@ -16,199 +16,193 @@ #include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h" #include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h" #include "components/autofill/core/browser/pattern_provider/pattern_provider.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" #include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/language_code.h" +#include "components/grit/components_resources.h" #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/resource/resource_bundle.h" namespace autofill { namespace { +LanguageCode kLanguageDe("de"); +LanguageCode kLanguageEn("en"); + MatchingPattern GetCompanyPatternEn() { autofill::MatchingPattern m_p; - m_p.pattern_identifier = "kCompanyPatternEn"; m_p.positive_pattern = "company|business|organization|organisation"; - m_p.positive_score = 1.1f; + m_p.positive_score = 1.1; m_p.negative_pattern = ""; m_p.match_field_attributes = MATCH_NAME; m_p.match_field_input_types = MATCH_TEXT; - m_p.language = "en"; + m_p.language = kLanguageEn; return m_p; } MatchingPattern GetCompanyPatternDe() { autofill::MatchingPattern m_p; - m_p.pattern_identifier = "kCompanyPatternDe"; m_p.positive_pattern = "|(?<!con)firma|firmenname"; - m_p.positive_score = 1.1f; + m_p.positive_score = 1.1; m_p.negative_pattern = ""; m_p.match_field_attributes = MATCH_LABEL | MATCH_NAME; m_p.match_field_input_types = MATCH_TEXT; - m_p.language = "de"; + m_p.language = kLanguageDe; return m_p; } // Pattern Provider with custom values set for testing. class UnitTestPatternProvider : public PatternProvider { public: - UnitTestPatternProvider(); + UnitTestPatternProvider() + : UnitTestPatternProvider({GetCompanyPatternDe()}, + {GetCompanyPatternEn()}) {} + UnitTestPatternProvider(const std::vector<MatchingPattern>& de_patterns, - const std::vector<MatchingPattern>& en_patterns); - ~UnitTestPatternProvider(); + const std::vector<MatchingPattern>& en_patterns) { + Map patterns; + auto& company_patterns = + patterns[AutofillType::ServerFieldTypeToString(COMPANY_NAME)]; + company_patterns[kLanguageDe] = de_patterns; + company_patterns[kLanguageEn] = en_patterns; + SetPatterns(std::move(patterns), base::Version()); + } }; -UnitTestPatternProvider::UnitTestPatternProvider() - : UnitTestPatternProvider({GetCompanyPatternDe()}, - {GetCompanyPatternEn()}) {} - -UnitTestPatternProvider::UnitTestPatternProvider( - const std::vector<MatchingPattern>& de_patterns, - const std::vector<MatchingPattern>& en_patterns) { - PatternProvider::SetPatternProviderForTesting(this); - Map patterns; - auto& company_patterns = patterns[AutofillType(COMPANY_NAME).ToString()]; - company_patterns["de"] = de_patterns; - company_patterns["en"] = en_patterns; - SetPatterns(patterns, base::Version(), true); +// Called when the JSON bundle has been parsed, and sets the PatternProvider's +// patterns. +void OnJsonParsed(base::OnceClosure done_callback, + data_decoder::DataDecoder::ValueOrError result) { + base::Version version = + field_type_parsing::ExtractVersionFromJsonObject(result.value.value()); + base::Optional<PatternProvider::Map> patterns = + field_type_parsing::GetConfigurationFromJsonObject(result.value.value()); + ASSERT_TRUE(patterns); + ASSERT_TRUE(version.IsValid()); + PatternProvider& pattern_provider = PatternProvider::GetInstance(); + pattern_provider.SetPatterns(std::move(patterns.value()), std::move(version)); + std::move(done_callback).Run(); } -UnitTestPatternProvider::~UnitTestPatternProvider() { - PatternProvider::ResetPatternProvider(); +// Loads the string from the Resource Bundle on a worker thread. +void LoadPatternsFromResourceBundle() { + ASSERT_TRUE(ui::ResourceBundle::HasSharedInstance()); + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + base::RunLoop run_loop; + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock()}, + base::BindOnce(&ui::ResourceBundle::LoadDataResourceString, + base::Unretained(&bundle), IDR_AUTOFILL_REGEX_JSON), + base::BindOnce( + [](base::OnceClosure done_callback, std::string resource_string) { + data_decoder::DataDecoder::ParseJsonIsolated( + std::move(resource_string), + base::BindOnce(&OnJsonParsed, std::move(done_callback))); + }, + run_loop.QuitClosure())); + run_loop.Run(); } } // namespace -class AutofillPatternProviderTest : public testing::Test { - protected: - UnitTestPatternProvider pattern_provider_; - - ~AutofillPatternProviderTest() override = default; -}; - bool operator==(const MatchingPattern& mp1, const MatchingPattern& mp2) { - return (mp1.language == mp2.language && - mp1.match_field_attributes == mp2.match_field_attributes && - mp1.match_field_input_types == mp2.match_field_input_types && - mp1.negative_pattern == mp2.negative_pattern && - mp1.pattern_identifier == mp2.pattern_identifier && - mp1.positive_pattern == mp2.positive_pattern && - mp1.positive_score == mp2.positive_score); + return mp1.language == mp2.language && + mp1.positive_pattern == mp2.positive_pattern && + mp1.negative_pattern == mp2.negative_pattern && + mp1.positive_score == mp2.positive_score && + mp1.match_field_attributes == mp2.match_field_attributes && + mp1.match_field_input_types == mp2.match_field_input_types; } -TEST(AutofillPatternProvider, Single_Match) { +TEST(AutofillPatternProviderTest, Single_Match) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature( - features::kAutofillUsePageLanguageToSelectFieldParsingPatterns); + features::kAutofillParsingPatternsLanguageDependent); - MatchingPattern kCompanyPatternEn = GetCompanyPatternEn(); - MatchingPattern kCompanyPatternDe = GetCompanyPatternDe(); - UnitTestPatternProvider* pattern_provider = new UnitTestPatternProvider(); - auto pattern_store = pattern_provider->GetMatchPatterns("COMPANY_NAME", "en"); - - ASSERT_EQ(pattern_store.size(), 1u); - EXPECT_EQ(pattern_store[0], kCompanyPatternEn); + UnitTestPatternProvider p; + EXPECT_THAT(p.GetMatchPatterns("COMPANY_NAME", kLanguageEn), + ::testing::ElementsAre(GetCompanyPatternEn())); + EXPECT_THAT( + p.GetMatchPatterns("COMPANY_NAME", kLanguageDe), + ::testing::ElementsAre(GetCompanyPatternDe(), GetCompanyPatternEn())); } -// Test that the default pattern provider loads without crashing. -TEST(AutofillPatternProviderPipelineTest, DefaultPatternProviderLoads) { - base::test::ScopedFeatureList scoped_feature_list; - // Enable so that PatternProvider::GetInstance() actually does load the JSON. - scoped_feature_list.InitAndEnableFeature( - autofill::features::kAutofillUsePageLanguageToSelectFieldParsingPatterns); - - base::test::TaskEnvironment task_environment_; - data_decoder::test::InProcessDataDecoder in_process_data_decoder_; - - base::RunLoop run_loop; - field_type_parsing::PopulateFromResourceBundle(run_loop.QuitClosure()); - run_loop.Run(); - PatternProvider& default_pattern_provider = PatternProvider::GetInstance(); - - EXPECT_FALSE(default_pattern_provider.patterns().empty()); - - // Call the getter to ensure sequence checks work correctly. - default_pattern_provider.GetMatchPatterns("EMAIL_ADDRESS", "en"); +TEST(AutofillPatternProviderTest, BasedOnMatchType) { + UnitTestPatternProvider p; + EXPECT_THAT( + p.GetAllPatternsByType("COMPANY_NAME"), + ::testing::ElementsAre(GetCompanyPatternDe(), GetCompanyPatternEn())); } -// Test that the TestPatternProvider class uses a PatternProvider::Map -// equivalent to the DefaultPatternProvider. This is also an example of what is -// needed to test the DefaultPatternProvider. Warning: If this crashes, check -// that no state carried over from other tests using the singleton. -TEST(AutofillPatternProviderPipelineTest, TestParsingEquivalent) { - base::test::ScopedFeatureList scoped_feature_list; - // Enable so that PatternProvider::GetInstance() actually does load the JSON. - scoped_feature_list.InitAndEnableFeature( - autofill::features::kAutofillUsePageLanguageToSelectFieldParsingPatterns); - +TEST(AutofillPatternProviderTest, TestDefaultEqualsJson) { base::test::TaskEnvironment task_environment_; data_decoder::test::InProcessDataDecoder in_process_data_decoder_; - base::RunLoop run_loop; - field_type_parsing::PopulateFromResourceBundle(run_loop.QuitClosure()); - run_loop.Run(); - PatternProvider& default_pattern_provider = PatternProvider::GetInstance(); + auto default_version = PatternProvider::GetInstance().pattern_version_; + auto default_patterns = PatternProvider::GetInstance().patterns_; - TestPatternProvider test_pattern_provider; + // We want to make sure that the JSON loading actually does set the patterns. + // To this end, manipulate the current patterns. Then |default_patterns| can + // only be identical to |json_patterns| if loading the JSON updates the + // patterns. + PatternProvider::GetInstance().patterns_.clear(); + ASSERT_NE(default_patterns, PatternProvider::GetInstance().patterns_); - EXPECT_EQ(default_pattern_provider.patterns(), - test_pattern_provider.patterns()); -} + // Load the JSON explicitly from the file. + LoadPatternsFromResourceBundle(); -TEST(AutofillPatternProvider, BasedOnMatchType) { - UnitTestPatternProvider p; - ASSERT_GT(p.GetAllPatternsByType("COMPANY_NAME").size(), 0u); - EXPECT_EQ(p.GetAllPatternsByType("COMPANY_NAME"), - std::vector<MatchingPattern>( - {GetCompanyPatternDe(), GetCompanyPatternEn()})); - EXPECT_EQ(p.GetAllPatternsByType("COMPANY_NAME").size(), 2u); + auto json_version = PatternProvider::GetInstance().pattern_version_; + auto json_patterns = PatternProvider::GetInstance().patterns_; + + EXPECT_FALSE(default_version.IsValid()); + EXPECT_TRUE(json_version.IsValid()); + EXPECT_EQ(default_patterns, json_patterns); } -TEST(AutofillPatternProvider, UnknownLanguages) { +TEST(AutofillPatternProviderTest, UnknownLanguages) { { base::test::ScopedFeatureList feature; feature.InitWithFeatures( // enabled - {features::kAutofillUsePageLanguageToSelectFieldParsingPatterns}, + {features::kAutofillParsingPatternsLanguageDependent}, // disabled - {features:: - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics}); + {features::kAutofillParsingPatternsNegativeMatching}); UnitTestPatternProvider p; - EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", ""), - p.GetAllPatternsByType("COMPANY_NAME")); - EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", "blabla"), - p.GetAllPatternsByType("COMPANY_NAME")); + EXPECT_EQ(p.GetMatchPatterns(COMPANY_NAME, LanguageCode("")), + p.GetAllPatternsByType(COMPANY_NAME)); + EXPECT_EQ(p.GetMatchPatterns(COMPANY_NAME, LanguageCode("io")), + p.GetAllPatternsByType(COMPANY_NAME)); } { base::test::ScopedFeatureList feature; feature.InitWithFeatures( // enabled - {features:: - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics}, + {features::kAutofillParsingPatternsNegativeMatching}, // disabled - {features::kAutofillUsePageLanguageToSelectFieldParsingPatterns}); + {features::kAutofillParsingPatternsLanguageDependent}); UnitTestPatternProvider p; - EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", ""), - p.GetAllPatternsByType("COMPANY_NAME")); - EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", "blabla"), - p.GetAllPatternsByType("COMPANY_NAME")); + EXPECT_EQ(p.GetMatchPatterns(COMPANY_NAME, LanguageCode("")), + p.GetAllPatternsByType(COMPANY_NAME)); + EXPECT_EQ(p.GetMatchPatterns(COMPANY_NAME, LanguageCode("io")), + p.GetAllPatternsByType(COMPANY_NAME)); } } -TEST(AutofillPatternProvider, EnrichPatternsWithEnVersion) { +TEST(AutofillPatternProviderTest, EnrichPatternsWithEnVersion) { { base::test::ScopedFeatureList feature; feature.InitWithFeatures( // enabled - {features::kAutofillUsePageLanguageToSelectFieldParsingPatterns}, + {features::kAutofillParsingPatternsLanguageDependent}, // disabled - {features:: - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics}); + {features::kAutofillParsingPatternsNegativeMatching}); UnitTestPatternProvider p; - EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", "en"), + EXPECT_EQ(p.GetMatchPatterns(COMPANY_NAME, kLanguageEn), std::vector<MatchingPattern>{GetCompanyPatternEn()}); - EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", "de"), + EXPECT_EQ(p.GetMatchPatterns(COMPANY_NAME, kLanguageDe), std::vector<MatchingPattern>( {GetCompanyPatternDe(), GetCompanyPatternEn()})); } @@ -217,26 +211,25 @@ TEST(AutofillPatternProvider, EnrichPatternsWithEnVersion) { base::test::ScopedFeatureList feature; feature.InitWithFeatures( // enabled - {features:: - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics}, + {features::kAutofillParsingPatternsNegativeMatching}, // disabled - {features::kAutofillUsePageLanguageToSelectFieldParsingPatterns}); + {features::kAutofillParsingPatternsLanguageDependent}); UnitTestPatternProvider p; - EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", "en"), - std::vector<MatchingPattern>({GetCompanyPatternDe(), - GetCompanyPatternEn()})); - EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", "de"), - std::vector<MatchingPattern>({GetCompanyPatternDe(), - GetCompanyPatternEn()})); + EXPECT_EQ(p.GetMatchPatterns(COMPANY_NAME, kLanguageEn), + std::vector<MatchingPattern>( + {GetCompanyPatternDe(), GetCompanyPatternEn()})); + EXPECT_EQ(p.GetMatchPatterns(COMPANY_NAME, kLanguageDe), + std::vector<MatchingPattern>( + {GetCompanyPatternDe(), GetCompanyPatternEn()})); } } -TEST(AutofillPatternProvider, SortPatternsByScore) { +TEST(AutofillPatternProviderTest, SortPatternsByScore) { base::test::ScopedFeatureList feature; feature.InitWithFeatures( // enabled - {features::kAutofillUsePageLanguageToSelectFieldParsingPatterns, - features::kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics}, + {features::kAutofillParsingPatternsLanguageDependent, + features::kAutofillParsingPatternsNegativeMatching}, // disabled {}); std::vector<MatchingPattern> de_input_patterns; @@ -250,7 +243,7 @@ TEST(AutofillPatternProvider, SortPatternsByScore) { de_input_patterns[3].positive_score = 3.0; UnitTestPatternProvider p(de_input_patterns, {}); const std::vector<MatchingPattern>& de_patterns = - p.GetMatchPatterns(COMPANY_NAME, "de"); + p.GetMatchPatterns(COMPANY_NAME, kLanguageDe); ASSERT_EQ(de_patterns.size(), de_input_patterns.size()); EXPECT_EQ(de_patterns[0].positive_score, 5.0); EXPECT_EQ(de_patterns[1].positive_score, 3.0); diff --git a/chromium/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json b/chromium/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json index 16778eac24e..6dc7780d233 100644 --- a/chromium/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json +++ b/chromium/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json @@ -20,6 +20,16 @@ "match_field_input_types": 1 } ], + "es" : [ + { + "pattern_identifier": "es_street_name", + "positive_pattern": "calle", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], "ru" : [ { "pattern_identifier": "ru_street_name", @@ -41,6 +51,68 @@ } ] }, + "ADDRESS_HOME_APT_NUM": { + "en": [ + { + "pattern_identifier": "en_apartment_number", + "positive_pattern": "apartment", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "es": [ + { + "pattern_identifier": "es_apartment_number", + "positive_pattern": "interior|número.*apartamento", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "de": [ + { + "pattern_identifier": "de_apartment_number", + "positive_pattern": "wohnung", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "ru": [ + { + "pattern_identifier": "ru_apartment_number", + "positive_pattern": "квартир", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "it": [ + { + "pattern_identifier": "it_apartment_number", + "positive_pattern": "numero.*appartamento", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "fr": [ + { + "pattern_identifier": "fr_apartment_number", + "positive_pattern": "numéro.*appartement", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, "ADDRESS_HOME_HOUSE_NUMBER":{ "en": [ { @@ -49,7 +121,7 @@ "positive_score": 1.1, "negative_pattern": null, "match_field_attributes": 3, - "match_field_input_types": 1 + "match_field_input_types": 69 } ], "de": [ @@ -59,7 +131,7 @@ "positive_score": 1.1, "negative_pattern": null, "match_field_attributes": 3, - "match_field_input_types": 1 + "match_field_input_types": 69 } ], "pt": [ @@ -69,9 +141,19 @@ "positive_score": 1.1, "negative_pattern": null, "match_field_attributes": 3, - "match_field_input_types": 1 + "match_field_input_types": 69 } ], + "es": [ + { + "pattern_identifier": "es_house_number", + "positive_pattern": "n(u|ú)mero.*apartamento|exterior", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], "ru": [ { "pattern_identifier": "ru_house_number", @@ -79,7 +161,7 @@ "positive_score": 1.1, "negative_pattern": null, "match_field_attributes": 3, - "match_field_input_types": 1 + "match_field_input_types": 69 } ] }, @@ -137,9 +219,39 @@ "match_field_attributes": 3, "match_field_input_types": 1 } + ], + "tr": [ + { + "pattern_identifier": "tr_address_name_ignored_preserving", + "positive_pattern": "adres ([İi]sim|başlığı|adı)", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "pt": [ + { + "pattern_identifier": "pt_address_name_ignored_preserving", + "positive_pattern": "identificação do endereço", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "id": [ + { + "pattern_identifier": "id_address_name_ignored_preserving", + "positive_pattern": "(label|judul|nama) alamat", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } ] }, - "COMPANY": { + "COMPANY_NAME": { "en": [ { "pattern_identifier": "en_company_preserving", @@ -210,7 +322,7 @@ "match_field_input_types": 1 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_company_preserving", "positive_pattern": "单位|公司", @@ -239,6 +351,16 @@ "match_field_attributes": 3, "match_field_input_types": 1 } + ], + "id": [ + { + "pattern_identifier": "id_company_preserving", + "positive_pattern": "(nama.?)?perusahaan", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } ] }, "ADDRESS_LINE_1": { @@ -253,7 +375,7 @@ }, { "pattern_identifier": "en_address_line_1_label_preserving", - "positive_pattern": "(^\\W*address)|(address\\W*$)|(?:shipping|billing|mailing|pick.?up|drop.?off|delivery|sender|postal|recipient|home|work|office|school|business|mail)[\\s\\-]+address|address\\s+(of|for|to|from)", + "positive_pattern": "(^\\W*address)|(address\\W*$)|(?:shipping|billing|mailing|pick.?up|drop.?off|delivery|sender|postal|recipient|home|work|office|school|business|mail)[\\s\\-]+address|address\\s+(of|for|to|from)|street.*(house|building|apartment|floor)|(house|building|apartment|floor).*street", "positive_score": 1.1, "negative_pattern": null, "match_field_attributes": 1, @@ -352,30 +474,30 @@ "negative_pattern": null, "match_field_attributes": 3, "match_field_input_types": 1 - } - ], - "zh": [ + }, { - "pattern_identifier": "zh_address_line_1_preserving", - "positive_pattern": "地址", + "pattern_identifier": "ru_address_line_1_label_preserving", + "positive_pattern": "улиц.*(дом|корпус|квартир|этаж)|(дом|корпус|квартир|этаж).*улиц", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": 3, + "match_field_attributes": 1, "match_field_input_types": 1 - }, + } + ], + "zh-CN": [ { - "pattern_identifier": "zh_address_line_1_label_preserving", + "pattern_identifier": "zh_address_line_1_preserving", "positive_pattern": "地址", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": 1, + "match_field_attributes": 3, "match_field_input_types": 1 } ], "tr": [ { "pattern_identifier": "tr_address_line_1_preserving", - "positive_pattern": "(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)", + "positive_pattern": "(\\b|_)adres(?! tarifi)(\\b|_)", "positive_score": 1.1, "negative_pattern": null, "match_field_attributes": 3, @@ -383,7 +505,7 @@ }, { "pattern_identifier": "tr_address_line_1_label_preserving", - "positive_pattern": "(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)", + "positive_pattern": "(\\b|_)adres(?! tarifi)(\\b|_)|(sokak|cadde).*(apartman|bina|daire|mahalle)|(apartman|bina|daire|mahalle).*(sokak|cadde)", "positive_score": 1.1, "negative_pattern": null, "match_field_attributes": 1, @@ -408,6 +530,25 @@ "match_field_attributes": 1, "match_field_input_types": 1 } + ], + "id": [ + { + "pattern_identifier": "id_address_line_1_preserving", + "positive_pattern": "^alamat", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + , + { + "pattern_identifier": "id_address_line_1_label_preserving", + "positive_pattern": "^alamat", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } ] }, "ADDRESS_LINE_2": { @@ -515,7 +656,7 @@ "match_field_input_types": 1 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_address_line_2_preserving", "positive_pattern": "地址2", @@ -647,7 +788,7 @@ "match_field_input_types": 137 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_country_preserving", "positive_pattern": "国家", @@ -686,6 +827,16 @@ "match_field_attributes": 3, "match_field_input_types": 137 } + ], + "id": [ + { + "pattern_identifier": "id_country_preserving", + "positive_pattern": "negara", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } ] }, "COUNTRY_LOCATION": { @@ -801,10 +952,20 @@ "match_field_input_types": 69 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_zip_code_preserving", - "positive_pattern": "邮政编码|邮编|郵遞區號", + "positive_pattern": "邮政编码|邮编", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "zh-TW": [ + { + "pattern_identifier": "zh_tw_zip_code_preserving", + "positive_pattern": "郵遞區號", "positive_score": 1.1, "negative_pattern": null, "match_field_attributes": 3, @@ -830,6 +991,16 @@ "match_field_attributes": 3, "match_field_input_types": 69 } + ], + "id": [ + { + "pattern_identifier": "id_zip_code_preserving", + "positive_pattern": "kode.?pos", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } ] }, "ZIP_4": { @@ -854,6 +1025,48 @@ } ] }, + "ADDRESS_HOME_DEPENDENT_LOCALITY": { + "en": [ + { + "pattern_identifier": "en_dependent_locality_preserving", + "positive_pattern": "neighbo(u)?rhood", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "pt": [ + { + "pattern_identifier": "pt_dependent_locality_preserving", + "positive_pattern": "bairro", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "tr": [ + { + "pattern_identifier": "tr_dependent_locality_preserving", + "positive_pattern": "mahalle|köy", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "id": [ + { + "pattern_identifier": "id_dependent_locality_preserving", + "positive_pattern": "kecamatan", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ] + }, "CITY": { "en": [ { @@ -928,14 +1141,14 @@ "ru": [ { "pattern_identifier": "ru_city_preserving", - "positive_pattern": "Город|Населённый.?пункт", + "positive_pattern": "Город|Насел(е|ё)нный.?пункт", "positive_score": 1.1, "negative_pattern": null, "match_field_attributes": 3, "match_field_input_types": 137 } ], - "zh": [ + "zh-TW": [ { "pattern_identifier": "zh_city_preserving", "positive_pattern": "市|分區", @@ -994,6 +1207,16 @@ "match_field_attributes": 3, "match_field_input_types": 137 } + ], + "id": [ + { + "pattern_identifier": "id_city_preserving", + "positive_pattern": "kota|kabupaten", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } ] }, "STATE": { @@ -1037,7 +1260,7 @@ "match_field_input_types": 137 } ], - "zh": [ + "zh-TW": [ { "pattern_identifier": "zh_state_preserving", "positive_pattern": "省|地區", @@ -1096,6 +1319,16 @@ "match_field_attributes": 3, "match_field_input_types": 137 } + ], + "id": [ + { + "pattern_identifier": "id_state_preserving", + "positive_pattern": "provinci", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } ] }, "SEARCH_TERM": { @@ -1119,7 +1352,7 @@ "match_field_input_types": 145 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_search_term_preserving", "positive_pattern": "搜索", @@ -1293,7 +1526,7 @@ "match_field_input_types": 1 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_name_on_card_preserving", "positive_pattern": "信用卡开户名|开户名|持卡人姓名|持卡人姓名", @@ -1302,6 +1535,16 @@ "match_field_attributes": 3, "match_field_input_types": 1 } + ], + "id": [ + { + "pattern_identifier": "id_name_on_card_preserving", + "positive_pattern": "nama.*kartu", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } ] }, "NAME_ON_CARD_CONTEXTUAL": { @@ -1357,10 +1600,20 @@ "match_field_input_types": 101 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_card_number_preserving", - "positive_pattern": "信用卡号|信用卡号码|信用卡卡號", + "positive_pattern": "信用卡号|信用卡号码", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ], + "zh-TW": [ + { + "pattern_identifier": "zh_card_number_preserving", + "positive_pattern": "信用卡卡號", "positive_score": 1.0, "negative_pattern": null, "match_field_attributes": 3, @@ -1406,6 +1659,16 @@ "match_field_attributes": 3, "match_field_input_types": 101 } + ], + "id": [ + { + "pattern_identifier": "id_card_number_preserving", + "positive_pattern": "no.*kartu", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } ] }, "CREDIT_CARD_VERIFICATION_CODE": { @@ -1501,7 +1764,7 @@ "match_field_input_types": 205 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_card_exp_month_preserving", "positive_pattern": "月", @@ -1510,6 +1773,16 @@ "match_field_attributes": 3, "match_field_input_types": 205 } + ], + "id": [ + { + "pattern_identifier": "id_card_exp_month_preserving", + "positive_pattern": "masa berlaku|berlaku hingga", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } ] }, "CREDIT_CARD_EXP_YEAR": { @@ -1583,7 +1856,7 @@ "match_field_input_types": 205 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_card_exp_year_preserving", "positive_pattern": "年|有效期", @@ -1698,6 +1971,16 @@ "match_field_attributes": 3, "match_field_input_types": 205 } + ], + "id": [ + { + "pattern_identifier": "id_card_exp_date_preserving", + "positive_pattern": "masa berlaku|berlaku hingga", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } ] }, "CREDIT_CARD_EXP_MONTH_BEFORE_YEAR": { @@ -1816,17 +2099,27 @@ "ru": [ { "pattern_identifier": "ru_email_preserving", - "positive_pattern": "Электронной.?Почты", + "positive_pattern": "Электронн(ая|ой).?Почт(а|ы)", "positive_score": 1.4, "negative_pattern": null, "match_field_attributes": 3, "match_field_input_types": 3 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_email_preserving", - "positive_pattern": "邮件|邮箱|電郵地址", + "positive_pattern": "邮件|邮箱", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "zh-TW": [ + { + "pattern_identifier": "zh_email_preserving", + "positive_pattern": "電郵地址", "positive_score": 1.4, "negative_pattern": null, "match_field_attributes": 3, @@ -1892,16 +2185,6 @@ "positive_score": 0.9, "negative_pattern": null, "match_field_attributes": 3, - "match_field_input_types": 137 - } - ], - "tr": [ - { - "pattern_identifier": "tr_name_ignored_preserving", - "positive_pattern": "adres başlığınız", - "positive_score": 0.9, - "negative_pattern": null, - "match_field_attributes": 3, "match_field_input_types": 137 } ], @@ -1915,7 +2198,7 @@ "match_field_input_types": 137 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_name_ignored_preserving", "positive_pattern": "用户名", @@ -1940,7 +2223,7 @@ "en": [ { "pattern_identifier": "en_full_name_preserving", - "positive_pattern": "^name|full.?name|your.?name|customer.?name|bill.?name|ship.?name|name.*first.*last|firstandlastname", + "positive_pattern": "^name|full.?name|your.?name|customer.?name|bill.?name|ship.?name|name.*first.*last|firstandlastname|contact.?(name|person)", "positive_score": 0.9, "negative_pattern": null, "match_field_attributes": 3, @@ -1960,7 +2243,7 @@ "fr": [ { "pattern_identifier": "fr_full_name_preserving", - "positive_pattern": "^nom(?!bre)", + "positive_pattern": "^nom(?![a-zA-Z])", "positive_score": 0.9, "negative_pattern": null, "match_field_attributes": 3, @@ -1997,7 +2280,7 @@ "match_field_input_types": 1 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_full_name_preserving", "positive_pattern": "姓名", @@ -2007,6 +2290,16 @@ "match_field_input_types": 1 } ], + "ru": [ + { + "pattern_identifier": "ru_full_name_preserving", + "positive_pattern": "контактное.?лицо", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], "tr": [ { "pattern_identifier": "tr_full_name_preserving", @@ -2026,6 +2319,16 @@ "match_field_attributes": 3, "match_field_input_types": 1 } + ], + "id": [ + { + "pattern_identifier": "id_full_name_preserving", + "positive_pattern": "nama.?(lengkap|penerima|kamu)", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } ] }, "NAME_SPECIFIC": { @@ -2180,6 +2483,16 @@ "match_field_attributes": 3, "match_field_input_types": 1 } + ], + "id": [ + { + "pattern_identifier": "id_first_name_preserving", + "positive_pattern": "nama depan", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } ] }, "MIDDLE_INITIAL": { @@ -2240,7 +2553,7 @@ "fr": [ { "pattern_identifier": "fr_last_name_preserving", - "positive_pattern": "famille|^nom(?!bre)", + "positive_pattern": "famille|^nom(?![a-zA-Z])", "positive_score": 0.9, "negative_pattern": null, "match_field_attributes": 3, @@ -2336,6 +2649,16 @@ "match_field_attributes": 3, "match_field_input_types": 1 } + ], + "id": [ + { + "pattern_identifier": "id_last_name_preserving", + "positive_pattern": "nama belakang", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } ] }, "LAST_NAME_FIRST": { @@ -2416,7 +2739,7 @@ "ru": [ { "pattern_identifier": "ru_honorific_prefix_preserving", - "positive_pattern": "обраще́ние|зва́ние", + "positive_pattern": "обращение|звание", "positive_score": 0.9, "negative_pattern": null, "match_field_attributes": 3, @@ -2535,7 +2858,7 @@ "match_field_input_types": 69 } ], - "zh": [ + "zh-CN": [ { "pattern_identifier": "zh_phone_preserving", "positive_pattern": "电话", @@ -2564,6 +2887,16 @@ "match_field_attributes": 3, "match_field_input_types": 69 } + ], + "id": [ + { + "pattern_identifier": "id_phone_preserving", + "positive_pattern": "telepon|ponsel|(nomor|no\\.?).?(hp|handphone)", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } ] }, "AUGMENTED_PHONE_COUNTRY_CODE": { diff --git a/chromium/components/autofill/core/browser/pattern_provider/test_pattern_provider.cc b/chromium/components/autofill/core/browser/pattern_provider/test_pattern_provider.cc deleted file mode 100644 index e6e1a075863..00000000000 --- a/chromium/components/autofill/core/browser/pattern_provider/test_pattern_provider.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2020 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 "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" - -#include "base/feature_list.h" -#include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h" -#include "components/autofill/core/common/autofill_features.h" - -namespace autofill { - -TestPatternProvider::TestPatternProvider() { - // TODO(crbug/1147608) This is an ugly hack to avoid loading the JSON. The - // motivation is that some Android unit tests fail because a dependency is - // missing. Instead of fixing this dependency, we'll go for an alternative - // solution that avoids the whole async/sync problem. - if (base::FeatureList::IsEnabled( - features::kAutofillUsePageLanguageToSelectFieldParsingPatterns) || - base::FeatureList::IsEnabled( - features:: - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics)) { - base::Optional<PatternProvider::Map> patterns = - field_type_parsing::GetPatternsFromResourceBundleSynchronously(); - if (patterns) - SetPatterns(patterns.value(), base::Version(), true); - - PatternProvider::SetPatternProviderForTesting(this); - } -} - -TestPatternProvider::~TestPatternProvider() { - PatternProvider::ResetPatternProvider(); -} - -} // namespace autofill diff --git a/chromium/components/autofill/core/browser/pattern_provider/test_pattern_provider.h b/chromium/components/autofill/core/browser/pattern_provider/test_pattern_provider.h deleted file mode 100644 index dc923237777..00000000000 --- a/chromium/components/autofill/core/browser/pattern_provider/test_pattern_provider.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020 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. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_TEST_PATTERN_PROVIDER_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_TEST_PATTERN_PROVIDER_H_ - -#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" - -namespace autofill { - -// The pattern provider to be used in tests. Loads the MatchingPattern -// configuration synchronously from the Resource Bundle and sets itself as the -// global PatternProvider. -class TestPatternProvider : public PatternProvider { - public: - TestPatternProvider(); - ~TestPatternProvider(); -}; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_TEST_PATTERN_PROVIDER_H_ diff --git a/chromium/components/autofill/core/browser/pattern_provider/transpile_default_regex_patterns.py b/chromium/components/autofill/core/browser/pattern_provider/transpile_default_regex_patterns.py new file mode 100755 index 00000000000..4bd93d80565 --- /dev/null +++ b/chromium/components/autofill/core/browser/pattern_provider/transpile_default_regex_patterns.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +# Copyright 2020 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. + +import io +import json +import sys + +def to_string_literal(json_string_literal): + return json.dumps(json_string_literal) + +def build_cpp_map_population(input): + lines = [] + def output(line): + lines.append(line) + + output('JsonPattern patterns[] = {') + for key1 in input: + for key2 in input[key1]: + for pattern in input[key1][key2]: + name = to_string_literal(key1) + language = to_string_literal(key2) + positive_pattern = pattern['positive_pattern'] + negative_pattern = pattern['negative_pattern'] + positive_score = pattern['positive_score'] + match_field_attributes = pattern['match_field_attributes'] + match_field_input_types = pattern['match_field_input_types'] + + positive_pattern = to_string_literal(positive_pattern) + + if negative_pattern is None: + negative_pattern = 'nullptr'; + else: + negative_pattern = to_string_literal(negative_pattern) + + # Shift to the right to match the MatchFieldTypes enum, which + # temporarily starts at 1<<2 instead of 1<<0. + match_field_input_types = '{} << 2'.format(match_field_input_types) + + output('{') + output('.name = {},'.format(name)) + output('.language = {},'.format(language)) + output('.positive_pattern = {},'.format(positive_pattern)) + output('.negative_pattern = {},'.format(negative_pattern)) + output('.positive_score = {},'.format(positive_score)) + output('.match_field_attributes = {},'.format(match_field_attributes)) + output('.match_field_input_types = {},'.format(match_field_input_types)) + output('},') + + output('};') + + return lines + + +def build_cpp_function(cpp, output_handle): + def output(s): + # unicode() exists and is necessary only in Python 2, not in Python 3. + if sys.version_info[0] < 3: + s = unicode(s, 'utf-8') + output_handle.write(s) + + output('// Copyright 2020 The Chromium Authors. All rights reserved.\n') + output('// Use of this source code is governed by a BSD-style license ') + output('that can be\n') + output('// found in the LICENSE file.\n') + output('\n') + output('#include "components/autofill/core/browser/pattern_provider/'\ + 'default_regex_patterns.h"\n') + output('#include "components/autofill/core/common/language_code.h"\n') + output('\n') + output('namespace autofill {\n') + output('\n') + output('PatternProvider::Map CreateDefaultRegexPatterns() {\n') + output(' struct JsonPattern {\n') + output(' const char* name;\n') + output(' const char* language;\n') + output(' const char* positive_pattern;\n') + output(' const char* negative_pattern;\n') + output(' float positive_score;\n') + output(' uint8_t match_field_attributes;\n') + output(' uint16_t match_field_input_types;\n') + output(' };\n') + output('\n') + for line in build_cpp_map_population(cpp): + output(line) + output('\n') + output(' PatternProvider::Map map;\n') + output(' size_t len = sizeof(patterns) / sizeof(patterns[0]);\n') + output(' for (size_t i = 0; i < len; ++i) {\n') + output(' const JsonPattern& p = patterns[i];\n') + output(' MatchingPattern mp;\n') + output(' mp.language = LanguageCode(p.language);\n') + output(' mp.positive_pattern = p.positive_pattern;\n') + output(' mp.negative_pattern = ' + 'p.negative_pattern ? p.negative_pattern : "";\n') + output(' mp.positive_score = p.positive_score;\n') + output(' mp.match_field_input_types = p.match_field_input_types;\n') + output(' mp.match_field_attributes = p.match_field_attributes;\n') + output(' map[p.name][LanguageCode(p.language)].push_back(mp);\n') + output(' }\n') + output(' return map;\n') + output('}\n') + output('\n') + output('}') + +if __name__ == '__main__': + input_file = sys.argv[1] + output_file = sys.argv[2] + with io.open(input_file, 'r', encoding='utf-8') as input_handle: + input_json = json.load(input_handle) + with io.open(output_file, 'w', encoding='utf-8') as output_handle: + build_cpp_function(input_json, output_handle) diff --git a/chromium/components/autofill/core/browser/payments/OWNERS b/chromium/components/autofill/core/browser/payments/OWNERS index c3fbe351b77..8b40deb104f 100644 --- a/chromium/components/autofill/core/browser/payments/OWNERS +++ b/chromium/components/autofill/core/browser/payments/OWNERS @@ -1 +1,2 @@ jsaul@google.com +siyua@chromium.org diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc index 0dbee443856..7784a1e9241 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc +++ b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.cc @@ -14,8 +14,10 @@ #include "base/timer/timer.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/data_model/autofill_offer_data.h" +#include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_payments_features.h" #include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" #include "url/gurl.h" @@ -66,6 +68,60 @@ void AutofillOfferManager::UpdateSuggestionsWithOffers( l10n_util::GetStringUTF16(IDS_AUTOFILL_OFFERS_CASHBACK); } } + // Sort the suggestions such that suggestions with offers are shown at the + // top. + if (base::FeatureList::IsEnabled( + features::kAutofillSortSuggestionsBasedOnOfferPresence)) { + std::sort(suggestions.begin(), suggestions.end(), + [](const Suggestion& a, const Suggestion& b) { + if (!a.offer_label.empty() && b.offer_label.empty()) { + return true; + } + return false; + }); + } +} + +bool AutofillOfferManager::IsUrlEligible(const GURL& last_committed_url) { + GURL last_committed_url_origin = last_committed_url.GetOrigin(); + return base::ranges::count(eligible_merchant_domains_, + last_committed_url_origin); +} + +std::tuple<std::vector<GURL>, GURL, CreditCard*> +AutofillOfferManager::GetEligibleDomainsAndCardForOfferForUrl( + const GURL& last_committed_url) { + std::vector<GURL> linked_domains; + std::vector<AutofillOfferData*> offers = + personal_data_->GetCreditCardOffers(); + CreditCard* card = nullptr; + // Initialize to an empty url. + GURL offer_details_url = GURL(); + + // Check which offer is eligible on current domain, then return the full set + // of domains for that offer. + for (auto* offer : offers) { + if (IsOfferEligible(*offer, last_committed_url.GetOrigin())) { + for (auto& domain : offer->merchant_domain) { + linked_domains.emplace_back(domain); + } + // Pick first card in the vector. The UI shows only one card's + // information. + card = offer->eligible_instrument_id.empty() + ? nullptr + : personal_data_->GetCreditCardByInstrumentId( + offer->eligible_instrument_id[0]); + offer_details_url = GURL(offer->offer_details_url); + break; + } + } + + // Remove duplicates in domains. + base::ranges::sort(linked_domains); + linked_domains.erase(base::ranges::unique(linked_domains), + linked_domains.end()); + + return std::make_tuple(linked_domains, offer_details_url, card); } void AutofillOfferManager::UpdateEligibleMerchantDomains() { diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h index df9fceef080..dab84da0594 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h +++ b/chromium/components/autofill/core/browser/payments/autofill_offer_manager.h @@ -8,6 +8,7 @@ #include <stdint.h> #include <map> #include <string> +#include <tuple> #include <vector> #include "base/strings/string16.h" @@ -22,6 +23,8 @@ namespace autofill { +class CreditCard; + // Manages all Autofill related offers. One per frame; owned by the // AutofillManager. class AutofillOfferManager : public KeyedService, @@ -42,7 +45,19 @@ class AutofillOfferManager : public KeyedService, void UpdateSuggestionsWithOffers(const GURL& last_committed_url, std::vector<Suggestion>& suggestions); + // Returns true only if the domain of |last_committed_url| has an offer. + bool IsUrlEligible(const GURL& last_committed_url); + + // Returns the set of domains and the card linked to a specific offer that + // contains the domain of |last_committed_url|. Also return the + // offer_details_url which redirects to a GPay surface with more details about + // the offer. + std::tuple<std::vector<GURL>, GURL, CreditCard*> + GetEligibleDomainsAndCardForOfferForUrl(const GURL& last_committed_url); + private: + FRIEND_TEST_ALL_PREFIXES(AutofillOfferManagerTest, IsUrlEligible); + // Queries |personal_data_| to reset the elements of // |eligible_merchant_domains_| void UpdateEligibleMerchantDomains(); diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc index 85f7277904a..24bac17c4c1 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc @@ -3,9 +3,11 @@ // found in the LICENSE file. #include <memory> +#include <tuple> #include "base/bind.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_test_utils.h" @@ -14,6 +16,7 @@ #include "components/autofill/core/browser/test_personal_data_manager.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_payments_features.h" #include "components/strings/grit/components_strings.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,10 +27,12 @@ namespace autofill { namespace { const char kTestGuid[] = "00000000-0000-0000-0000-000000000001"; +const char kTestGuid2[] = "00000000-0000-0000-0000-000000000002"; const char kTestNumber[] = "4234567890123456"; // Visa const char kTestUrl[] = "http://www.example.com/"; const char kTestUrlWithParam[] = "http://www.example.com/en/payments?name=checkout"; +const char kOfferDetailsUrl[] = "http://pay.google.com"; } // namespace @@ -41,6 +46,7 @@ class AutofillOfferManagerTest : public testing::Test { personal_data_manager_.Init(/*profile_database=*/database_, /*account_database=*/nullptr, /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, @@ -51,21 +57,25 @@ class AutofillOfferManagerTest : public testing::Test { } CreditCard CreateCreditCard(std::string guid, - std::string number = kTestNumber) { + std::string number = kTestNumber, + int64_t instrument_id = 0) { CreditCard card = CreditCard(); test::SetCreditCardInfo(&card, "Jane Doe", number.c_str(), test::NextMonth().c_str(), test::NextYear().c_str(), "1"); card.set_guid(guid); + card.set_instrument_id(instrument_id); card.set_record_type(CreditCard::MASKED_SERVER_CARD); personal_data_manager_.AddServerCreditCard(card); return card; } - void CreateCreditCardOfferForCard(const CreditCard& card, - std::string offer_reward_amount, - bool expired = false) { + AutofillOfferData CreateCreditCardOfferForCard( + const CreditCard& card, + std::string offer_reward_amount, + bool expired = false, + std::vector<GURL> domains = {GURL(kTestUrl)}) { AutofillOfferData offer_data; offer_data.offer_id = 4444; offer_data.offer_reward_amount = offer_reward_amount; @@ -74,9 +84,10 @@ class AutofillOfferManagerTest : public testing::Test { } else { offer_data.expiry = AutofillClock::Now() + base::TimeDelta::FromDays(2); } - offer_data.merchant_domain = {GURL(kTestUrl)}; + offer_data.merchant_domain = std::move(domains); offer_data.eligible_instrument_id = {card.instrument_id()}; - personal_data_manager_.AddCreditCardOfferData(offer_data); + offer_data.offer_details_url = GURL(kOfferDetailsUrl); + return offer_data; } protected: @@ -86,11 +97,13 @@ class AutofillOfferManagerTest : public testing::Test { scoped_refptr<AutofillWebDataService> database_; TestPersonalDataManager personal_data_manager_; std::unique_ptr<AutofillOfferManager> autofill_offer_manager_ = nullptr; + base::test::ScopedFeatureList scoped_feature_list_; }; TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_EligibleCashback) { CreditCard card = CreateCreditCard(kTestGuid); - CreateCreditCardOfferForCard(card, "5%"); + personal_data_manager_.AddCreditCardOfferData( + CreateCreditCardOfferForCard(card, "5%")); std::vector<Suggestion> suggestions = {Suggestion()}; suggestions[0].backend_id = kTestGuid; @@ -103,7 +116,8 @@ TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_EligibleCashback) { TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_ExpiredOffer) { CreditCard card = CreateCreditCard(kTestGuid); - CreateCreditCardOfferForCard(card, "5%", /*expired=*/true); + personal_data_manager_.AddCreditCardOfferData( + CreateCreditCardOfferForCard(card, "5%", /*expired=*/true)); std::vector<Suggestion> suggestions = {Suggestion()}; suggestions[0].backend_id = kTestGuid; @@ -115,7 +129,8 @@ TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_ExpiredOffer) { TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_WrongUrl) { CreditCard card = CreateCreditCard(kTestGuid); - CreateCreditCardOfferForCard(card, "5%"); + personal_data_manager_.AddCreditCardOfferData( + CreateCreditCardOfferForCard(card, "5%")); std::vector<Suggestion> suggestions = {Suggestion()}; suggestions[0].backend_id = kTestGuid; @@ -125,4 +140,150 @@ TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_WrongUrl) { EXPECT_TRUE(suggestions[0].offer_label.empty()); } +TEST_F(AutofillOfferManagerTest, + UpdateSuggestionsWithOffer_SuggestionsSortedByOfferPresence) { + CreditCard cardWithoutOffer = CreateCreditCard(kTestGuid); + CreditCard cardWithOffer = + CreateCreditCard(kTestGuid2, "4111111111111111", 100); + personal_data_manager_.AddCreditCardOfferData( + CreateCreditCardOfferForCard(cardWithOffer, "5%")); + + std::vector<Suggestion> suggestions = {Suggestion(), Suggestion()}; + suggestions[0].backend_id = kTestGuid; + suggestions[1].backend_id = kTestGuid2; + autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam), + suggestions); + + // offer_label was set on suggestions[1] but then was sorted to become + // suggestion[0] + EXPECT_TRUE(!suggestions[0].offer_label.empty()); + EXPECT_TRUE(suggestions[1].offer_label.empty()); + EXPECT_EQ(suggestions[0].backend_id, kTestGuid2); + EXPECT_EQ(suggestions[1].backend_id, kTestGuid); +} + +TEST_F(AutofillOfferManagerTest, + UpdateSuggestionsWithOffer_SuggestionsNotSortedByOfferPresence_ExpOff) { + scoped_feature_list_.Reset(); + scoped_feature_list_.InitAndDisableFeature( + features::kAutofillSortSuggestionsBasedOnOfferPresence); + CreditCard cardWithoutOffer = CreateCreditCard(kTestGuid); + CreditCard cardWithOffer = + CreateCreditCard(kTestGuid2, "4111111111111111", 100); + personal_data_manager_.AddCreditCardOfferData( + CreateCreditCardOfferForCard(cardWithOffer, "5%")); + + std::vector<Suggestion> suggestions = {Suggestion(), Suggestion()}; + suggestions[0].backend_id = kTestGuid; + suggestions[1].backend_id = kTestGuid2; + autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam), + suggestions); + + // offer_label was set on suggestions[1] and wasn't sorted because experiment + // is turned off. + EXPECT_TRUE(suggestions[0].offer_label.empty()); + EXPECT_TRUE(!suggestions[1].offer_label.empty()); + EXPECT_EQ(suggestions[0].backend_id, kTestGuid); + EXPECT_EQ(suggestions[1].backend_id, kTestGuid2); +} + +TEST_F(AutofillOfferManagerTest, + UpdateSuggestionsWithOffer_SuggestionsNotSortedIfAllCardsHaveOffers) { + CreditCard card1 = CreateCreditCard(kTestGuid, kTestNumber, 100); + CreditCard card2 = CreateCreditCard(kTestGuid2, "4111111111111111", 101); + personal_data_manager_.AddCreditCardOfferData( + CreateCreditCardOfferForCard(card1, "5%")); + personal_data_manager_.AddCreditCardOfferData( + CreateCreditCardOfferForCard(card2, "5%")); + + std::vector<Suggestion> suggestions = {Suggestion(), Suggestion()}; + suggestions[0].backend_id = kTestGuid; + suggestions[1].backend_id = kTestGuid2; + autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam), + suggestions); + + EXPECT_EQ(suggestions[0].backend_id, kTestGuid); + EXPECT_EQ(suggestions[1].backend_id, kTestGuid2); +} + +TEST_F(AutofillOfferManagerTest, IsUrlEligible) { + CreditCard card1 = CreateCreditCard(kTestGuid, kTestNumber, 100); + CreditCard card2 = CreateCreditCard(kTestGuid2, "4111111111111111", 101); + personal_data_manager_.AddCreditCardOfferData(CreateCreditCardOfferForCard( + card1, "5%", /*expired=*/false, + {GURL("http://www.google.com"), GURL("http://www.youtube.com")})); + personal_data_manager_.AddCreditCardOfferData(CreateCreditCardOfferForCard( + card2, "10%", /*expired=*/false, {GURL("http://maps.google.com")})); + autofill_offer_manager_->UpdateEligibleMerchantDomains(); + + EXPECT_TRUE( + autofill_offer_manager_->IsUrlEligible(GURL("http://www.google.com"))); + EXPECT_FALSE( + autofill_offer_manager_->IsUrlEligible(GURL("http://www.example.com"))); + EXPECT_TRUE( + autofill_offer_manager_->IsUrlEligible(GURL("http://maps.google.com"))); +} + +TEST_F(AutofillOfferManagerTest, + GetEligibleDomainsAndCardForOfferForUrl_ReturnNothingWhenFindNoMatch) { + CreditCard card1 = CreateCreditCard(kTestGuid, kTestNumber, 100); + personal_data_manager_.AddCreditCardOfferData(CreateCreditCardOfferForCard( + card1, "5%", /*expired=*/false, + {GURL("http://www.google.com"), GURL("http://www.youtube.com")})); + + auto result = + autofill_offer_manager_->GetEligibleDomainsAndCardForOfferForUrl( + GURL("http://www.example.com")); + EXPECT_EQ(0U, std::get<0>(result).size()); + EXPECT_EQ(GURL(), std::get<1>(result)); + EXPECT_EQ(nullptr, std::get<2>(result)); +} + +TEST_F(AutofillOfferManagerTest, + GetEligibleDomainsAndCardForOfferForUrl_ReturnCorrectSetWhenFindMatch) { + CreditCard card1 = CreateCreditCard(kTestGuid, kTestNumber, 100); + CreditCard card2 = CreateCreditCard(kTestGuid2, "4111111111111111", 101); + + personal_data_manager_.AddCreditCardOfferData(CreateCreditCardOfferForCard( + card1, "5%", /*expired=*/false, + /*domains=*/ + {GURL("http://www.google.com"), GURL("http://www.youtube.com")})); + personal_data_manager_.AddCreditCardOfferData(CreateCreditCardOfferForCard( + card2, "5%", /*expired=*/false, + /*domains=*/ + {GURL("http://www.example.com"), GURL("http://www.example2.com")})); + + auto result = + autofill_offer_manager_->GetEligibleDomainsAndCardForOfferForUrl( + GURL("http://www.example.com")); + std::vector<GURL> eligible_domain = std::get<0>(result); + EXPECT_EQ(2U, eligible_domain.size()); + EXPECT_NE(eligible_domain.end(), + std::find(eligible_domain.begin(), eligible_domain.end(), + GURL("http://www.example.com"))); + EXPECT_NE(eligible_domain.end(), + std::find(eligible_domain.begin(), eligible_domain.end(), + GURL("http://www.example2.com"))); + EXPECT_EQ(GURL(kOfferDetailsUrl), std::get<1>(result)); + EXPECT_EQ(101, std::get<2>(result)->instrument_id()); +} + +TEST_F( + AutofillOfferManagerTest, + GetEligibleDomainsAndCardForOfferForUrl_ReturnNoCardWhenFindNoMatchedCardData) { + CreditCard card1 = CreateCreditCard(kTestGuid, kTestNumber, 100); + AutofillOfferData offer_data1 = CreateCreditCardOfferForCard( + card1, "5%", /*expired=*/false, + {GURL("http://www.google.com"), GURL("http://www.youtube.com")}); + offer_data1.eligible_instrument_id.clear(); + personal_data_manager_.AddCreditCardOfferData(offer_data1); + + auto result = + autofill_offer_manager_->GetEligibleDomainsAndCardForOfferForUrl( + GURL("http://www.google.com")); + EXPECT_EQ(2U, std::get<0>(result).size()); + EXPECT_EQ(GURL(kOfferDetailsUrl), std::get<1>(result)); + EXPECT_EQ(nullptr, std::get<2>(result)); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.cc b/chromium/components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.cc new file mode 100644 index 00000000000..b1d73910379 --- /dev/null +++ b/chromium/components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.cc @@ -0,0 +1,103 @@ +// Copyright 2021 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 "components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.h" + +#include <utility> + +#include "base/check_op.h" +#include "base/notreached.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "build/branding_buildflags.h" +#include "components/autofill/core/browser/autofill_experiments.h" +#include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/common/autofill_constants.h" +#include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_payments_features.h" +#include "components/autofill/core/common/autofill_prefs.h" +#include "components/grit/components_scaled_resources.h" +#include "components/infobars/core/infobar.h" +#include "components/infobars/core/infobar_delegate.h" +#include "components/infobars/core/infobar_manager.h" +#include "components/prefs/pref_service.h" +#include "components/strings/grit/components_strings.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/window_open_disposition.h" +#include "url/gurl.h" + +namespace autofill { + +AutofillOfferNotificationInfoBarDelegateMobile:: + AutofillOfferNotificationInfoBarDelegateMobile( + const GURL& offer_details_url, + const CreditCard& card) + : credit_card_identifier_string_( + card.CardIdentifierStringForAutofillDisplay()), + network_icon_id_(CreditCard::IconResourceId(card.network())), + deep_link_url_(offer_details_url), + user_manually_closed_infobar_(false) { + AutofillMetrics::LogOfferNotificationInfoBarShown(); +} + +AutofillOfferNotificationInfoBarDelegateMobile:: + ~AutofillOfferNotificationInfoBarDelegateMobile() { + if (!user_manually_closed_infobar_) { + AutofillMetrics::LogOfferNotificationInfoBarResultMetric( + AutofillMetrics::OfferNotificationInfoBarResultMetric:: + OFFER_NOTIFICATION_INFOBAR_IGNORED); + } +} + +void AutofillOfferNotificationInfoBarDelegateMobile::OnOfferDeepLinkClicked( + GURL url) { + AutofillMetrics::LogOfferNotificationInfoBarDeepLinkClicked(); + infobar()->owner()->OpenURL(url, WindowOpenDisposition::NEW_FOREGROUND_TAB); +} + +int AutofillOfferNotificationInfoBarDelegateMobile::GetIconId() const { + return IDR_AUTOFILL_GOOGLE_PAY_WITH_DIVIDER; +} + +base::string16 AutofillOfferNotificationInfoBarDelegateMobile::GetMessageText() + const { + return l10n_util::GetStringUTF16(IDS_AUTOFILL_OFFERS_REMINDER_TITLE); +} + +infobars::InfoBarDelegate::InfoBarIdentifier +AutofillOfferNotificationInfoBarDelegateMobile::GetIdentifier() const { + return AUTOFILL_OFFER_NOTIFICATION_INFOBAR_DELEGATE; +} + +int AutofillOfferNotificationInfoBarDelegateMobile::GetButtons() const { + return BUTTON_OK; +} + +base::string16 AutofillOfferNotificationInfoBarDelegateMobile::GetButtonLabel( + InfoBarButton button) const { + if (button == BUTTON_OK) { + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_OFFERS_REMINDER_POSITIVE_BUTTON_LABEL); + } + + NOTREACHED() << "Unsupported button label requested: " << button; + return base::string16(); +} + +void AutofillOfferNotificationInfoBarDelegateMobile::InfoBarDismissed() { + AutofillMetrics::LogOfferNotificationInfoBarResultMetric( + AutofillMetrics::OfferNotificationInfoBarResultMetric:: + OFFER_NOTIFICATION_INFOBAR_CLOSED); + user_manually_closed_infobar_ = true; +} + +bool AutofillOfferNotificationInfoBarDelegateMobile::Accept() { + AutofillMetrics::LogOfferNotificationInfoBarResultMetric( + AutofillMetrics::OfferNotificationInfoBarResultMetric:: + OFFER_NOTIFICATION_INFOBAR_ACKNOWLEDGED); + user_manually_closed_infobar_ = true; + return true; +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.h b/chromium/components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.h new file mode 100644 index 00000000000..a4a0e967d9a --- /dev/null +++ b/chromium/components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.h @@ -0,0 +1,70 @@ +// Copyright 2021 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_AUTOFILL_OFFER_NOTIFICATION_INFOBAR_DELEGATE_MOBILE_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_AUTOFILL_OFFER_NOTIFICATION_INFOBAR_DELEGATE_MOBILE_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "build/build_config.h" +#include "components/autofill/core/browser/autofill_client.h" +#include "components/autofill/core/browser/autofill_metrics.h" +#include "components/autofill/core/browser/payments/legal_message_line.h" +#include "components/infobars/core/confirm_infobar_delegate.h" + +namespace autofill { + +class CreditCard; + +// An InfoBarDelegate that provides the information required to display an +// InfoBar when an offer is available on the merchant website. +class AutofillOfferNotificationInfoBarDelegateMobile + : public ConfirmInfoBarDelegate { + public: + AutofillOfferNotificationInfoBarDelegateMobile(const GURL& offer_details_url, + const CreditCard& card); + + ~AutofillOfferNotificationInfoBarDelegateMobile() override; + + AutofillOfferNotificationInfoBarDelegateMobile( + const AutofillOfferNotificationInfoBarDelegateMobile&) = delete; + AutofillOfferNotificationInfoBarDelegateMobile& operator=( + const AutofillOfferNotificationInfoBarDelegateMobile&) = delete; + + const base::string16& credit_card_identifier_string() const { + return credit_card_identifier_string_; + } + int network_icon_id() { return network_icon_id_; } + const GURL& deep_link_url() const { return deep_link_url_; } + + // Called when the offer details deep link was clicked. + virtual void OnOfferDeepLinkClicked(GURL url); + + // ConfirmInfoBarDelegate: + int GetIconId() const override; + base::string16 GetMessageText() const override; + infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override; + int GetButtons() const override; + base::string16 GetButtonLabel(InfoBarButton button) const override; + void InfoBarDismissed() override; + bool Accept() override; + + private: + // Identifier for the credit card associated with the offer. + base::string16 credit_card_identifier_string_; + // Resource id for the icon representing the network of the credit card. + int network_icon_id_; + // URL that links to the offer details page in the Google Pay app. + GURL deep_link_url_; + // Indicates whether the user manually closed the infobar by clicking on the X + // icon or the Got it button. + bool user_manually_closed_infobar_; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_AUTOFILL_OFFER_NOTIFICATION_INFOBAR_DELEGATE_MOBILE_H_ diff --git a/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc b/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc index 25ea9af5ec7..0ca6a1d2065 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc +++ b/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc @@ -13,7 +13,6 @@ #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/prefs/pref_service.h" -#include "components/sync/driver/sync_auth_util.h" #include "components/sync/driver/sync_driver_switches.h" #include "components/sync/driver/sync_service.h" #include "components/sync/driver/sync_user_settings.h" @@ -34,8 +33,6 @@ AutofillWalletModelTypeController::AutofillWalletModelTypeController( type == syncer::AUTOFILL_WALLET_METADATA || type == syncer::AUTOFILL_WALLET_OFFER); SubscribeToPrefChanges(); - // TODO(crbug.com/906995): remove this observing mechanism once all sync - // datatypes are stopped by ProfileSyncService, when sync is paused. sync_service_->AddObserver(this); } @@ -56,8 +53,6 @@ AutofillWalletModelTypeController::AutofillWalletModelTypeController( type == syncer::AUTOFILL_WALLET_METADATA || type == syncer::AUTOFILL_WALLET_OFFER); SubscribeToPrefChanges(); - // TODO(crbug.com/906995): remove this observing mechanism once all sync - // datatypes are stopped by ProfileSyncService, when sync is paused. sync_service_->AddObserver(this); } @@ -85,10 +80,6 @@ void AutofillWalletModelTypeController::Stop( syncer::DataTypeController::PreconditionState AutofillWalletModelTypeController::GetPreconditionState() const { DCHECK(CalledOnValidThread()); - // Not being in a persistent error state implies not being in a web signout - // state. - // TODO(https://crbug.com/819729): Add integration tests for web signout and - // other persistent auth errors. bool preconditions_met = pref_service_->GetBoolean( autofill::prefs::kAutofillWalletImportEnabled) && diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc index 9b38d40a594..560f30b57c8 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc @@ -30,7 +30,6 @@ #include "components/autofill/core/browser/payments/webauthn_callback_types.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/common/autofill_clock.h" -#include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_tick_clock.h" #include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" @@ -260,18 +259,16 @@ void CreditCardAccessManager::FetchCreditCard( return; } - if (base::FeatureList::IsEnabled(features::kAutofillCacheServerCardInfo)) { - // If card has been previously unmasked, use cached data. - std::unordered_map<std::string, CachedServerCardInfo>::iterator it = - unmasked_card_cache_.find(card->server_id()); - if (it != unmasked_card_cache_.end()) { // key is in cache - accessor->OnCreditCardFetched(/*did_succeed=*/true, - /*CreditCard=*/&it->second.card, - /*cvc=*/it->second.cvc); - base::UmaHistogramCounts1000("Autofill.UsedCachedServerCard", - ++it->second.cache_uses); - return; - } + // If card has been previously unmasked, use cached data. + std::unordered_map<std::string, CachedServerCardInfo>::iterator it = + unmasked_card_cache_.find(card->server_id()); + if (it != unmasked_card_cache_.end()) { // key is in cache + accessor->OnCreditCardFetched(/*did_succeed=*/true, + /*credit_card=*/&it->second.card, + /*cvc=*/it->second.cvc); + base::UmaHistogramCounts1000("Autofill.UsedCachedServerCard", + ++it->second.cache_uses); + return; } // Latency metrics should only be logged if the user is verifiable and the diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc index 68b1b1ccf5a..faa396e337d 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc @@ -147,6 +147,7 @@ class CreditCardAccessManagerTest : public testing::Test { personal_data_manager_.Init(/*profile_database=*/database_, /*account_database=*/nullptr, /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, @@ -1838,8 +1839,6 @@ TEST_F(CreditCardAccessManagerTest, AuthenticationInProgress) { // Ensures that the use of |unmasked_card_cache_| is set and logged correctly. TEST_F(CreditCardAccessManagerTest, FetchCreditCardUsesUnmaskedCardCache) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillCacheServerCardInfo); base::HistogramTester histogram_tester; CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false); CreditCard* unmasked_card = diff --git a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc index c5ce8b4d8ba..1f3916fe2e6 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc @@ -4,6 +4,8 @@ #include "components/autofill/core/browser/payments/credit_card_cvc_authenticator.h" +#include <memory> + #include "base/strings/string16.h" #include "build/build_config.h" #include "components/autofill/core/browser/autofill_client.h" @@ -28,11 +30,13 @@ void CreditCardCVCAuthenticator::Authenticate( PersonalDataManager* personal_data_manager, const base::TimeTicks& form_parsed_timestamp) { requester_ = requester; - if (!card) - return OnFullCardRequestFailed(); - full_card_request_.reset(new payments::FullCardRequest( + if (!card) { + return OnFullCardRequestFailed( + payments::FullCardRequest::FailureType::GENERIC_FAILURE); + } + full_card_request_ = std::make_unique<payments::FullCardRequest>( client_, client_->GetPaymentsClient(), personal_data_manager, - form_parsed_timestamp)); + form_parsed_timestamp); full_card_request_->GetFullCard(*card, AutofillClient::UNMASK_FOR_AUTOFILL, weak_ptr_factory_.GetWeakPtr(), weak_ptr_factory_.GetWeakPtr()); @@ -54,7 +58,8 @@ void CreditCardCVCAuthenticator::OnFullCardRequestSucceeded( .with_card_authorization_token(response.card_authorization_token)); } -void CreditCardCVCAuthenticator::OnFullCardRequestFailed() { +void CreditCardCVCAuthenticator::OnFullCardRequestFailed( + payments::FullCardRequest::FailureType failure_type) { requester_->OnCVCAuthenticationComplete( CVCAuthenticationResponse().with_did_succeed(false)); } diff --git a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h index fee486eb2fe..20fe78c46e8 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h +++ b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h @@ -95,7 +95,8 @@ class CreditCardCVCAuthenticator const payments::FullCardRequest& full_card_request, const CreditCard& card, const base::string16& cvc) override; - void OnFullCardRequestFailed() override; + void OnFullCardRequestFailed( + payments::FullCardRequest::FailureType failure_type) override; // payments::FullCardRequest::UIDelegate void ShowUnmaskPrompt(const CreditCard& card, diff --git a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc index 41b50dbab78..8430cb6dbb1 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc @@ -85,6 +85,7 @@ class CreditCardCVCAuthenticatorTest : public testing::Test { personal_data_manager_.Init(/*profile_database=*/database_, /*account_database=*/nullptr, /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc index d7b17fdd33d..51882104cf3 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc @@ -520,7 +520,8 @@ void CreditCardFIDOAuthenticator::OnFullCardRequestSucceeded( requester_->OnFIDOAuthenticationComplete(/*did_succeed=*/true, &card, cvc); } -void CreditCardFIDOAuthenticator::OnFullCardRequestFailed() { +void CreditCardFIDOAuthenticator::OnFullCardRequestFailed( + payments::FullCardRequest::FailureType failure_type) { DCHECK_EQ(AUTHENTICATION_FLOW, current_flow_); current_flow_ = NONE_FLOW; requester_->OnFIDOAuthenticationComplete(/*did_succeed=*/false); @@ -573,10 +574,13 @@ CreditCardFIDOAuthenticator::ParseCreationOptions( options->relying_party.icon_url = GURL(*icon_url); const std::string gaia = - autofill_client_->GetIdentityManager()->GetPrimaryAccountInfo().gaia; + autofill_client_->GetIdentityManager() + ->GetPrimaryAccountInfo(signin::ConsentLevel::kSync) + .gaia; options->user.id = std::vector<uint8_t>(gaia.begin(), gaia.end()); - options->user.name = - autofill_client_->GetIdentityManager()->GetPrimaryAccountInfo().email; + options->user.name = autofill_client_->GetIdentityManager() + ->GetPrimaryAccountInfo(signin::ConsentLevel::kSync) + .email; base::Optional<AccountInfo> account_info = autofill_client_->GetIdentityManager() diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h index 93b11355f9a..c857d8481c6 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h +++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h @@ -194,7 +194,8 @@ class CreditCardFIDOAuthenticator const payments::FullCardRequest& full_card_request, const CreditCard& card, const base::string16& cvc) override; - void OnFullCardRequestFailed() override; + void OnFullCardRequestFailed( + payments::FullCardRequest::FailureType failure_type) override; // Converts |request_options| from JSON to mojom pointer. PublicKeyCredentialRequestOptionsPtr ParseRequestOptions( diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc index effc89380ae..a8180c90fcb 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc @@ -114,6 +114,7 @@ class CreditCardFIDOAuthenticatorTest : public testing::Test { personal_data_manager_.Init(/*profile_database=*/database_, /*account_database=*/nullptr, /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc index 2addf2ad08f..dbd58bb9209 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc @@ -43,7 +43,6 @@ #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_util.h" -#include "components/infobars/core/infobar_feature.h" #include "components/prefs/pref_service.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "url/gurl.h" @@ -216,9 +215,8 @@ void CreditCardSaveManager::AttemptToOfferCardUploadSave( // iOS should always provide a valid expiration date when attempting to // upload a Saved Card. Calling LogSaveCardRequestExpirationDateReasonMetric // would trigger a DCHECK. - if (!(base::FeatureList::IsEnabled( - features::kAutofillSaveCardInfobarEditSupport) && - base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) { + if (!base::FeatureList::IsEnabled( + features::kAutofillSaveCardInfobarEditSupport)) { // Remove once both flags are deleted. LogSaveCardRequestExpirationDateReasonMetric(); } @@ -229,9 +227,8 @@ void CreditCardSaveManager::AttemptToOfferCardUploadSave( } #if defined(OS_IOS) - if ((base::FeatureList::IsEnabled( - features::kAutofillSaveCardInfobarEditSupport) && - base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) { + if (base::FeatureList::IsEnabled( + features::kAutofillSaveCardInfobarEditSupport)) { // iOS's new credit card save dialog requires the user to enter both // cardholder name and expiration date before saving. Regardless of what // Chrome thought it needed to do before, disable both of the previous @@ -776,9 +773,8 @@ int CreditCardSaveManager::GetDetectedValues() const { // card unless the user provides both a valid cardholder name and expiration // date. #if defined(OS_IOS) - if ((base::FeatureList::IsEnabled( - features::kAutofillSaveCardInfobarEditSupport) && - base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) { + if (base::FeatureList::IsEnabled( + features::kAutofillSaveCardInfobarEditSupport)) { detected_values |= DetectedValue::USER_PROVIDED_NAME; detected_values |= DetectedValue::USER_PROVIDED_EXPIRATION_DATE; } @@ -852,9 +848,8 @@ void CreditCardSaveManager::OnUserDidAcceptUploadHelper( // the user, but not through the fix flow triggered via // |should_request_name_from_user_|. DCHECK(should_request_name_from_user_ || - (base::FeatureList::IsEnabled( - autofill::features::kAutofillSaveCardInfobarEditSupport) && - base::FeatureList::IsEnabled(kIOSInfobarUIReboot))); + base::FeatureList::IsEnabled( + autofill::features::kAutofillSaveCardInfobarEditSupport)); #else DCHECK(should_request_name_from_user_); #endif @@ -873,9 +868,8 @@ void CreditCardSaveManager::OnUserDidAcceptUploadHelper( // the user, but not through the fix flow triggered via // |should_request_expiration_date_from_user_|. DCHECK(should_request_expiration_date_from_user_ || - (base::FeatureList::IsEnabled( - autofill::features::kAutofillSaveCardInfobarEditSupport) && - base::FeatureList::IsEnabled(kIOSInfobarUIReboot))); + base::FeatureList::IsEnabled( + autofill::features::kAutofillSaveCardInfobarEditSupport)); #else DCHECK(should_request_expiration_date_from_user_); #endif diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc index 1868db7fc2b..50fce2c7cf0 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc @@ -31,7 +31,6 @@ #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/mock_autocomplete_history_manager.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" #include "components/autofill/core/browser/payments/payments_customer_data.h" #include "components/autofill/core/browser/payments/test_credit_card_save_manager.h" #include "components/autofill/core/browser/payments/test_credit_card_save_strike_database.h" @@ -51,7 +50,6 @@ #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" -#include "components/infobars/core/infobar_feature.h" #include "components/prefs/pref_service.h" #include "components/sync/driver/test_sync_service.h" #include "components/ukm/test_ukm_recorder.h" @@ -132,6 +130,7 @@ class CreditCardSaveManagerTest : public testing::Test { personal_data_.Init(/*profile_database=*/database_, /*account_database=*/nullptr, /*pref_service=*/autofill_client_.GetPrefs(), + /*local_state=*/autofill_client_.GetPrefs(), /*identity_manager=*/nullptr, /*client_profile_validator=*/nullptr, /*history_service=*/nullptr, @@ -175,7 +174,7 @@ class CreditCardSaveManagerTest : public testing::Test { } void FormsSeen(const std::vector<FormData>& forms) { - autofill_manager_->OnFormsSeen(forms, base::TimeTicks()); + autofill_manager_->OnFormsSeen(forms); } void FormSubmitted(const FormData& form) { @@ -349,8 +348,6 @@ class CreditCardSaveManagerTest : public testing::Test { MockPersonalDataManager personal_data_; MockAutocompleteHistoryManager autocomplete_history_manager_; syncer::TestSyncService sync_service_; - base::test::ScopedFeatureList scoped_feature_list_; - TestPatternProvider test_pattern_provider_; // Ends up getting owned (and destroyed) by TestFormDataImporter: TestCreditCardSaveManager* credit_card_save_manager_; // Ends up getting owned (and destroyed) by TestAutofillClient: @@ -517,7 +514,6 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_OnlyCountryInAddresses) { EXPECT_EQ(1U, personal_data_.GetProfiles().size()); AutofillProfile only_country; only_country.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"), "en-US"); - only_country.FinalizeAfterImport(); EXPECT_EQ(1U, payments_client_->addresses_in_upload_details().size()); // AutofillProfile::Compare will ignore the difference in guid between our // actual profile being sent and the expected one constructed here. @@ -2961,9 +2957,8 @@ TEST_F(CreditCardSaveManagerTest, // iOS should always provide a valid expiration date when attempting to // upload a Saved Card due to the Messages SaveCard modal. The manager // shouldn't handle expired dates. - if ((base::FeatureList::IsEnabled( - features::kAutofillSaveCardInfobarEditSupport) && - base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) { + if (base::FeatureList::IsEnabled( + features::kAutofillSaveCardInfobarEditSupport)) { return; } #endif @@ -3013,9 +3008,8 @@ TEST_F(CreditCardSaveManagerTest, // iOS should always provide a valid expiration date when attempting to // upload a Saved Card due to the Messages SaveCard modal. The manager // shouldn't handle expired dates. - if ((base::FeatureList::IsEnabled( - features::kAutofillSaveCardInfobarEditSupport) && - base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) { + if (base::FeatureList::IsEnabled( + features::kAutofillSaveCardInfobarEditSupport)) { return; } #endif @@ -3065,9 +3059,8 @@ TEST_F(CreditCardSaveManagerTest, // iOS should always provide a valid expiration date when attempting to // upload a Saved Card due to the Messages SaveCard modal. The manager // shouldn't handle expired dates. - if ((base::FeatureList::IsEnabled( - features::kAutofillSaveCardInfobarEditSupport) && - base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) { + if (base::FeatureList::IsEnabled( + features::kAutofillSaveCardInfobarEditSupport)) { return; } #endif @@ -3117,9 +3110,8 @@ TEST_F(CreditCardSaveManagerTest, // iOS should always provide a valid expiration date when attempting to // upload a Saved Card due to the Messages SaveCard modal. The manager // shouldn't handle expired dates. - if ((base::FeatureList::IsEnabled( - features::kAutofillSaveCardInfobarEditSupport) && - base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) { + if (base::FeatureList::IsEnabled( + features::kAutofillSaveCardInfobarEditSupport)) { return; } #endif @@ -3170,9 +3162,8 @@ TEST_F( // iOS should always provide a valid expiration date when attempting to // upload a Saved Card due to the Messages SaveCard modal. The manager // shouldn't handle expired dates. - if ((base::FeatureList::IsEnabled( - features::kAutofillSaveCardInfobarEditSupport) && - base::FeatureList::IsEnabled(kIOSInfobarUIReboot))) { + if (base::FeatureList::IsEnabled( + features::kAutofillSaveCardInfobarEditSupport)) { return; } #endif diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_strike_database.cc b/chromium/components/autofill/core/browser/payments/credit_card_save_strike_database.cc index cc7f49a34f1..438e7e594f3 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_save_strike_database.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_save_strike_database.cc @@ -24,7 +24,7 @@ int CreditCardSaveStrikeDatabase::GetMaxStrikesLimit() { return 3; } -int64_t CreditCardSaveStrikeDatabase::GetExpiryTimeMicros() { +base::Optional<int64_t> CreditCardSaveStrikeDatabase::GetExpiryTimeMicros() { // Expiry time is 6 months. return (int64_t)1000000 * 60 * 60 * 24 * 180; } diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_strike_database.h b/chromium/components/autofill/core/browser/payments/credit_card_save_strike_database.h index 6e0a86536b1..6ce21bee328 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_save_strike_database.h +++ b/chromium/components/autofill/core/browser/payments/credit_card_save_strike_database.h @@ -22,7 +22,7 @@ class CreditCardSaveStrikeDatabase : public StrikeDatabaseIntegratorBase { std::string GetProjectPrefix() override; int GetMaxStrikesLimit() override; - int64_t GetExpiryTimeMicros() override; + base::Optional<int64_t> GetExpiryTimeMicros() override; bool UniqueIdsRequired() override; }; diff --git a/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.cc b/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.cc index 33e4db3a8b2..08926ea170e 100644 --- a/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.cc +++ b/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.cc @@ -30,7 +30,8 @@ int FidoAuthenticationStrikeDatabase::GetMaxStrikesLimit() { return 3; } -int64_t FidoAuthenticationStrikeDatabase::GetExpiryTimeMicros() { +base::Optional<int64_t> +FidoAuthenticationStrikeDatabase::GetExpiryTimeMicros() { // Expiry time is six months. return (int64_t)1000000 * 60 * 60 * 24 * 30 * 6; } diff --git a/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.h b/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.h index 343c5d576d2..5ee994ea02e 100644 --- a/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.h +++ b/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.h @@ -30,7 +30,7 @@ class FidoAuthenticationStrikeDatabase : public StrikeDatabaseIntegratorBase { std::string GetProjectPrefix() override; int GetMaxStrikesLimit() override; - int64_t GetExpiryTimeMicros() override; + base::Optional<int64_t> GetExpiryTimeMicros() override; bool UniqueIdsRequired() override; }; diff --git a/chromium/components/autofill/core/browser/payments/full_card_request.cc b/chromium/components/autofill/core/browser/payments/full_card_request.cc index a09cde2fded..16acda82fdc 100644 --- a/chromium/components/autofill/core/browser/payments/full_card_request.cc +++ b/chromium/components/autofill/core/browser/payments/full_card_request.cc @@ -82,7 +82,7 @@ void FullCardRequest::GetFullCard( // |result_delegate_| is already set, then immediately reject the new request // through the method parameter |result_delegate_|. if (result_delegate_) { - result_delegate_->OnFullCardRequestFailed(); + result_delegate_->OnFullCardRequestFailed(FailureType::GENERIC_FAILURE); return; } @@ -160,7 +160,7 @@ void FullCardRequest::OnUnmaskPromptAccepted( void FullCardRequest::OnUnmaskPromptClosed() { if (result_delegate_) - result_delegate_->OnFullCardRequestFailed(); + result_delegate_->OnFullCardRequestFailed(FailureType::PROMPT_CLOSED); Reset(); } @@ -216,10 +216,15 @@ void FullCardRequest::OnDidGetRealPan( // Neither PERMANENT_FAILURE nor NETWORK_ERROR allow retry. case AutofillClient::PERMANENT_FAILURE: - // Intentional fall through. + if (result_delegate_) { + result_delegate_->OnFullCardRequestFailed( + FailureType::VERIFICATION_DECLINED); + } + Reset(); + break; case AutofillClient::NETWORK_ERROR: { if (result_delegate_) - result_delegate_->OnFullCardRequestFailed(); + result_delegate_->OnFullCardRequestFailed(FailureType::GENERIC_FAILURE); Reset(); break; } diff --git a/chromium/components/autofill/core/browser/payments/full_card_request.h b/chromium/components/autofill/core/browser/payments/full_card_request.h index 20c9408860f..b09fbb8d213 100644 --- a/chromium/components/autofill/core/browser/payments/full_card_request.h +++ b/chromium/components/autofill/core/browser/payments/full_card_request.h @@ -32,6 +32,24 @@ namespace payments { // TODO(crbug/1061638): Refactor to use base::WaitableEvent where possible. class FullCardRequest final : public CardUnmaskDelegate { public: + // The type of failure. + enum FailureType { + // The user closed the prompt. The following scenarios are possible: + // 1) The user declined to enter their CVC and closed the prompt. + // 2) The user provided their CVC, got auth declined and then closed the + // prompt without attempting a second time. + // 3) The user provided their CVC and closed the prompt before waiting for + // the result. + PROMPT_CLOSED, + + // The card could not be looked up due to card auth declined or failed. + VERIFICATION_DECLINED, + + // The request failed for technical reasons, such as a closing page or lack + // of network connection. + GENERIC_FAILURE + }; + // The interface for receiving the full card details. class ResultDelegate { public: @@ -40,7 +58,7 @@ class FullCardRequest final : public CardUnmaskDelegate { const payments::FullCardRequest& full_card_request, const CreditCard& card, const base::string16& cvc) = 0; - virtual void OnFullCardRequestFailed() = 0; + virtual void OnFullCardRequestFailed(FailureType failure_type) = 0; }; // The delegate responsible for displaying the unmask prompt UI. diff --git a/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc b/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc index 909a3562728..d7967c39587 100644 --- a/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc @@ -42,7 +42,8 @@ class MockResultDelegate : public FullCardRequest::ResultDelegate, void(const payments::FullCardRequest&, const CreditCard&, const base::string16&)); - MOCK_METHOD0(OnFullCardRequestFailed, void()); + MOCK_METHOD1(OnFullCardRequestFailed, + void(payments::FullCardRequest::FailureType)); }; // The delegate responsible for displaying the unmask prompt UI. @@ -87,7 +88,8 @@ class FullCardRequestTest : public testing::Test { request_ = std::make_unique<FullCardRequest>( &autofill_client_, payments_client_.get(), &personal_data_); personal_data_.SetAccountInfoForPayments( - autofill_client_.GetIdentityManager()->GetPrimaryAccountInfo()); + autofill_client_.GetIdentityManager()->GetPrimaryAccountInfo( + signin::ConsentLevel::kSync)); // Silence the warning from PaymentsClient about matching sync and Payments // server types. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( @@ -402,7 +404,9 @@ TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForExpiredFullServerCard) { // Only one request at a time should be allowed. TEST_F(FullCardRequestTest, OneRequestAtATime) { - EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()); + EXPECT_CALL( + *result_delegate(), + OnFullCardRequestFailed(FullCardRequest::FailureType::GENERIC_FAILURE)); EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(_)).Times(0); @@ -418,7 +422,7 @@ TEST_F(FullCardRequestTest, OneRequestAtATime) { // After the first request completes, it's OK to start the second request. TEST_F(FullCardRequestTest, SecondRequestOkAfterFirstFinished) { - EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()).Times(0); + EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed(_)).Times(0); EXPECT_CALL( *result_delegate(), OnFullCardRequestSucceeded(testing::Ref(*request()), @@ -450,7 +454,9 @@ TEST_F(FullCardRequestTest, SecondRequestOkAfterFirstFinished) { // If the user cancels the CVC prompt, // FullCardRequest::Delegate::OnFullCardRequestFailed() should be invoked. TEST_F(FullCardRequestTest, ClosePromptWithoutUserInput) { - EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()); + EXPECT_CALL( + *result_delegate(), + OnFullCardRequestFailed(FullCardRequest::FailureType::PROMPT_CLOSED)); EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(_)).Times(0); @@ -464,7 +470,9 @@ TEST_F(FullCardRequestTest, ClosePromptWithoutUserInput) { // If the server provides an empty PAN with PERMANENT_FAILURE error, // FullCardRequest::Delegate::OnFullCardRequestFailed() should be invoked. TEST_F(FullCardRequestTest, PermanentFailure) { - EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()); + EXPECT_CALL(*result_delegate(), + OnFullCardRequestFailed( + FullCardRequest::FailureType::VERIFICATION_DECLINED)); EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(AutofillClient::PERMANENT_FAILURE)); @@ -483,7 +491,9 @@ TEST_F(FullCardRequestTest, PermanentFailure) { // If the server provides an empty PAN with NETWORK_ERROR error, // FullCardRequest::Delegate::OnFullCardRequestFailed() should be invoked. TEST_F(FullCardRequestTest, NetworkError) { - EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()); + EXPECT_CALL( + *result_delegate(), + OnFullCardRequestFailed(FullCardRequest::FailureType::GENERIC_FAILURE)); EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(AutofillClient::NETWORK_ERROR)); @@ -502,7 +512,9 @@ TEST_F(FullCardRequestTest, NetworkError) { // If the server provides an empty PAN with TRY_AGAIN_FAILURE, the user can // manually cancel out of the dialog. TEST_F(FullCardRequestTest, TryAgainFailureGiveUp) { - EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()); + EXPECT_CALL( + *result_delegate(), + OnFullCardRequestFailed(FullCardRequest::FailureType::PROMPT_CLOSED)); EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(AutofillClient::TRY_AGAIN_FAILURE)); @@ -521,7 +533,7 @@ TEST_F(FullCardRequestTest, TryAgainFailureGiveUp) { // If the server provides an empty PAN with TRY_AGAIN_FAILURE, the user can // correct their mistake and resubmit. TEST_F(FullCardRequestTest, TryAgainFailureRetry) { - EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()).Times(0); + EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed(_)).Times(0); EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( testing::Ref(*request()), diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc index ec61627e313..2f3dd6e8a35 100644 --- a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc +++ b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc @@ -10,6 +10,7 @@ #include <vector> #include "base/bind.h" +#include "base/containers/contains.h" #include "base/metrics/histogram_functions.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc index a7286883894..bb3b77081de 100644 --- a/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc @@ -28,7 +28,6 @@ #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/mock_autocomplete_history_manager.h" -#include "components/autofill/core/browser/pattern_provider/test_pattern_provider.h" #include "components/autofill/core/browser/payments/payments_customer_data.h" #include "components/autofill/core/browser/payments/test_credit_card_save_manager.h" #include "components/autofill/core/browser/payments/test_local_card_migration_manager.h" @@ -111,7 +110,7 @@ class LocalCardMigrationManagerTest : public testing::Test { } void FormsSeen(const std::vector<FormData>& forms) { - autofill_manager_->OnFormsSeen(forms, base::TimeTicks()); + autofill_manager_->OnFormsSeen(forms); } void FormSubmitted(const FormData& form) { @@ -314,7 +313,6 @@ class LocalCardMigrationManagerTest : public testing::Test { MockAutocompleteHistoryManager autocomplete_history_manager_; syncer::TestSyncService sync_service_; base::test::ScopedFeatureList scoped_feature_list_; - TestPatternProvider test_pattern_provider_; // Ends up getting owned (and destroyed) by TestAutofillClient: TestStrikeDatabase* strike_database_; // Ends up getting owned (and destroyed) by TestFormDataImporter: diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_strike_database.cc b/chromium/components/autofill/core/browser/payments/local_card_migration_strike_database.cc index 422cfa20532..f277e8944b4 100644 --- a/chromium/components/autofill/core/browser/payments/local_card_migration_strike_database.cc +++ b/chromium/components/autofill/core/browser/payments/local_card_migration_strike_database.cc @@ -30,14 +30,12 @@ int LocalCardMigrationStrikeDatabase::GetMaxStrikesLimit() { return 6; } -int64_t LocalCardMigrationStrikeDatabase::GetExpiryTimeMicros() { +base::Optional<int64_t> +LocalCardMigrationStrikeDatabase::GetExpiryTimeMicros() { // Ideally, we should be able to annotate cards deselected at migration time // as cards the user is not interested in uploading. Until then, we have been - // asked to not expire local card migration strikes based on a time limit. - // This option does not yet exist, so as a workaround the expiry time is set - // to the maximum amount (roughly 292,000 years). - // TODO(jsaul): Create an option to disable expiry time completely. - return std::numeric_limits<int64_t>::max(); + // asked to not expire local card migration strikes. + return base::nullopt; } bool LocalCardMigrationStrikeDatabase::UniqueIdsRequired() { diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_strike_database.h b/chromium/components/autofill/core/browser/payments/local_card_migration_strike_database.h index 08d228d071e..5f732c05928 100644 --- a/chromium/components/autofill/core/browser/payments/local_card_migration_strike_database.h +++ b/chromium/components/autofill/core/browser/payments/local_card_migration_strike_database.h @@ -31,7 +31,7 @@ class LocalCardMigrationStrikeDatabase : public StrikeDatabaseIntegratorBase { std::string GetProjectPrefix() override; int GetMaxStrikesLimit() override; - int64_t GetExpiryTimeMicros() override; + base::Optional<int64_t> GetExpiryTimeMicros() override; bool UniqueIdsRequired() override; }; diff --git a/chromium/components/autofill/core/browser/payments/payments_client.cc b/chromium/components/autofill/core/browser/payments/payments_client.cc index 7417d5ea3e1..06cd9bfdf62 100644 --- a/chromium/components/autofill/core/browser/payments/payments_client.cc +++ b/chromium/components/autofill/core/browser/payments/payments_client.cc @@ -218,11 +218,8 @@ base::Value BuildCreditCardDictionary(const CreditCard& credit_card, SetStringIfNotEmpty(credit_card, CREDIT_CARD_NAME_FULL, app_locale, "cardholder_name", card); - if (base::FeatureList::IsEnabled( - features::kAutofillEnableCardNicknameUpstream) && - credit_card.HasNonEmptyValidNickname()) { + if (credit_card.HasNonEmptyValidNickname()) card.SetKey("nickname", base::Value(credit_card.nickname())); - } card.SetKey("encrypted_pan", base::Value("__param:" + pan_field_name)); return card; @@ -825,9 +822,7 @@ class UploadCardRequest : public PaymentsRequest { if (base::StringToInt(exp_year, &value)) request_dict.SetKey("expiration_year", base::Value(value)); - if (base::FeatureList::IsEnabled( - features::kAutofillEnableCardNicknameUpstream) && - request_details_.card.HasNonEmptyValidNickname()) { + if (request_details_.card.HasNonEmptyValidNickname()) { request_dict.SetKey("nickname", base::Value(request_details_.card.nickname())); } diff --git a/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc index d96c2f31f4b..0ee7b134ddf 100644 --- a/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc @@ -374,7 +374,7 @@ class PaymentsClientTest : public testing::Test { profile.SetInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16(zip), "en-US"); profile.SetInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16(phone_number), "en-US"); - + profile.FinalizeAfterImport(); return profile; } }; @@ -905,9 +905,6 @@ TEST_F(PaymentsClientTest, UploadDoesNotIncludeCvcInRequestIfNotProvided) { } TEST_F(PaymentsClientTest, UploadIncludesCardNickname) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillEnableCardNicknameUpstream); - StartUploading(/*include_cvc=*/true, /*include_nickname=*/true); IssueOAuthToken(); @@ -917,21 +914,7 @@ TEST_F(PaymentsClientTest, UploadIncludesCardNickname) { std::string::npos); } -TEST_F(PaymentsClientTest, UploadDoesNotIncludeCardNicknameFlagDisabled) { - scoped_feature_list_.InitAndDisableFeature( - features::kAutofillEnableCardNicknameUpstream); - - StartUploading(/*include_cvc=*/true, /*include_nickname=*/true); - IssueOAuthToken(); - - // Card nickname was not set. - EXPECT_FALSE(GetUploadData().find("nickname") != std::string::npos); -} - TEST_F(PaymentsClientTest, UploadDoesNotIncludeCardNicknameEmptyNickname) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillEnableCardNicknameUpstream); - StartUploading(/*include_cvc=*/true, /*include_nickname=*/false); IssueOAuthToken(); @@ -1132,9 +1115,6 @@ TEST_F(PaymentsClientTest, } TEST_F(PaymentsClientTest, MigrationRequestIncludesCardNickname) { - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillEnableCardNicknameUpstream); - StartMigrating(/*has_cardholder_name=*/true, /*set_nickname_to_first_card=*/true); IssueOAuthToken(); @@ -1150,18 +1130,6 @@ TEST_F(PaymentsClientTest, MigrationRequestIncludesCardNickname) { EXPECT_FALSE(GetUploadData().find("nickname", pos + 1) != std::string::npos); } -TEST_F(PaymentsClientTest, MigrationRequestExcludesCardNicknameIfFlagDisabled) { - scoped_feature_list_.InitAndDisableFeature( - features::kAutofillEnableCardNicknameUpstream); - - StartMigrating(/*has_cardholder_name=*/true, - /*set_nickname_for_first_card=*/true); - IssueOAuthToken(); - - // Nickname was not set. - EXPECT_FALSE(GetUploadData().find("nickname") != std::string::npos); -} - TEST_F(PaymentsClientTest, MigrationSuccessWithSaveResult) { StartMigrating(/*has_cardholder_name=*/true); IssueOAuthToken(); diff --git a/chromium/components/autofill/core/browser/payments/strike_database_integrator_base.cc b/chromium/components/autofill/core/browser/payments/strike_database_integrator_base.cc index 039ea214d05..30f94046132 100644 --- a/chromium/components/autofill/core/browser/payments/strike_database_integrator_base.cc +++ b/chromium/components/autofill/core/browser/payments/strike_database_integrator_base.cc @@ -73,11 +73,15 @@ void StrikeDatabaseIntegratorBase::ClearAllStrikes() { } void StrikeDatabaseIntegratorBase::RemoveExpiredStrikes() { + if (!GetExpiryTimeMicros().has_value()) { + // Strikes don't expire. + return; + } std::vector<std::string> expired_keys; for (auto entry : strike_database_->strike_map_cache_) { if (AutofillClock::Now().ToDeltaSinceWindowsEpoch().InMicroseconds() - entry.second.last_update_timestamp() > - GetExpiryTimeMicros()) { + GetExpiryTimeMicros().value()) { if (strike_database_->GetStrikes(entry.first) > 0) { expired_keys.push_back(entry.first); base::UmaHistogramCounts1000( diff --git a/chromium/components/autofill/core/browser/payments/strike_database_integrator_base.h b/chromium/components/autofill/core/browser/payments/strike_database_integrator_base.h index 1600d4b973b..60538eaed43 100644 --- a/chromium/components/autofill/core/browser/payments/strike_database_integrator_base.h +++ b/chromium/components/autofill/core/browser/payments/strike_database_integrator_base.h @@ -57,14 +57,14 @@ class StrikeDatabaseIntegratorBase { protected: // Removes all strikes in which it has been longer than GetExpiryTimeMicros() // past |last_update_timestamp|. - // TODO(crbug/1061639): Provide option to NOT expire strikes, perhaps by - // GetExpiryTimeMicros() return a base::Optional. void RemoveExpiredStrikes(); private: FRIEND_TEST_ALL_PREFIXES(ChromeBrowsingDataRemoverDelegateTest, StrikeDatabaseEmptyOnAutofillRemoveEverything); FRIEND_TEST_ALL_PREFIXES(StrikeDatabaseIntegratorTestStrikeDatabaseTest, + NonExpiringStrikesDoNotExpire); + FRIEND_TEST_ALL_PREFIXES(StrikeDatabaseIntegratorTestStrikeDatabaseTest, RemoveExpiredStrikesTest); FRIEND_TEST_ALL_PREFIXES(StrikeDatabaseIntegratorTestStrikeDatabaseTest, GetKeyForStrikeDatabaseIntegratorUniqueIdTest); @@ -98,8 +98,9 @@ class StrikeDatabaseIntegratorBase { // opportunity stops being offered. virtual int GetMaxStrikesLimit() = 0; - // Returns the time after which the most recent strike should expire. - virtual int64_t GetExpiryTimeMicros() = 0; + // Returns the time after which the most recent strike should expire. If the + // Optional is empty, then strikes don't expire. + virtual base::Optional<int64_t> GetExpiryTimeMicros() = 0; // Returns whether or not a unique string identifier is required for every // strike in this project. diff --git a/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database.cc b/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database.cc index 6e6d37b9577..6cb36f3bfc4 100644 --- a/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database.cc +++ b/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database.cc @@ -12,6 +12,14 @@ const char kProjectPrefix[] = "StrikeDatabaseIntegratorTest"; const int kMaxStrikesLimit = 6; StrikeDatabaseIntegratorTestStrikeDatabase:: + StrikeDatabaseIntegratorTestStrikeDatabase( + StrikeDatabase* strike_database, + base::Optional<int64_t> expiry_time_micros) + : StrikeDatabaseIntegratorTestStrikeDatabase(strike_database) { + expiry_time_micros_ = expiry_time_micros; +} + +StrikeDatabaseIntegratorTestStrikeDatabase:: StrikeDatabaseIntegratorTestStrikeDatabase(StrikeDatabase* strike_database) : StrikeDatabaseIntegratorBase(strike_database) { RemoveExpiredStrikes(); @@ -28,9 +36,9 @@ int StrikeDatabaseIntegratorTestStrikeDatabase::GetMaxStrikesLimit() { return kMaxStrikesLimit; } -int64_t StrikeDatabaseIntegratorTestStrikeDatabase::GetExpiryTimeMicros() { - // Expiry time is 1 year. - return (int64_t)1000000 * 60 * 60 * 24 * 365; +base::Optional<int64_t> +StrikeDatabaseIntegratorTestStrikeDatabase::GetExpiryTimeMicros() { + return expiry_time_micros_; } bool StrikeDatabaseIntegratorTestStrikeDatabase::UniqueIdsRequired() { diff --git a/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database.h b/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database.h index efe4a4584d6..c26e36af08f 100644 --- a/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database.h +++ b/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database.h @@ -18,18 +18,25 @@ namespace autofill { class StrikeDatabaseIntegratorTestStrikeDatabase : public StrikeDatabaseIntegratorBase { public: - StrikeDatabaseIntegratorTestStrikeDatabase(StrikeDatabase* strike_database); + StrikeDatabaseIntegratorTestStrikeDatabase( + StrikeDatabase* strike_database, + base::Optional<int64_t> expiry_time_micros); + explicit StrikeDatabaseIntegratorTestStrikeDatabase( + StrikeDatabase* strike_database); ~StrikeDatabaseIntegratorTestStrikeDatabase() override; std::string GetProjectPrefix() override; int GetMaxStrikesLimit() override; - int64_t GetExpiryTimeMicros() override; + base::Optional<int64_t> GetExpiryTimeMicros() override; bool UniqueIdsRequired() override; void SetUniqueIdsRequired(bool unique_ids_required); private: bool unique_ids_required_ = false; + base::Optional<int64_t> expiry_time_micros_ = + static_cast<int64_t>(1000000) * 60 * 60 * 24 * + 365; // Default expiry time is 1 year. }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database_unittest.cc b/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database_unittest.cc index e35e0bd9d6a..e87e7df4a68 100644 --- a/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/strike_database_integrator_test_strike_database_unittest.cc @@ -36,6 +36,9 @@ class StrikeDatabaseIntegratorTestStrikeDatabaseTest : public ::testing::Test { strike_database_ = std::make_unique<StrikeDatabaseIntegratorTestStrikeDatabase>( strike_database_service_.get()); + no_expiry_strike_database_ = + std::make_unique<StrikeDatabaseIntegratorTestStrikeDatabase>( + strike_database_service_.get(), base::nullopt); } void TearDown() override { @@ -54,6 +57,8 @@ class StrikeDatabaseIntegratorTestStrikeDatabaseTest : public ::testing::Test { std::unique_ptr<leveldb_proto::ProtoDatabaseProvider> db_provider_; std::unique_ptr<StrikeDatabase> strike_database_service_; std::unique_ptr<StrikeDatabaseIntegratorTestStrikeDatabase> strike_database_; + std::unique_ptr<StrikeDatabaseIntegratorTestStrikeDatabase> + no_expiry_strike_database_; private: base::HistogramTester histogram_tester_; @@ -113,6 +118,22 @@ TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest, } TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest, + NonExpiringStrikesDoNotExpire) { + autofill::TestAutofillClock test_clock; + test_clock.SetNow(AutofillClock::Now()); + no_expiry_strike_database_->AddStrikes(1); + EXPECT_EQ(1, no_expiry_strike_database_->GetStrikes()); + + // Advance clock very far into the future. + test_clock.Advance(base::TimeDelta::FromDays(INT_MAX)); + + no_expiry_strike_database_->RemoveExpiredStrikes(); + + // Strike should not be removed. + EXPECT_EQ(1, no_expiry_strike_database_->GetStrikes()); +} + +TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest, RemoveExpiredStrikesTest) { autofill::TestAutofillClock test_clock; test_clock.SetNow(AutofillClock::Now()); @@ -121,7 +142,7 @@ TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest, // Advance clock to past expiry time. test_clock.Advance(base::TimeDelta::FromMicroseconds( - strike_database_->GetExpiryTimeMicros() + 1)); + strike_database_->GetExpiryTimeMicros().value() + 1)); // One strike should be removed. strike_database_->RemoveExpiredStrikes(); @@ -133,7 +154,7 @@ TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest, // Advance clock to past expiry time. test_clock.Advance(base::TimeDelta::FromMicroseconds( - strike_database_->GetExpiryTimeMicros() + 1)); + strike_database_->GetExpiryTimeMicros().value() + 1)); // Strike count should be one less than the max limit. strike_database_->RemoveExpiredStrikes(); @@ -149,7 +170,7 @@ TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest, // Advance clock to past expiry time. test_clock.Advance(base::TimeDelta::FromMicroseconds( - strike_database_->GetExpiryTimeMicros() + 1)); + strike_database_->GetExpiryTimeMicros().value() + 1)); // One strike should be removed. strike_database_->RemoveExpiredStrikes(); @@ -161,7 +182,7 @@ TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest, // Advance clock to past expiry time. test_clock.Advance(base::TimeDelta::FromMicroseconds( - strike_database_->GetExpiryTimeMicros() + 1)); + strike_database_->GetExpiryTimeMicros().value() + 1)); // Strike count should be one less than the max limit. strike_database_->RemoveExpiredStrikes(); @@ -283,7 +304,7 @@ TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest, // Advance clock to past the entry for |unique_id_1|'s expiry time. test_clock.Advance(base::TimeDelta::FromMicroseconds( - strike_database_->GetExpiryTimeMicros() + 1)); + strike_database_->GetExpiryTimeMicros().value() + 1)); strike_database_->AddStrike(unique_id_2); strike_database_->RemoveExpiredStrikes(); @@ -295,7 +316,7 @@ TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest, // Advance clock to past |unique_id_2|'s expiry time. test_clock.Advance(base::TimeDelta::FromMicroseconds( - strike_database_->GetExpiryTimeMicros() + 1)); + strike_database_->GetExpiryTimeMicros().value() + 1)); strike_database_->RemoveExpiredStrikes(); diff --git a/chromium/components/autofill/core/browser/payments/test_strike_database.h b/chromium/components/autofill/core/browser/payments/test_strike_database.h index 803a1f152e4..7fb5fb53a06 100644 --- a/chromium/components/autofill/core/browser/payments/test_strike_database.h +++ b/chromium/components/autofill/core/browser/payments/test_strike_database.h @@ -12,6 +12,7 @@ #include <vector> #include "components/autofill/core/browser/payments/strike_database.h" +#include "components/autofill/core/browser/proto/strike_data.pb.h" namespace autofill { diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc index 11d891b1282..caabfdf41a8 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager.cc @@ -15,6 +15,7 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/containers/contains.h" #include "base/feature_list.h" #include "base/i18n/case_conversion.h" #include "base/i18n/timezone.h" @@ -26,6 +27,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "build/chromeos_buildflags.h" #include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/autofill_download_manager.h" #include "components/autofill/core/browser/autofill_experiments.h" @@ -117,12 +119,6 @@ bool FindByContents(const C& container, const T& needle) { }); } -bool IsSyncEnabledFor(const syncer::SyncService* sync_service, - syncer::ModelType model_type) { - return sync_service != nullptr && sync_service->CanSyncFeatureStart() && - sync_service->GetPreferredDataTypes().Has(model_type); -} - // Receives the loaded profiles from the web data service and stores them in // |*dest|. The pending handle is the address of the pending handle // corresponding to this request type. This function is used to save bShouldoth @@ -263,8 +259,7 @@ PersonalDataManager::PersonalDataManager( const std::string& app_locale, const std::string& variations_country_code) : app_locale_(app_locale), - variations_country_code_(variations_country_code), - test_data_creator_(kDisusedDataModelDeletionTimeDelta, app_locale_) { + variations_country_code_(variations_country_code) { database_helper_ = std::make_unique<PersonalDatabaseHelper>(this); } @@ -275,6 +270,7 @@ void PersonalDataManager::Init( scoped_refptr<AutofillWebDataService> profile_database, scoped_refptr<AutofillWebDataService> account_database, PrefService* pref_service, + PrefService* local_state, signin::IdentityManager* identity_manager, AutofillProfileValidator* client_profile_validator, history::HistoryService* history_service, @@ -291,10 +287,14 @@ void PersonalDataManager::Init( base::BindRepeating(&PersonalDataManager::ResetProfileValidity, base::Unretained(this))); + alternative_state_name_map_updater_ = + std::make_unique<AlternativeStateNameMapUpdater>(local_state, this); + AddObserver(alternative_state_name_map_updater_.get()); + // Listen for URL deletions from browsing history. history_service_ = history_service; if (history_service_) - history_service_->AddObserver(this); + history_service_observation_.Observe(history_service_); // Listen for account cookie deletion by the user. identity_manager_ = identity_manager; @@ -324,14 +324,8 @@ void PersonalDataManager::Init( Refresh(); - // Check if profile cleanup has already been performed this major version. - is_autofill_profile_cleanup_pending_ = - pref_service_->GetInteger(prefs::kAutofillLastVersionDeduped) >= - CHROME_VERSION_MAJOR; - DVLOG(1) << "Autofill profile cleanup " - << (is_autofill_profile_cleanup_pending_ ? "needs to be" - : "has already been") - << " performed for this version"; + personal_data_manager_cleaner_ = std::make_unique<PersonalDataManagerCleaner>( + this, alternative_state_name_map_updater_.get(), pref_service); } PersonalDataManager::~PersonalDataManager() { @@ -339,6 +333,9 @@ PersonalDataManager::~PersonalDataManager() { CancelPendingLocalQuery(&pending_creditcards_query_); CancelPendingLocalQuery(&pending_upi_ids_query_); CancelPendingServerQueries(); + + if (alternative_state_name_map_updater_) + RemoveObserver(alternative_state_name_map_updater_.get()); } void PersonalDataManager::Shutdown() { @@ -347,7 +344,7 @@ void PersonalDataManager::Shutdown() { sync_service_ = nullptr; if (history_service_) - history_service_->RemoveObserver(this); + history_service_observation_.Reset(); history_service_ = nullptr; if (identity_manager_) @@ -494,24 +491,12 @@ void PersonalDataManager::OnWebDataServiceRequestDone( if (!HasPendingQueries() && database_helper_->GetServerDatabase()) { // On initial data load, is_data_loaded_ will be false here. if (!is_data_loaded_) { - // If sync is enabled for addresses, defer running cleanups until address - // sync has started; otherwise, do it now. - if (!IsSyncEnabledFor(sync_service_, syncer::AUTOFILL_PROFILE)) - ApplyAddressFixesAndCleanups(); - - // If sync is enabled for credit cards, defer running cleanups until card - // sync has started; otherwise, do it now. - if (!IsSyncEnabledFor(sync_service_, syncer::AUTOFILL_WALLET_DATA)) - ApplyCardFixesAndCleanups(); - - // Log address, credit card and offer startup metrics. - LogStoredProfileMetrics(); - LogStoredCreditCardMetrics(); - LogStoredOfferMetrics(); + is_data_loaded_ = true; + personal_data_manager_cleaner_ + ->CleanupDataAndNotifyPersonalDataObservers(); + } else { + NotifyPersonalDataObserver(); } - - is_data_loaded_ = true; - NotifyPersonalDataObserver(); } } @@ -530,15 +515,7 @@ void PersonalDataManager::AutofillAddressConversionCompleted() { } void PersonalDataManager::SyncStarted(syncer::ModelType model_type) { - // Run deferred autofill address profile startup code. - // See: OnSyncServiceInitialized - if (model_type == syncer::AUTOFILL_PROFILE) - ApplyAddressFixesAndCleanups(); - - // Run deferred credit card startup code. - // See: OnSyncServiceInitialized - if (model_type == syncer::AUTOFILL_WALLET_DATA) - ApplyCardFixesAndCleanups(); + personal_data_manager_cleaner_->SyncStarted(model_type); } void PersonalDataManager::OnStateChanged(syncer::SyncService* sync_service) { @@ -565,7 +542,8 @@ CoreAccountInfo PersonalDataManager::GetAccountInfoForPaymentsServer() const { // the user has disabled sync. // In both cases, the AccountInfo will be empty if the user is not signed in. return sync_service_ ? sync_service_->GetAuthenticatedAccountInfo() - : identity_manager_->GetPrimaryAccountInfo(); + : identity_manager_->GetPrimaryAccountInfo( + signin::ConsentLevel::kSync); } // TODO(crbug.com/903914): Clean up this function so that it's more clear what @@ -583,8 +561,6 @@ void PersonalDataManager::OnAccountsCookieDeletedByUserAction() { ::autofill::prefs::ClearSyncTransportOptIns(pref_service_); } -// TODO(crbug.com/903896): Generalize this to all the possible states relavant -// to Autofill. AutofillSyncSigninState PersonalDataManager::GetSyncSigninState() const { // Check if the user is signed out. if (!sync_service_ || !identity_manager_ || @@ -592,21 +568,16 @@ AutofillSyncSigninState PersonalDataManager::GetSyncSigninState() const { return AutofillSyncSigninState::kSignedOut; } - // Check if the user has turned on sync. - if (sync_service_->IsSyncFeatureEnabled()) { - // TODO(crbug.com/906995): Remove this once the kStopSyncInPausedState - // feature is launched. - if (syncer::IsWebSignout(sync_service_->GetAuthError())) { - return AutofillSyncSigninState::kSyncPaused; - } - return AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled; - } - if (sync_service_->GetTransportState() == syncer::SyncService::TransportState::PAUSED) { return AutofillSyncSigninState::kSyncPaused; } + // Check if the user has turned on sync. + if (sync_service_->IsSyncFeatureEnabled()) { + return AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled; + } + // Check if the feature is enabled and if Wallet data types are supported. if (base::FeatureList::IsEnabled( features::kAutofillEnableAccountWalletStorage) && @@ -631,46 +602,58 @@ void PersonalDataManager::MarkObserversInsufficientFormDataForImport() { observer.OnInsufficientFormData(); } -void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) { +void PersonalDataManager::RecordUseOf( + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card) { if (is_off_the_record_) return; - CreditCard* credit_card = GetCreditCardByGUID(data_model.guid()); - if (credit_card) { - credit_card->RecordAndLogUse(); + if (absl::holds_alternative<const CreditCard*>(profile_or_credit_card)) { + CreditCard* credit_card = GetCreditCardByGUID( + absl::get<const CreditCard*>(profile_or_credit_card)->guid()); - if (credit_card->record_type() == CreditCard::LOCAL_CARD) { - // Fail silently if there's no local database, because we need to support - // this for tests. - if (database_helper_->GetLocalDatabase()) { - database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card); + if (credit_card) { + credit_card->RecordAndLogUse(); + + if (credit_card->record_type() == CreditCard::LOCAL_CARD) { + // Fail silently if there's no local database, because we need to + // support this for tests. + if (database_helper_->GetLocalDatabase()) { + database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card); + } + } else { + DCHECK(database_helper_->GetServerDatabase()) + << "Recording use of server card without server storage."; + database_helper_->GetServerDatabase()->UpdateServerCardMetadata( + *credit_card); } - } else { - DCHECK(database_helper_->GetServerDatabase()) - << "Recording use of server card without server storage."; - database_helper_->GetServerDatabase()->UpdateServerCardMetadata( - *credit_card); - } - Refresh(); - return; + Refresh(); + return; + } } - AutofillProfile* profile = GetProfileByGUID(data_model.guid()); - if (profile) { - profile->RecordAndLogUse(); + if (absl::holds_alternative<const AutofillProfile*>(profile_or_credit_card)) { + // TODO(crbug.com/941498): Server profiles are not recorded therefore + // GetProfileByGUID returns null for them. + AutofillProfile* profile = GetProfileByGUID( + absl::get<const AutofillProfile*>(profile_or_credit_card)->guid()); - switch (profile->record_type()) { - case AutofillProfile::LOCAL_PROFILE: - UpdateProfileInDB(*profile, /*enforced=*/true); - break; - case AutofillProfile::SERVER_PROFILE: - DCHECK(database_helper_->GetServerDatabase()) - << "Recording use of server address without server storage."; - database_helper_->GetServerDatabase()->UpdateServerAddressMetadata( - *profile); - Refresh(); - break; + if (profile) { + profile->RecordAndLogUse(); + + switch (profile->record_type()) { + case AutofillProfile::LOCAL_PROFILE: + UpdateProfileInDB(*profile, /*enforced=*/true); + break; + case AutofillProfile::SERVER_PROFILE: + DCHECK(database_helper_->GetServerDatabase()) + << "Recording use of server address without server storage."; + database_helper_->GetServerDatabase()->UpdateServerAddressMetadata( + *profile); + Refresh(); + break; + } } } } @@ -862,17 +845,19 @@ void PersonalDataManager::UpdateServerCreditCard( Refresh(); } -void PersonalDataManager::UpdateServerCardMetadata( - const CreditCard& credit_card) { - DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); - +void PersonalDataManager::UpdateServerCardsMetadata( + const std::vector<CreditCard>& credit_cards) { if (is_off_the_record_) return; DCHECK(database_helper_->GetServerDatabase()) << "Updating server card metadata without server storage."; - database_helper_->GetServerDatabase()->UpdateServerCardMetadata(credit_card); + for (const auto& credit_card : credit_cards) { + DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); + database_helper_->GetServerDatabase()->UpdateServerCardMetadata( + credit_card); + } Refresh(); } @@ -950,6 +935,11 @@ void PersonalDataManager::SetSyncServiceForTest( sync_service_->AddObserver(this); } +void PersonalDataManager::AddOfferDataForTest( + std::unique_ptr<AutofillOfferData> offer_data) { + autofill_offer_data_.push_back(std::move(offer_data)); +} + void PersonalDataManager:: RemoveAutofillProfileByGUIDAndBlankCreditCardReference( const std::string& guid) { @@ -1007,6 +997,16 @@ CreditCard* PersonalDataManager::GetCreditCardByNumber( return nullptr; } +CreditCard* PersonalDataManager::GetCreditCardByInstrumentId( + int64_t instrument_id) { + const std::vector<CreditCard*> credit_cards = GetCreditCards(); + for (CreditCard* credit_card : credit_cards) { + if (credit_card->instrument_id() == instrument_id) + return credit_card; + } + return nullptr; +} + void PersonalDataManager::GetNonEmptyTypes( ServerFieldTypeSet* non_empty_types) const { for (AutofillProfile* profile : GetProfiles()) @@ -1290,8 +1290,6 @@ std::vector<Suggestion> PersonalDataManager::GetProfileSuggestions( return unique_suggestions; } -// TODO(crbug.com/613187): Investigate if it would be more efficient to dedupe -// with a vector instead of a list. const std::vector<CreditCard*> PersonalDataManager::GetCreditCardsToSuggest( bool include_server_cards) const { if (!IsAutofillCreditCardEnabled()) @@ -1438,32 +1436,6 @@ void PersonalDataManager::SetPrefService(PrefService* pref_service) { } } -void PersonalDataManager::ClearProfileNonSettingsOrigins() { - for (AutofillProfile* profile : GetProfiles()) { - if (profile->origin() != kSettingsOrigin && !profile->origin().empty()) { - profile->set_origin(std::string()); - UpdateProfileInDB(*profile, /*enforced=*/true); - } - } -} - -void PersonalDataManager::ClearCreditCardNonSettingsOrigins() { - bool has_updated = false; - - for (CreditCard* card : GetLocalCreditCards()) { - if (card->origin() != kSettingsOrigin && !card->origin().empty()) { - card->set_origin(std::string()); - database_helper_->GetLocalDatabase()->UpdateCreditCard(*card); - has_updated = true; - } - } - - // Refresh the local cache and send notifications to observers if a changed - // was made. - if (has_updated) - Refresh(); -} - void PersonalDataManager::OnValidated(const AutofillProfile* profile) { if (!profile) return; @@ -1957,7 +1929,9 @@ bool PersonalDataManager::IsServerCard(const CreditCard* credit_card) const { bool PersonalDataManager::ShouldShowCardsFromAccountOption() const { // The feature is only for Linux, Windows and Mac. -#if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_WIN) || \ +// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch +// of lacros-chrome is complete. +#if (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) || defined(OS_WIN) || \ defined(OS_APPLE) // This option should only be shown for users that have not enabled the Sync // Feature and that have server credit cards available. @@ -1981,8 +1955,8 @@ bool PersonalDataManager::ShouldShowCardsFromAccountOption() const { return !is_opted_in; #else return false; -#endif // #if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_WIN) || \ - // defined(OS_APPLE) +#endif // #if (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) || + // defined(OS_WIN) || defined(OS_APPLE) } void PersonalDataManager::OnUserAcceptedCardsFromAccountOption() { @@ -2164,206 +2138,6 @@ std::vector<Suggestion> PersonalDataManager::GetSuggestionsForCards( return suggestions; } -void PersonalDataManager::RemoveOrphanAutofillTableRows() { - // Don't run if the fix has already been applied. - if (pref_service_->GetBoolean(prefs::kAutofillOrphanRowsRemoved)) - return; - - if (!database_helper_->GetLocalDatabase()) - return; - - database_helper_->GetLocalDatabase()->RemoveOrphanAutofillTableRows(); - - // Set the pref so that this fix is never run again. - pref_service_->SetBoolean(prefs::kAutofillOrphanRowsRemoved, true); -} - -bool PersonalDataManager::ApplyDedupingRoutine() { - if (!is_autofill_profile_cleanup_pending_) - return false; - - is_autofill_profile_cleanup_pending_ = false; - - // No need to de-duplicate if there are less than two profiles. - if (web_profiles_.size() < 2) { - DVLOG(1) << "Autofill profile de-duplication not needed."; - return false; - } - - // Check if de-duplication has already been performed this major version. - if (pref_service_->GetInteger(prefs::kAutofillLastVersionDeduped) >= - CHROME_VERSION_MAJOR) { - DVLOG(1) - << "Autofill profile de-duplication already performed for this version"; - return false; - } - - DVLOG(1) << "Starting autofill profile de-duplication."; - std::unordered_set<std::string> profiles_to_delete; - profiles_to_delete.reserve(web_profiles_.size()); - - // Create the map used to update credit card's billing addresses after the - // dedupe. - std::unordered_map<std::string, std::string> guids_merge_map; - - // The changes can't happen directly on the web_profiles_, but need to be - // updated in the database at first, and then updated on the web_profiles_. - // Therefore, we need a copy of web_profiles_ to keep track of the changes. - std::vector<std::unique_ptr<AutofillProfile>> new_profiles; - for (const auto& it : web_profiles_) { - new_profiles.push_back(std::make_unique<AutofillProfile>(*(it.get()))); - } - - DedupeProfiles(&new_profiles, &profiles_to_delete, &guids_merge_map); - - // Apply the profile changes to the database. - for (const auto& profile : new_profiles) { - // If the profile was set to be deleted, remove it from the database, - // otherwise update it. - if (profiles_to_delete.count(profile->guid())) { - RemoveProfileFromDB(profile->guid()); - } else { - UpdateProfileInDB(*(profile.get())); - } - } - - UpdateCardsBillingAddressReference(guids_merge_map); - - // Set the pref to the current major version. - pref_service_->SetInteger(prefs::kAutofillLastVersionDeduped, - CHROME_VERSION_MAJOR); - return true; -} - -void PersonalDataManager::DedupeProfiles( - std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, - std::unordered_set<std::string>* profiles_to_delete, - std::unordered_map<std::string, std::string>* guids_merge_map) const { - AutofillMetrics::LogNumberOfProfilesConsideredForDedupe( - existing_profiles->size()); - - // Sort the profiles by frecency with all the verified profiles at the end. - // That way the most relevant profiles will get merged into the less relevant - // profiles, which keeps the syntax of the most relevant profiles data. - // Verified profiles are put at the end because they do not merge into other - // profiles, so the loop can be stopped when we reach those. However they need - // to be in the vector because an unverified profile trying to merge into a - // similar verified profile will be discarded. - base::Time comparison_time = AutofillClock::Now(); - std::sort(existing_profiles->begin(), existing_profiles->end(), - [comparison_time](const std::unique_ptr<AutofillProfile>& a, - const std::unique_ptr<AutofillProfile>& b) { - if (a->IsVerified() != b->IsVerified()) - return !a->IsVerified(); - return a->HasGreaterFrecencyThan(b.get(), comparison_time); - }); - - AutofillProfileComparator comparator(app_locale_); - - for (size_t i = 0; i < existing_profiles->size(); ++i) { - AutofillProfile* profile_to_merge = (*existing_profiles)[i].get(); - - // If the profile was set to be deleted, skip it. It has already been - // merged into another profile. - if (profiles_to_delete->count(profile_to_merge->guid())) - continue; - - // If we have reached the verified profiles, stop trying to merge. Verified - // profiles do not get merged. - if (profile_to_merge->IsVerified()) - break; - - // If we have not reached the last profile, try to merge |profile_to_merge| - // with all the less relevant |existing_profiles|. - for (size_t j = i + 1; j < existing_profiles->size(); ++j) { - AutofillProfile* existing_profile = (*existing_profiles)[j].get(); - - // Don't try to merge a profile that was already set for deletion. - if (profiles_to_delete->count(existing_profile->guid())) - continue; - - // Move on if the profiles are not mergeable. - if (!comparator.AreMergeable(*existing_profile, *profile_to_merge)) - continue; - - // The profiles are found to be mergeable. Attempt to update the existing - // profile. This returns true if the merge was successful, or if the - // merge would have been successful but the existing profile IsVerified() - // and will not accept updates from profile_to_merge. - if (existing_profile->SaveAdditionalInfo(*profile_to_merge, - app_locale_)) { - // Keep track that a credit card using |profile_to_merge|'s GUID as its - // billing address id should replace it by |existing_profile|'s GUID. - guids_merge_map->emplace(profile_to_merge->guid(), - existing_profile->guid()); - - // Since |profile_to_merge| was a duplicate of |existing_profile| - // and was merged successfully, it can now be deleted. - profiles_to_delete->insert(profile_to_merge->guid()); - - // Now try to merge the new resulting profile with the rest of the - // existing profiles. - profile_to_merge = existing_profile; - - // Verified profiles do not get merged. Save some time by not - // trying. - if (profile_to_merge->IsVerified()) - break; - } - } - } - AutofillMetrics::LogNumberOfProfilesRemovedDuringDedupe( - profiles_to_delete->size()); -} - -void PersonalDataManager::UpdateCardsBillingAddressReference( - const std::unordered_map<std::string, std::string>& guids_merge_map) { - /* Here is an example of what the graph might look like. - - A -> B - \ - -> E - / - C -> D - */ - - for (auto* credit_card : GetCreditCards()) { - // If the credit card is not associated with a billing address, skip it. - if (credit_card->billing_address_id().empty()) - break; - - // If the billing address profile associated with the card has been merged, - // replace it by the id of the profile in which it was merged. Repeat the - // process until the billing address has not been merged into another one. - std::unordered_map<std::string, std::string>::size_type nb_guid_changes = 0; - bool was_modified = false; - auto it = guids_merge_map.find(credit_card->billing_address_id()); - while (it != guids_merge_map.end()) { - was_modified = true; - credit_card->set_billing_address_id(it->second); - it = guids_merge_map.find(credit_card->billing_address_id()); - - // Out of abundance of caution. - if (nb_guid_changes > guids_merge_map.size()) { - NOTREACHED(); - // Cancel the changes for that card. - was_modified = false; - break; - } - } - - // If the card was modified, apply the changes to the database. - if (was_modified) { - if (credit_card->record_type() == CreditCard::LOCAL_CARD) - database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card); - else - database_helper_->GetServerDatabase()->UpdateServerCardMetadata( - *credit_card); - } - } - Refresh(); -} - void PersonalDataManager::ConvertWalletAddressesAndUpdateWalletCards() { // If the full Sync feature isn't enabled, then do NOT convert any Wallet // addresses to local ones. @@ -2381,109 +2155,6 @@ void PersonalDataManager::ConvertWalletAddressesAndUpdateWalletCards() { app_locale_, GetAccountInfoForPaymentsServer().email); } -bool PersonalDataManager::DeleteDisusedCreditCards() { - // Only delete local cards, as server cards are managed by Payments. - auto cards = GetLocalCreditCards(); - - // Early exit when there is no local cards. - if (cards.empty()) { - return true; - } - - std::vector<std::string> guid_to_delete; - for (CreditCard* card : cards) { - if (card->IsDeletable()) { - guid_to_delete.push_back(card->guid()); - } - } - - size_t num_deleted_cards = guid_to_delete.size(); - - for (auto const& guid : guid_to_delete) { - database_helper_->GetLocalDatabase()->RemoveCreditCard(guid); - } - - if (num_deleted_cards > 0) { - Refresh(); - } - - AutofillMetrics::LogNumberOfCreditCardsDeletedForDisuse(num_deleted_cards); - - return true; -} - -bool PersonalDataManager::DeleteDisusedAddresses() { - const std::vector<AutofillProfile*>& profiles = GetProfiles(); - - // Early exit when there are no profiles. - if (profiles.empty()) { - DVLOG(1) << "There are no profiles"; - return true; - } - - std::unordered_set<std::string> used_billing_address_guids; - for (CreditCard* card : GetCreditCards()) { - if (!card->IsDeletable()) { - used_billing_address_guids.insert(card->billing_address_id()); - } - } - - std::vector<std::string> guids_to_delete; - for (AutofillProfile* profile : profiles) { - if (profile->IsDeletable() && - !used_billing_address_guids.count(profile->guid())) { - guids_to_delete.push_back(profile->guid()); - } - } - - size_t num_deleted_addresses = guids_to_delete.size(); - - for (auto const& guid : guids_to_delete) { - RemoveAutofillProfileByGUIDAndBlankCreditCardReference(guid); - } - - if (num_deleted_addresses > 0) { - Refresh(); - } - - AutofillMetrics::LogNumberOfAddressesDeletedForDisuse(num_deleted_addresses); - - return true; -} - -void PersonalDataManager::ApplyAddressFixesAndCleanups() { - // Validate profiles once per major. - UpdateClientValidityStates(GetProfiles()); - - // One-time fix, otherwise NOP. - RemoveOrphanAutofillTableRows(); - - // Once per major version, otherwise NOP. - ApplyDedupingRoutine(); - - DeleteDisusedAddresses(); - - // If feature AutofillCreateDataForTest is enabled, and once per user profile - // startup. - test_data_creator_.MaybeAddTestProfiles(base::BindRepeating( - &PersonalDataManager::AddProfile, base::Unretained(this))); - - // Ran everytime it is called. - ClearProfileNonSettingsOrigins(); -} - -void PersonalDataManager::ApplyCardFixesAndCleanups() { - DeleteDisusedCreditCards(); - - // If feature AutofillCreateDataForTest is enabled, and once per user profile - // startup. - test_data_creator_.MaybeAddTestCreditCards(base::BindRepeating( - &PersonalDataManager::AddCreditCard, base::Unretained(this))); - - // Ran everytime it is called. - ClearCreditCardNonSettingsOrigins(); -} - void PersonalDataManager::ResetProfileValidity() { synced_profile_validity_.reset(); profiles_server_validities_need_update_ = true; @@ -2697,11 +2368,6 @@ void PersonalDataManager::MigrateUserOptedInWalletSyncTransportIfNeeded() { base::string16 PersonalDataManager::GetDisplayNicknameForCreditCard( const CreditCard& card) const { - if (!base::FeatureList::IsEnabled( - features::kAutofillEnableCardNicknameManagement)) { - return base::string16(); - } - // Always prefer a local nickname if available. if (card.HasNonEmptyValidNickname() && card.record_type() == CreditCard::LOCAL_CARD) @@ -2719,4 +2385,14 @@ base::string16 PersonalDataManager::GetDisplayNicknameForCreditCard( return card.nickname(); } +bool PersonalDataManager::IsSyncEnabledFor(syncer::ModelType model_type) { + return sync_service_ != nullptr && sync_service_->CanSyncFeatureStart() && + sync_service_->GetPreferredDataTypes().Has(model_type); +} + +scoped_refptr<AutofillWebDataService> PersonalDataManager::GetLocalDatabase() { + DCHECK(database_helper_); + return database_helper_->GetLocalDatabase(); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h index eea8c516658..0ccbd642d61 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.h +++ b/chromium/components/autofill/core/browser/personal_data_manager.h @@ -17,6 +17,7 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/observer_list.h" +#include "base/scoped_observation.h" #include "base/strings/string16.h" #include "build/build_config.h" #include "components/autofill/core/browser/autofill_profile_validator.h" @@ -24,16 +25,18 @@ #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/data_model/credit_card_cloud_token_data.h" -#include "components/autofill/core/browser/data_model/test_data_creator.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map_updater.h" #include "components/autofill/core/browser/payments/account_info_getter.h" #include "components/autofill/core/browser/payments/payments_customer_data.h" +#include "components/autofill/core/browser/personal_data_manager_cleaner.h" #include "components/autofill/core/browser/proto/server.pb.h" #include "components/autofill/core/browser/sync_utils.h" #include "components/autofill/core/browser/ui/suggestion.h" #include "components/autofill/core/browser/webdata/autofill_change.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h" +#include "components/history/core/browser/history_service.h" #include "components/history/core/browser/history_service_observer.h" #include "components/keyed_service/core/keyed_service.h" #include "components/prefs/pref_change_registrar.h" @@ -43,7 +46,7 @@ #include "components/sync/driver/sync_service_observer.h" #include "components/webdata/common/web_data_service_consumer.h" -class Browser; +class Profile; class PrefService; class RemoveAutofillTester; @@ -93,6 +96,7 @@ class PersonalDataManager : public KeyedService, void Init(scoped_refptr<AutofillWebDataService> profile_database, scoped_refptr<AutofillWebDataService> account_database, PrefService* pref_service, + PrefService* local_state, signin::IdentityManager* identity_manager, AutofillProfileValidator* client_profile_validator, history::HistoryService* history_service, @@ -143,9 +147,11 @@ class PersonalDataManager : public KeyedService, // imported from a form. void MarkObserversInsufficientFormDataForImport(); - // Called to indicate |data_model| was used (to fill in a form). Updates - // the database accordingly. Can invalidate |data_model|. - virtual void RecordUseOf(const AutofillDataModel& data_model); + // Called to indicate |profile_or_credit_card| was used (to fill in a form). + // Updates the database accordingly. + virtual void RecordUseOf( + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card); // Saves |imported_profile| to the WebDB if it exists. Returns the guid of // the new or updated profile, or the empty string if no profile was saved. @@ -201,9 +207,10 @@ class PersonalDataManager : public KeyedService, // status can be changed. Looks up the card by server ID. virtual void UpdateServerCreditCard(const CreditCard& credit_card); - // Updates the use stats and billing address id for the server |credit_card|. - // Looks up the card by server_id. - virtual void UpdateServerCardMetadata(const CreditCard& credit_card); + // Updates the use stats and billing address id for the server |credit_cards|. + // Looks up the cards by server_id. + virtual void UpdateServerCardsMetadata( + const std::vector<CreditCard>& credit_cards); // Resets the card for |guid| to the masked state. void ResetFullServerCard(const std::string& guid); @@ -228,6 +235,10 @@ class PersonalDataManager : public KeyedService, // owned by this class and must outlive |this|. void SetSyncServiceForTest(syncer::SyncService* sync_service); + // Adds the offer data to local cache for tests. This does not affect data in + // the real database. + void AddOfferDataForTest(std::unique_ptr<AutofillOfferData> offer_data); + // Returns the credit card with the specified |guid|, or nullptr if there is // no credit card with the specified |guid|. virtual CreditCard* GetCreditCardByGUID(const std::string& guid); @@ -236,7 +247,11 @@ class PersonalDataManager : public KeyedService, // no credit card with the specified |number|. virtual CreditCard* GetCreditCardByNumber(const std::string& number); - // Gets the field types availabe in the stored address and credit card data. + // Returns the credit card with the specified |instrument_id|, or nullptr if + // there is no credit card with the specified |instrument_id|. + CreditCard* GetCreditCardByInstrumentId(int64_t instrument_id); + + // Gets the field types available in the stored address and credit card data. void GetNonEmptyTypes(ServerFieldTypeSet* non_empty_types) const; // Returns whether the personal data has been loaded from the web database. @@ -293,10 +308,6 @@ class PersonalDataManager : public KeyedService, bool field_is_autofilled, const std::vector<ServerFieldType>& field_types); - // Tries to delete disused addresses once per major version if the - // feature is enabled. - bool DeleteDisusedAddresses(); - // Returns the credit cards to suggest to the user. Those have been deduped // and ordered by frecency with the expired cards put at the end of the // vector. If |include_server_cards| is false, server side cards should not @@ -321,10 +332,6 @@ class PersonalDataManager : public KeyedService, const base::string16& field_contents, bool include_server_cards); - // Tries to delete disused credit cards once per major version if the - // feature is enabled. - bool DeleteDisusedCreditCards(); - // Re-loads profiles and credit cards from the WebDatabase asynchronously. // In the general case, this is a no-op and will re-create the same // in-memory model as existed prior to the call. If any change occurred to @@ -349,6 +356,14 @@ class PersonalDataManager : public KeyedService, void set_variations_country_code_for_testing(std::string country_code) { variations_country_code_ = country_code; } + + // Returns the raw pointer to PersonalDataManagerCleaner used for testing + // purposes. + PersonalDataManagerCleaner* personal_data_manager_cleaner_for_testing() + const { + DCHECK(personal_data_manager_cleaner_); + return personal_data_manager_cleaner_.get(); + } #endif // Returns our best guess for the country a user is likely to use when @@ -421,6 +436,9 @@ class PersonalDataManager : public KeyedService, client_profile_validator_ = validator; } + // Returns true if the PDM is in the off-the-record mode. + bool IsOffTheRecord() { return is_off_the_record_; } + protected: // Only PersonalDataManagerFactory and certain tests can create instances of // PersonalDataManager. @@ -491,15 +509,12 @@ class PersonalDataManager : public KeyedService, PersonalDataManagerTest, GetCreditCardSuggestions_NoCreditCardsAddedIfDisabled); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ClearProfileNonSettingsOrigins); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ClearCreditCardNonSettingsOrigins); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, RequestProfileServerValidity); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, GetProfileSuggestions_Validity); friend class autofill::AutofillInteractiveTest; + friend class autofill::PersonalDataManagerCleaner; friend class autofill::PersonalDataManagerFactory; friend class AutofillMetricsTest; friend class FormDataImporterTest; @@ -517,7 +532,7 @@ class PersonalDataManager : public KeyedService, friend void autofill_helper::SetCreditCards( int, std::vector<autofill::CreditCard>*); - friend void SetTestProfiles(Browser* browser, + friend void SetTestProfiles(Profile* base_profile, std::vector<AutofillProfile>* profiles); // Sets |web_profiles_| to the contents of |profiles| and updates the web @@ -578,11 +593,6 @@ class PersonalDataManager : public KeyedService, // this class and must outlive |this|. void SetPrefService(PrefService* pref_service); - // Clears the value of the origin field of the autofill profiles or cards that - // were not created from the settings page. - void ClearProfileNonSettingsOrigins(); - void ClearCreditCardNonSettingsOrigins(); - // Called when the |profile| is validated by the AutofillProfileValidator, // updates the profiles on the |ongoing_profile_changes_| and the DB. virtual void OnValidated(const AutofillProfile* profile); @@ -637,6 +647,11 @@ class PersonalDataManager : public KeyedService, // The observers. base::ObserverList<PersonalDataManagerObserver>::Unchecked observers_; + // Used to populate AlternativeStateNameMap with the geographical state data + // (including their abbreviations and localized names). + std::unique_ptr<AlternativeStateNameMapUpdater> + alternative_state_name_map_updater_; + // |profile_valditiies_need_update| whenever the profile validities are out of bool profiles_server_validities_need_update_ = true; @@ -664,36 +679,6 @@ class PersonalDataManager : public KeyedService, const base::string16& field_contents, const std::vector<CreditCard*>& cards_to_suggest) const; - // Runs the routine that removes the orphan rows in the autofill tables if - // it's never been done. - void RemoveOrphanAutofillTableRows(); - - // Applies the deduping routine once per major version if the feature is - // enabled. Calls DedupeProfiles with the content of |web_profiles_| as a - // parameter. Removes the profiles to delete from the database and updates the - // others. Also updates the credit cards' billing address references. Returns - // true if the routine was run. - bool ApplyDedupingRoutine(); - - // Goes through all the |existing_profiles| and merges all similar unverified - // profiles together. Also discards unverified profiles that are similar to a - // verified profile. All the profiles except the results of the merges will be - // added to |profile_guids_to_delete|. This routine should be run once per - // major version. Records all the merges into the |guids_merge_map|. - // - // This method should only be called by ApplyDedupingRoutine. It is split for - // testing purposes. - void DedupeProfiles( - std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, - std::unordered_set<std::string>* profile_guids_to_delete, - std::unordered_map<std::string, std::string>* guids_merge_map) const; - - // Updates the credit cards' billing address reference based on the merges - // that happened during the dedupe, as defined in |guids_merge_map|. Also - // updates the cards entries in the database. - void UpdateCardsBillingAddressReference( - const std::unordered_map<std::string, std::string>& guids_merge_map); - // Converts the Wallet addresses to local autofill profiles. This should be // called after all the syncable data has been processed (local cards and // profiles, Wallet data and metadata). Also updates Wallet cards' billing @@ -728,12 +713,6 @@ class PersonalDataManager : public KeyedService, void RemoveAutofillProfileByGUIDAndBlankCreditCardReference( const std::string& guid); - // Applies various fixes and cleanups on autofill addresses. - void ApplyAddressFixesAndCleanups(); - - // Applies various fixes and cleanups on autofill credit cards. - void ApplyCardFixesAndCleanups(); - // Resets |synced_profile_validity_|. void ResetProfileValidity(); @@ -773,6 +752,12 @@ class PersonalDataManager : public KeyedService, // one copy has a nickname, take that. base::string16 GetDisplayNicknameForCreditCard(const CreditCard& card) const; + // Returns true if the sync is enabled for |model_type|. + bool IsSyncEnabledFor(syncer::ModelType model_type); + + // Returns the database that is used for storing local data. + scoped_refptr<AutofillWebDataService> GetLocalDatabase(); + // Stores the |app_locale| supplied on construction. const std::string app_locale_; @@ -799,6 +784,11 @@ class PersonalDataManager : public KeyedService, // |profile_validities_need_update_| whenever this is changed. std::unique_ptr<UserProfileValidityMap> synced_profile_validity_; + // PersonalDataManagerCleaner is used to apply various address and credit + // card fixes/cleanups one time at browser startup or when the sync starts. + // PersonalDataManagerCleaner is declared as a friend class. + std::unique_ptr<PersonalDataManagerCleaner> personal_data_manager_cleaner_; + // A timely ordered list of ongoing changes for each profile. std::unordered_map<std::string, std::deque<AutofillProfileDeepChange>> ongoing_profile_changes_; @@ -834,17 +824,12 @@ class PersonalDataManager : public KeyedService, // An observer to listen for changes to prefs::kAutofillWalletImportEnabled. std::unique_ptr<BooleanPrefMember> wallet_enabled_pref_; - // True if autofill profile cleanup needs to be performed. - bool is_autofill_profile_cleanup_pending_ = false; - - // Used to create test data. If the AutofillCreateDataForTest feature is - // enabled, this helper creates autofill profiles and credit card data that - // would otherwise be difficult to create manually using the UI. - TestDataCreator test_data_creator_; - // Whether sync should be considered on in a test. bool is_syncing_for_test_ = false; + base::ScopedObservation<history::HistoryService, HistoryServiceObserver> + history_service_observation_{this}; + base::WeakPtrFactory<PersonalDataManager> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(PersonalDataManager); diff --git a/chromium/components/autofill/core/browser/personal_data_manager_cleaner.cc b/chromium/components/autofill/core/browser/personal_data_manager_cleaner.cc new file mode 100644 index 00000000000..b1f46d6979e --- /dev/null +++ b/chromium/components/autofill/core/browser/personal_data_manager_cleaner.cc @@ -0,0 +1,455 @@ +// Copyright 2020 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 "components/autofill/core/browser/personal_data_manager_cleaner.h" + +#include "base/logging.h" +#include "components/autofill/core/browser/autofill_metrics.h" +#include "components/autofill/core/browser/data_model/autofill_profile_comparator.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_constants.h" +#include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_prefs.h" +#include "components/prefs/pref_service.h" + +namespace autofill { + +PersonalDataManagerCleaner::PersonalDataManagerCleaner( + PersonalDataManager* personal_data_manager, + AlternativeStateNameMapUpdater* alternative_state_name_map_updater, + PrefService* pref_service) + : test_data_creator_(kDisusedDataModelDeletionTimeDelta, + personal_data_manager->app_locale()), + personal_data_manager_(personal_data_manager), + pref_service_(pref_service), + alternative_state_name_map_updater_(alternative_state_name_map_updater) { + // Check if profile cleanup has already been performed this major version. + is_autofill_profile_cleanup_pending_ = + pref_service_->GetInteger(prefs::kAutofillLastVersionDeduped) < + CHROME_VERSION_MAJOR; + DVLOG(1) << "Autofill profile cleanup " + << (is_autofill_profile_cleanup_pending_ ? "needs to be" + : "has already been") + << " performed for this version"; +} + +PersonalDataManagerCleaner::~PersonalDataManagerCleaner() = default; + +void PersonalDataManagerCleaner::CleanupDataAndNotifyPersonalDataObservers() { + // The profile de-duplication is run once every major chrome version. If the + // profile de-duplication has not run for the |CHROME_VERSION_MAJOR| yet, + // |AlternativeStateNameMap| needs to be populated first. Otherwise, + // defer the insertion to when the observers are notified. + if (!alternative_state_name_map_updater_ + ->is_alternative_state_name_map_populated() && + base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap) && + is_autofill_profile_cleanup_pending_) { + alternative_state_name_map_updater_->PopulateAlternativeStateNameMap( + base::BindOnce(&PersonalDataManagerCleaner:: + CleanupDataAndNotifyPersonalDataObservers, + weak_ptr_factory_.GetWeakPtr())); + return; + } + + // If sync is enabled for addresses, defer running cleanups until address + // sync has started; otherwise, do it now. + if (!personal_data_manager_->IsSyncEnabledFor(syncer::AUTOFILL_PROFILE)) + ApplyAddressFixesAndCleanups(); + + // If sync is enabled for credit cards, defer running cleanups until card + // sync has started; otherwise, do it now. + if (!personal_data_manager_->IsSyncEnabledFor(syncer::AUTOFILL_WALLET_DATA)) + ApplyCardFixesAndCleanups(); + + // Log address, credit card and offer startup metrics. + personal_data_manager_->LogStoredProfileMetrics(); + personal_data_manager_->LogStoredCreditCardMetrics(); + personal_data_manager_->LogStoredOfferMetrics(); + + personal_data_manager_->NotifyPersonalDataObserver(); +} + +void PersonalDataManagerCleaner::SyncStarted(syncer::ModelType model_type) { + // The profile de-duplication is run once every major chrome version. If the + // profile de-duplication has not run for the |CHROME_VERSION_MAJOR| yet, + // |AlternativeStateNameMap| needs to be populated first. Otherwise, + // defer the insertion to when the observers are notified. + if (!alternative_state_name_map_updater_ + ->is_alternative_state_name_map_populated() && + base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap) && + is_autofill_profile_cleanup_pending_) { + alternative_state_name_map_updater_->PopulateAlternativeStateNameMap( + base::BindOnce(&PersonalDataManagerCleaner::SyncStarted, + weak_ptr_factory_.GetWeakPtr(), model_type)); + return; + } + + // Run deferred autofill address profile startup code. + // See: PersonalDataManager::OnSyncServiceInitialized + if (model_type == syncer::AUTOFILL_PROFILE) + ApplyAddressFixesAndCleanups(); + + // Run deferred credit card startup code. + // See: PersonalDataManager::OnSyncServiceInitialized + if (model_type == syncer::AUTOFILL_WALLET_DATA) + ApplyCardFixesAndCleanups(); +} + +void PersonalDataManagerCleaner::ApplyAddressFixesAndCleanups() { + // Validate profiles once per major. + personal_data_manager_->UpdateClientValidityStates( + personal_data_manager_->GetProfiles()); + + // One-time fix, otherwise NOP. + RemoveOrphanAutofillTableRows(); + + // Once per major version, otherwise NOP. + ApplyDedupingRoutine(); + + // Once per major version, otherwise NOP. + DeleteDisusedAddresses(); + + // If feature AutofillCreateDataForTest is enabled, and once per user profile + // startup. + test_data_creator_.MaybeAddTestProfiles(base::BindRepeating( + &PersonalDataManagerCleaner::AddProfileForTest, base::Unretained(this))); + + // Ran everytime it is called. + ClearProfileNonSettingsOrigins(); +} + +void PersonalDataManagerCleaner::ApplyCardFixesAndCleanups() { + // Once per major version, otherwise NOP. + DeleteDisusedCreditCards(); + + // If feature AutofillCreateDataForTest is enabled, and once per user profile + // startup. + test_data_creator_.MaybeAddTestCreditCards( + base::BindRepeating(&PersonalDataManagerCleaner::AddCreditCardForTest, + base::Unretained(this))); + + // Ran everytime it is called. + ClearCreditCardNonSettingsOrigins(); +} + +void PersonalDataManagerCleaner::RemoveOrphanAutofillTableRows() { + // Don't run if the fix has already been applied. + if (pref_service_->GetBoolean(prefs::kAutofillOrphanRowsRemoved)) + return; + + scoped_refptr<AutofillWebDataService> local_db = + personal_data_manager_->GetLocalDatabase(); + if (!local_db) + return; + + local_db->RemoveOrphanAutofillTableRows(); + + // Set the pref so that this fix is never run again. + pref_service_->SetBoolean(prefs::kAutofillOrphanRowsRemoved, true); +} + +bool PersonalDataManagerCleaner::ApplyDedupingRoutine() { + if (!base::FeatureList::IsEnabled( + features::kAutofillEnableProfileDeduplication)) { + return false; + } + + // Check if de-duplication has already been performed on this major version. + if (!is_autofill_profile_cleanup_pending_) { + DVLOG(1) + << "Autofill profile de-duplication already performed for this version"; + return false; + } + + const std::vector<AutofillProfile*>& profiles = + personal_data_manager_->GetProfiles(); + + // No need to de-duplicate if there are less than two profiles. + if (profiles.size() < 2) { + DVLOG(1) << "Autofill profile de-duplication not needed."; + return false; + } + + DVLOG(1) << "Starting autofill profile de-duplication."; + std::unordered_set<std::string> profiles_to_delete; + profiles_to_delete.reserve(profiles.size()); + + // Create the map used to update credit card's billing addresses after the + // dedupe. + std::unordered_map<std::string, std::string> guids_merge_map; + + // The changes can't happen directly on the profiles, but need to be + // updated in the database at first, and then updated on the profiles. + // Therefore, we need a copy of profiles to keep track of the changes. + std::vector<std::unique_ptr<AutofillProfile>> new_profiles; + for (auto* it : profiles) { + new_profiles.push_back(std::make_unique<AutofillProfile>(*it)); + } + + DedupeProfiles(&new_profiles, &profiles_to_delete, &guids_merge_map); + + // Apply the profile changes to the database. + for (const auto& profile : new_profiles) { + // If the profile was set to be deleted, remove it from the database, + // otherwise update it. + if (profiles_to_delete.count(profile->guid())) { + personal_data_manager_->RemoveProfileFromDB(profile->guid()); + } else { + personal_data_manager_->UpdateProfileInDB(*(profile.get())); + } + } + + UpdateCardsBillingAddressReference(guids_merge_map); + + is_autofill_profile_cleanup_pending_ = false; + // Set the pref to the current major version. + pref_service_->SetInteger(prefs::kAutofillLastVersionDeduped, + CHROME_VERSION_MAJOR); + + return true; +} + +void PersonalDataManagerCleaner::DedupeProfiles( + std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, + std::unordered_set<std::string>* profiles_to_delete, + std::unordered_map<std::string, std::string>* guids_merge_map) const { + AutofillMetrics::LogNumberOfProfilesConsideredForDedupe( + existing_profiles->size()); + + // Sort the profiles by frecency with all the verified profiles at the end. + // That way the most relevant profiles will get merged into the less relevant + // profiles, which keeps the syntax of the most relevant profiles data. + // Verified profiles are put at the end because they do not merge into other + // profiles, so the loop can be stopped when we reach those. However they need + // to be in the vector because an unverified profile trying to merge into a + // similar verified profile will be discarded. + base::Time comparison_time = AutofillClock::Now(); + std::sort(existing_profiles->begin(), existing_profiles->end(), + [comparison_time](const std::unique_ptr<AutofillProfile>& a, + const std::unique_ptr<AutofillProfile>& b) { + if (a->IsVerified() != b->IsVerified()) + return !a->IsVerified(); + return a->HasGreaterFrecencyThan(b.get(), comparison_time); + }); + + AutofillProfileComparator comparator(personal_data_manager_->app_locale()); + + for (size_t i = 0; i < existing_profiles->size(); ++i) { + AutofillProfile* profile_to_merge = (*existing_profiles)[i].get(); + + // If the profile was set to be deleted, skip it. It has already been + // merged into another profile. + if (profiles_to_delete->count(profile_to_merge->guid())) + continue; + + // If we have reached the verified profiles, stop trying to merge. Verified + // profiles do not get merged. + if (profile_to_merge->IsVerified()) + break; + + // If we have not reached the last profile, try to merge |profile_to_merge| + // with all the less relevant |existing_profiles|. + for (size_t j = i + 1; j < existing_profiles->size(); ++j) { + AutofillProfile* existing_profile = (*existing_profiles)[j].get(); + + // Don't try to merge a profile that was already set for deletion. + if (profiles_to_delete->count(existing_profile->guid())) + continue; + + // Move on if the profiles are not mergeable. + if (!comparator.AreMergeable(*existing_profile, *profile_to_merge)) + continue; + + // The profiles are found to be mergeable. Attempt to update the existing + // profile. This returns true if the merge was successful, or if the + // merge would have been successful but the existing profile IsVerified() + // and will not accept updates from profile_to_merge. + if (existing_profile->SaveAdditionalInfo( + *profile_to_merge, personal_data_manager_->app_locale())) { + // Keep track that a credit card using |profile_to_merge|'s GUID as its + // billing address id should replace it by |existing_profile|'s GUID. + guids_merge_map->emplace(profile_to_merge->guid(), + existing_profile->guid()); + + // Since |profile_to_merge| was a duplicate of |existing_profile| + // and was merged successfully, it can now be deleted. + profiles_to_delete->insert(profile_to_merge->guid()); + + // Now try to merge the new resulting profile with the rest of the + // existing profiles. + profile_to_merge = existing_profile; + + // Verified profiles do not get merged. Save some time by not + // trying. + if (profile_to_merge->IsVerified()) + break; + } + } + } + AutofillMetrics::LogNumberOfProfilesRemovedDuringDedupe( + profiles_to_delete->size()); +} + +void PersonalDataManagerCleaner::UpdateCardsBillingAddressReference( + const std::unordered_map<std::string, std::string>& guids_merge_map) { + /* Here is an example of what the graph might look like. + + A -> B + \ + -> E + / + C -> D + */ + + std::vector<CreditCard> server_cards_to_be_updated; + for (auto* credit_card : personal_data_manager_->GetCreditCards()) { + // If the credit card is not associated with a billing address, skip it. + if (credit_card->billing_address_id().empty()) + break; + + // If the billing address profile associated with the card has been merged, + // replace it by the id of the profile in which it was merged. Repeat the + // process until the billing address has not been merged into another one. + std::unordered_map<std::string, std::string>::size_type nb_guid_changes = 0; + bool was_modified = false; + auto it = guids_merge_map.find(credit_card->billing_address_id()); + while (it != guids_merge_map.end()) { + was_modified = true; + credit_card->set_billing_address_id(it->second); + it = guids_merge_map.find(credit_card->billing_address_id()); + + // Out of abundance of caution. + if (nb_guid_changes > guids_merge_map.size()) { + NOTREACHED(); + // Cancel the changes for that card. + was_modified = false; + break; + } + } + + // If the card was modified, apply the changes to the database. + if (was_modified) { + if (credit_card->record_type() == CreditCard::LOCAL_CARD) + personal_data_manager_->GetLocalDatabase()->UpdateCreditCard( + *credit_card); + else + server_cards_to_be_updated.push_back(*credit_card); + } + } + + // In case, there are server cards that need to be updated, + // |PersonalDataManager::Refresh()| is called after they are updated. + if (server_cards_to_be_updated.empty()) + personal_data_manager_->Refresh(); + else + personal_data_manager_->UpdateServerCardsMetadata( + server_cards_to_be_updated); +} + +bool PersonalDataManagerCleaner::DeleteDisusedAddresses() { + const std::vector<AutofillProfile*>& profiles = + personal_data_manager_->GetProfiles(); + + // Early exit when there are no profiles. + if (profiles.empty()) { + DVLOG(1) << "There are no profiles"; + return true; + } + + std::unordered_set<std::string> used_billing_address_guids; + for (CreditCard* card : personal_data_manager_->GetCreditCards()) { + if (!card->IsDeletable()) { + used_billing_address_guids.insert(card->billing_address_id()); + } + } + + std::vector<std::string> guids_to_delete; + for (AutofillProfile* profile : profiles) { + if (profile->IsDeletable() && + !used_billing_address_guids.count(profile->guid())) { + guids_to_delete.push_back(profile->guid()); + } + } + + size_t num_deleted_addresses = guids_to_delete.size(); + + for (auto const& guid : guids_to_delete) { + personal_data_manager_ + ->RemoveAutofillProfileByGUIDAndBlankCreditCardReference(guid); + } + + if (num_deleted_addresses > 0) { + personal_data_manager_->Refresh(); + } + + AutofillMetrics::LogNumberOfAddressesDeletedForDisuse(num_deleted_addresses); + + return true; +} + +void PersonalDataManagerCleaner::ClearProfileNonSettingsOrigins() { + for (AutofillProfile* profile : personal_data_manager_->GetProfiles()) { + if (profile->origin() != kSettingsOrigin && !profile->origin().empty()) { + profile->set_origin(std::string()); + personal_data_manager_->UpdateProfileInDB(*profile, /*enforced=*/true); + } + } +} + +void PersonalDataManagerCleaner::ClearCreditCardNonSettingsOrigins() { + bool has_updated = false; + + for (CreditCard* card : personal_data_manager_->GetLocalCreditCards()) { + if (card->origin() != kSettingsOrigin && !card->origin().empty()) { + card->set_origin(std::string()); + personal_data_manager_->GetLocalDatabase()->UpdateCreditCard(*card); + has_updated = true; + } + } + + // Refresh the local cache and send notifications to observers if a changed + // was made. + if (has_updated) + personal_data_manager_->Refresh(); +} + +bool PersonalDataManagerCleaner::DeleteDisusedCreditCards() { + // Only delete local cards, as server cards are managed by Payments. + auto cards = personal_data_manager_->GetLocalCreditCards(); + + // Early exit when there is no local cards. + if (cards.empty()) + return true; + + std::vector<CreditCard> cards_to_delete; + for (CreditCard* card : cards) { + if (card->IsDeletable()) { + cards_to_delete.push_back(*card); + } + } + + size_t num_deleted_cards = cards_to_delete.size(); + + if (num_deleted_cards > 0) + personal_data_manager_->DeleteLocalCreditCards(cards_to_delete); + + AutofillMetrics::LogNumberOfCreditCardsDeletedForDisuse(num_deleted_cards); + + return true; +} + +void PersonalDataManagerCleaner::AddProfileForTest( + const AutofillProfile& profile) { + personal_data_manager_->AddProfile(profile); +} + +void PersonalDataManagerCleaner::AddCreditCardForTest( + const CreditCard& credit_card) { + personal_data_manager_->AddCreditCard(credit_card); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/personal_data_manager_cleaner.h b/chromium/components/autofill/core/browser/personal_data_manager_cleaner.h new file mode 100644 index 00000000000..8f970f66254 --- /dev/null +++ b/chromium/components/autofill/core/browser/personal_data_manager_cleaner.h @@ -0,0 +1,164 @@ +// Copyright 2020 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_CLEANER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_CLEANER_H_ + +#include <unordered_set> + +#include "components/autofill/core/browser/data_model/autofill_profile.h" +#include "components/autofill/core/browser/data_model/test_data_creator.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map_updater.h" +#include "components/sync/base/model_type.h" + +class PrefService; + +namespace autofill { + +class PersonalDataManager; + +// PersonalDataManagerCleaner is responsible for applying address and credit +// card cleanups once on browser startup provided that the sync is enabled or +// when the sync starts. +class PersonalDataManagerCleaner { + public: + PersonalDataManagerCleaner( + PersonalDataManager* personal_data_manager, + AlternativeStateNameMapUpdater* alternative_state_name_map_updater, + PrefService* pref_service); + ~PersonalDataManagerCleaner(); + PersonalDataManagerCleaner(const PersonalDataManagerCleaner&) = delete; + PersonalDataManagerCleaner& operator=(const PersonalDataManagerCleaner&) = + delete; + + // Applies address and credit card fixes and cleanups if the sync is enabled. + // Also, logs address, credit card and offer startup metrics. + void CleanupDataAndNotifyPersonalDataObservers(); + + // Applies address/credit card fixes and cleanups depending on the + // |model_type|. + void SyncStarted(syncer::ModelType model_type); + +#if defined(UNIT_TEST) + // A wrapper around |ApplyDedupingRoutine()| used for testing purposes. + bool ApplyDedupingRoutineForTesting() { return ApplyDedupingRoutine(); } + + // A wrapper around |DedupeProfiles()| used for testing purposes. + void DedupeProfilesForTesting( + std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, + std::unordered_set<std::string>* profile_guids_to_delete, + std::unordered_map<std::string, std::string>* guids_merge_map) const { + DedupeProfiles(existing_profiles, profile_guids_to_delete, guids_merge_map); + } + + // A wrapper around |UpdateCardsBillingAddressReference()| used for testing + // purposes. + void UpdateCardsBillingAddressReferenceForTesting( + const std::unordered_map<std::string, std::string>& guids_merge_map) { + UpdateCardsBillingAddressReference(guids_merge_map); + } + + // A wrapper around |DeleteDisusedAddresses()| used for testing purposes. + bool DeleteDisusedAddressesForTesting() { return DeleteDisusedAddresses(); } + + // A wrapper around |DeleteDisusedCreditCards()| used for testing purposes. + bool DeleteDisusedCreditCardsForTesting() { + return DeleteDisusedCreditCards(); + } + + // A wrapper around |ClearProfileNonSettingsOrigins()| used for testing + // purposes. + void ClearProfileNonSettingsOriginsForTesting() { + ClearProfileNonSettingsOrigins(); + } + + // A wrapper around |ClearCreditCardNonSettingsOrigins()| used for testing + // purposes. + void ClearCreditCardNonSettingsOriginsForTesting() { + ClearCreditCardNonSettingsOrigins(); + } +#endif // defined(UNIT_TEST) + + private: + // Applies various fixes and cleanups on autofill addresses. + void ApplyAddressFixesAndCleanups(); + + // Applies various fixes and cleanups on autofill credit cards. + void ApplyCardFixesAndCleanups(); + + // Runs the routine that removes the orphan rows in the autofill tables if + // it's never been done. + void RemoveOrphanAutofillTableRows(); + + // Applies the deduping routine once per major version if the feature is + // enabled. Calls DedupeProfiles with the content of + // |PersonalDataManager::GetProfiles()| as a parameter. Removes the profiles + // to delete from the database and updates the others. Also updates the credit + // cards billing address references. Returns true if the routine was run. + bool ApplyDedupingRoutine(); + + // Goes through all the |existing_profiles| and merges all similar unverified + // profiles together. Also discards unverified profiles that are similar to a + // verified profile. All the profiles except the results of the merges will be + // added to |profile_guids_to_delete|. This routine should be run once per + // major version. Records all the merges into the |guids_merge_map|. + // + // This method should only be called by ApplyDedupingRoutine. It is split for + // testing purposes. + void DedupeProfiles( + std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, + std::unordered_set<std::string>* profile_guids_to_delete, + std::unordered_map<std::string, std::string>* guids_merge_map) const; + + // Updates the credit cards billing address references based on the merges + // that happened during the dedupe, as defined in |guids_merge_map|. Also + // updates the cards entries in the database. + void UpdateCardsBillingAddressReference( + const std::unordered_map<std::string, std::string>& guids_merge_map); + + // Tries to delete disused addresses once per major version if the + // feature is enabled. + bool DeleteDisusedAddresses(); + + // Tries to delete disused credit cards once per major version if the + // feature is enabled. + bool DeleteDisusedCreditCards(); + + // Clears the value of the origin field of the autofill profiles or cards that + // were not created from the settings page. + void ClearProfileNonSettingsOrigins(); + void ClearCreditCardNonSettingsOrigins(); + + // Used for adding test data by |test_data_creator_|. + void AddProfileForTest(const AutofillProfile& profile); + void AddCreditCardForTest(const CreditCard& credit_card); + + // True if autofill profile cleanup needs to be performed. + bool is_autofill_profile_cleanup_pending_ = false; + + // Used to create test data. If the AutofillCreateDataForTest feature is + // enabled, this helper creates autofill profiles and credit card data that + // would otherwise be difficult to create manually using the UI. + TestDataCreator test_data_creator_; + + // The personal data manager, used to load and update the personal data + // from/to the web database. + PersonalDataManager* const personal_data_manager_ = nullptr; + + // The PrefService used by this instance. + PrefService* const pref_service_ = nullptr; + + // The AlternativeStateNameMapUpdater, used to populate + // AlternativeStateNameMap with the geographical state data. + AlternativeStateNameMapUpdater* const alternative_state_name_map_updater_ = + nullptr; + + // base::WeakPtr ensures that the callback bound to the object is canceled + // when that object is destroyed. + base::WeakPtrFactory<PersonalDataManagerCleaner> weak_ptr_factory_{this}; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_CLEANER_H_ diff --git a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc index 99b095478df..70d5a739895 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc @@ -31,11 +31,13 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" +#include "build/chromeos_buildflags.h" #include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/autofill_profile_comparator.h" +#include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/personal_data_manager_observer.h" @@ -68,6 +70,11 @@ #include "testing/gtest/include/gtest/gtest.h" namespace autofill { + +using structured_address::HonorificPrefixEnabled; +using structured_address::StructuredAddressesEnabled; +using structured_address::StructuredNamesEnabled; + namespace { const char kPrimaryAccountEmail[] = "syncuser@example.com"; @@ -83,15 +90,6 @@ ACTION_P(QuitMessageLoop, loop) { loop->Quit(); } -bool StructuredNames() { - return base::FeatureList::IsEnabled( - features::kAutofillEnableSupportForMoreStructureInNames); -} - -bool StructuredAddress() { - return base::FeatureList::IsEnabled( - features::kAutofillEnableSupportForMoreStructureInAddresses); -} class PersonalDataLoadedObserverMock : public PersonalDataManagerObserver { public: @@ -240,7 +238,7 @@ class PersonalDataManagerTestBase { features::kAutofillEnableAccountWalletStorage) ? scoped_refptr<AutofillWebDataService>(account_database_service_) : nullptr, - prefs_.get(), identity_test_env_.identity_manager(), + prefs_.get(), prefs_.get(), identity_test_env_.identity_manager(), TestAutofillProfileValidator::GetInstance(), /*history_service=*/nullptr, is_incognito); @@ -369,10 +367,6 @@ class PersonalDataManagerHelper : public PersonalDataManagerTestBase { return PersonalDataManagerTestBase::TurnOnSyncFeature(personal_data_.get()); } - void EnableAutofillProfileCleanup() { - personal_data_->is_autofill_profile_cleanup_pending_ = true; - } - void SetUpReferenceProfile(const AutofillProfile& profile) { ASSERT_EQ(0U, personal_data_->GetProfiles().size()); @@ -449,7 +443,9 @@ class PersonalDataManagerHelper : public PersonalDataManagerTestBase { // Cards are automatically remasked on Linux since full server cards are not // supported. -#if !defined(OS_LINUX) || defined(OS_CHROMEOS) +// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch +// of lacros-chrome is complete. +#if !(defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) personal_data_->ResetFullServerCard( personal_data_->GetCreditCards()[0]->guid()); #endif @@ -580,7 +576,7 @@ class PersonalDataManagerMigrationTest : public PersonalDataManagerHelper, public: PersonalDataManagerMigrationTest() : PersonalDataManagerHelper( -#if defined(OS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS_ASH) { ::switches::kAccountIdMigration } #endif ) { @@ -603,7 +599,6 @@ class PersonalDataManagerMockTest : public PersonalDataManagerTestBase, personal_data_->pref_service_->SetInteger( prefs::kAutofillLastVersionValidated, atoi(version_info::GetVersionNumber().c_str())); - personal_data_->is_autofill_profile_cleanup_pending_ = true; } void TearDown() override { @@ -885,6 +880,7 @@ TEST_F(PersonalDataManagerTest, AddProfile_CrazyCharacters) { base::WideToUTF16(L"861088828000")); profile1.SetInfo(AutofillType(ADDRESS_HOME_COUNTRY), base::WideToUTF16(L"India"), "en-US"); + profile1.FinalizeAfterImport(); profiles.push_back(profile1); AutofillProfile profile2; @@ -894,11 +890,13 @@ TEST_F(PersonalDataManagerTest, AddProfile_CrazyCharacters) { L"\u8def1915\u53f7")); profile2.SetRawInfo(NAME_LAST, base::WideToUTF16(L"aguantó")); profile2.SetRawInfo(ADDRESS_HOME_ZIP, base::WideToUTF16(L"HOME 94043")); + profile2.FinalizeAfterImport(); profiles.push_back(profile2); AutofillProfile profile3; profile3.SetRawInfo(EMAIL_ADDRESS, base::WideToUTF16(L"sue@example.com")); profile3.SetRawInfo(COMPANY_NAME, base::WideToUTF16(L"Company X")); + profile3.FinalizeAfterImport(); profiles.push_back(profile3); AutofillProfile profile4; @@ -917,6 +915,7 @@ TEST_F(PersonalDataManagerTest, AddProfile_CrazyCharacters) { L"\u0905\u092a\u094b\u0932\u094b " L"\u091f\u093e\u092f\u0930\u094d\u0938 " L"\u0906\u0926\u093f")); + profile4.FinalizeAfterImport(); profiles.push_back(profile4); AutofillProfile profile5; @@ -928,6 +927,7 @@ TEST_F(PersonalDataManagerTest, AddProfile_CrazyCharacters) { base::WideToUTF16(L"111111111111110000GOOGLE")); profile5.SetRawInfo(EMAIL_ADDRESS, base::WideToUTF16(L"page@000000.com")); profile5.SetRawInfo(COMPANY_NAME, base::WideToUTF16(L"Google")); + profile5.FinalizeAfterImport(); profiles.push_back(profile5); AutofillProfile profile6; @@ -944,6 +944,7 @@ TEST_F(PersonalDataManagerTest, AddProfile_CrazyCharacters) { L"\u064a \u0639\u0645\u0631 " L"\u0627\u0644\u0628\u0634\u064a\u0631")); profile6.SetRawInfo(ADDRESS_HOME_ZIP, base::WideToUTF16(L"HOME 94043")); + profile6.FinalizeAfterImport(); profiles.push_back(profile6); AutofillProfile profile7; @@ -960,6 +961,7 @@ TEST_F(PersonalDataManagerTest, AddProfile_CrazyCharacters) { base::WideToUTF16(L"15466784565")); profile7.SetInfo(AutofillType(ADDRESS_HOME_COUNTRY), base::WideToUTF16(L"United States"), "en-US"); + profile7.FinalizeAfterImport(); profiles.push_back(profile7); personal_data_->SetProfiles(&profiles); @@ -1403,7 +1405,7 @@ TEST_F(PersonalDataManagerTest, KeepExistingLocalDataOnSignIn) { /*disabled_features=*/{}); // ClearPrimaryAccount is not supported on CrOS. -#if !defined(OS_CHROMEOS) +#if !BUILDFLAG(IS_CHROMEOS_ASH) // Sign out. identity_test_env_.ClearPrimaryAccount(); EXPECT_EQ(AutofillSyncSigninState::kSignedOut, @@ -1739,25 +1741,27 @@ TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) { // For structured names and addresses, there are more non-empty types. // TODO(crbug.com/1103421): Clean once launched. unsigned int non_empty_types_expectation = 15; - if (StructuredNames()) + if (StructuredNamesEnabled()) non_empty_types_expectation += 1; // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddress()) + if (StructuredAddressesEnabled()) non_empty_types_expectation += 2; + if (HonorificPrefixEnabled()) + non_empty_types_expectation += 1; EXPECT_EQ(non_empty_types_expectation, non_empty_types.size()); EXPECT_TRUE(non_empty_types.count(NAME_FIRST)); EXPECT_TRUE(non_empty_types.count(NAME_LAST)); // TODO(crbug.com/1103421): Clean once launched. - if (StructuredNames()) + if (StructuredNamesEnabled()) EXPECT_TRUE(non_empty_types.count(NAME_LAST_SECOND)); EXPECT_TRUE(non_empty_types.count(NAME_FULL)); EXPECT_TRUE(non_empty_types.count(EMAIL_ADDRESS)); EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE1)); EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_ADDRESS)); // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddress()) { + if (StructuredAddressesEnabled()) { EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_NAME)); EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_HOUSE_NUMBER)); } @@ -1791,17 +1795,19 @@ TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) { non_empty_types_expectation = 19; // For structured names, there is one more non-empty type. // TODO(crbug.com/1103421): Clean once launched. - if (StructuredNames()) + if (StructuredNamesEnabled()) + non_empty_types_expectation += 1; + if (HonorificPrefixEnabled()) non_empty_types_expectation += 1; // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddress()) + if (StructuredAddressesEnabled()) non_empty_types_expectation += 2; EXPECT_EQ(non_empty_types_expectation, non_empty_types.size()); EXPECT_TRUE(non_empty_types.count(NAME_FIRST)); EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE)); EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE_INITIAL)); // TODO(crbug.com/1103421): Clean once launched. - if (StructuredNames()) + if (StructuredNamesEnabled()) EXPECT_TRUE(non_empty_types.count(NAME_LAST)); EXPECT_TRUE(non_empty_types.count(NAME_FULL)); EXPECT_TRUE(non_empty_types.count(EMAIL_ADDRESS)); @@ -1810,7 +1816,7 @@ TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) { EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE2)); EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_ADDRESS)); // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddress()) { + if (StructuredAddressesEnabled()) { EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_NAME)); EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_HOUSE_NUMBER)); } @@ -1837,10 +1843,12 @@ TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) { // For structured names, there is one more non-empty type. // TODO(crbug.com/1103421): Clean once launched. non_empty_types_expectation = 29; - if (StructuredNames()) + if (StructuredNamesEnabled()) + non_empty_types_expectation += 1; + if (HonorificPrefixEnabled()) non_empty_types_expectation += 1; // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddress()) + if (StructuredAddressesEnabled()) non_empty_types_expectation += 2; EXPECT_EQ(non_empty_types_expectation, non_empty_types.size()); EXPECT_TRUE(non_empty_types.count(NAME_FIRST)); @@ -1848,7 +1856,7 @@ TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) { EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE_INITIAL)); EXPECT_TRUE(non_empty_types.count(NAME_LAST)); EXPECT_TRUE(non_empty_types.count(NAME_FULL)); - if (StructuredNames()) + if (StructuredNamesEnabled()) EXPECT_TRUE(non_empty_types.count(NAME_LAST)); EXPECT_TRUE(non_empty_types.count(EMAIL_ADDRESS)); EXPECT_TRUE(non_empty_types.count(COMPANY_NAME)); @@ -1856,7 +1864,7 @@ TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) { EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE2)); EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_ADDRESS)); // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddress()) { + if (StructuredAddressesEnabled()) { EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_NAME)); EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_HOUSE_NUMBER)); } @@ -3524,8 +3532,7 @@ TEST_F(PersonalDataManagerTest, server_cards.push_back(credit_card1); server_cards.push_back(credit_card2); SetServerCards(server_cards); - personal_data_->UpdateServerCardMetadata(credit_card1); - personal_data_->UpdateServerCardMetadata(credit_card2); + personal_data_->UpdateServerCardsMetadata({credit_card1, credit_card2}); // Add an expired local card last used 180 days ago. CreditCard credit_card3("1141084B-72D7-4B73-90CF-3D6AC154673B", @@ -3917,18 +3924,21 @@ TEST_F(PersonalDataManagerTest, RecordUseOf) { TestAutofillClock test_clock; test_clock.SetNow(kArbitraryTime); + auto Check = [](const AutofillDataModel& data_model, size_t use_count, + base::Time use_date, base::Time modification_date) { + EXPECT_EQ(use_count, data_model.use_count()); + EXPECT_EQ(use_date, data_model.use_date()); + EXPECT_EQ(modification_date, data_model.modification_date()); + }; + AutofillProfile profile(test::GetFullProfile()); - EXPECT_EQ(1U, profile.use_count()); - EXPECT_EQ(kArbitraryTime, profile.use_date()); - EXPECT_EQ(kArbitraryTime, profile.modification_date()); + Check(profile, 1u, kArbitraryTime, kArbitraryTime); AddProfileToPersonalDataManager(profile); CreditCard credit_card(base::GenerateGUID(), test::kEmptyOrigin); test::SetCreditCardInfo(&credit_card, "John Dillinger", "4234567890123456" /* Visa */, "01", "2999", "1"); - EXPECT_EQ(1U, credit_card.use_count()); - EXPECT_EQ(kArbitraryTime, credit_card.use_date()); - EXPECT_EQ(kArbitraryTime, credit_card.modification_date()); + Check(credit_card, 1u, kArbitraryTime, kArbitraryTime); personal_data_->AddCreditCard(credit_card); // Make sure everything is set up correctly. @@ -3943,40 +3953,43 @@ TEST_F(PersonalDataManagerTest, RecordUseOf) { personal_data_->GetProfileByGUID(profile.guid()); ASSERT_TRUE(added_profile); EXPECT_EQ(*added_profile, profile); - EXPECT_EQ(1U, added_profile->use_count()); - EXPECT_EQ(kArbitraryTime, added_profile->use_date()); - EXPECT_EQ(kArbitraryTime, added_profile->modification_date()); - - base::RunLoop run_loop; - EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) - .WillOnce(QuitMessageLoop(&run_loop)); - EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1); - - personal_data_->RecordUseOf(profile); - - run_loop.Run(); + Check(*added_profile, 1u, kArbitraryTime, kArbitraryTime); CreditCard* added_card = personal_data_->GetCreditCardByGUID(credit_card.guid()); ASSERT_TRUE(added_card); EXPECT_EQ(*added_card, credit_card); - EXPECT_EQ(1U, added_card->use_count()); - EXPECT_EQ(kArbitraryTime, added_card->use_date()); - EXPECT_EQ(kArbitraryTime, added_card->modification_date()); - personal_data_->RecordUseOf(credit_card); + Check(*added_card, 1u, kArbitraryTime, kArbitraryTime); + + // Use |profile|, then verify usage stats. + base::RunLoop profile_run_loop; + EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) + .WillOnce(QuitMessageLoop(&profile_run_loop)); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1); + personal_data_->RecordUseOf(&profile); + profile_run_loop.Run(); - // Verify usage stats are updated. added_profile = personal_data_->GetProfileByGUID(profile.guid()); + added_card = personal_data_->GetCreditCardByGUID(credit_card.guid()); ASSERT_TRUE(added_profile); - EXPECT_EQ(2U, added_profile->use_count()); - EXPECT_EQ(kSomeLaterTime, added_profile->use_date()); - EXPECT_EQ(kArbitraryTime, added_profile->modification_date()); + ASSERT_TRUE(added_card); + Check(*added_profile, 2u, kSomeLaterTime, kArbitraryTime); + Check(*added_card, 1u, kArbitraryTime, kArbitraryTime); + + // Use |credit_card|, then verify usage stats. + base::RunLoop credit_card_run_loop; + EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) + .WillOnce(QuitMessageLoop(&credit_card_run_loop)); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1); + personal_data_->RecordUseOf(&credit_card); + credit_card_run_loop.Run(); + added_profile = personal_data_->GetProfileByGUID(profile.guid()); added_card = personal_data_->GetCreditCardByGUID(credit_card.guid()); + ASSERT_TRUE(added_profile); ASSERT_TRUE(added_card); - EXPECT_EQ(2U, added_card->use_count()); - EXPECT_EQ(kSomeLaterTime, added_card->use_date()); - EXPECT_EQ(kArbitraryTime, added_card->modification_date()); + Check(*added_profile, 2u, kSomeLaterTime, kArbitraryTime); + Check(*added_card, 2u, kSomeLaterTime, kArbitraryTime); } TEST_F(PersonalDataManagerTest, ClearAllServerData) { @@ -4068,8 +4081,6 @@ class SaveImportedProfileTest } } - bool StructuredNames() const { return structured_names_enabled_; } - private: bool structured_names_enabled_; base::test::ScopedFeatureList scoped_features_; @@ -4143,9 +4154,10 @@ TEST_P(SaveImportedProfileTest, SaveImportedProfile) { // date were properly updated. EXPECT_EQ(1U, saved_profiles.front()->use_count()); EXPECT_EQ(kSomeLaterTime, saved_profiles.front()->use_date()); - // For structured names, the modification date is only updated when the + + // For structured addresses, the modification date is only updated when the // profile actually changes. - if (StructuredNames()) { + if (StructuredNamesEnabled() || StructuredAddressesEnabled()) { EXPECT_EQ(*saved_profiles.front() == original_profile ? kArbitraryTime : kSomeLaterTime, saved_profiles.front()->modification_date()); @@ -4591,13 +4603,11 @@ TEST_F(PersonalDataManagerTest, DedupeProfiles_ProfilesToDelete) { existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile4)); existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile5)); - // Enable the profile cleanup. - EnableAutofillProfileCleanup(); - base::HistogramTester histogram_tester; std::unordered_map<std::string, std::string> guids_merge_map; std::unordered_set<std::string> profiles_to_delete; - personal_data_->DedupeProfiles(&existing_profiles, &profiles_to_delete, + personal_data_->personal_data_manager_cleaner_for_testing() + ->DedupeProfilesForTesting(&existing_profiles, &profiles_to_delete, &guids_merge_map); // 5 profiles were considered for dedupe. histogram_tester.ExpectUniqueSample( @@ -4676,13 +4686,11 @@ TEST_F(PersonalDataManagerTest, DedupeProfiles_GuidsMergeMap) { existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile4)); existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile5)); - // Enable the profile cleanup. - EnableAutofillProfileCleanup(); - std::unordered_map<std::string, std::string> guids_merge_map; std::unordered_set<std::string> profiles_to_delete; - personal_data_->DedupeProfiles(&existing_profiles, &profiles_to_delete, + personal_data_->personal_data_manager_cleaner_for_testing() + ->DedupeProfilesForTesting(&existing_profiles, &profiles_to_delete, &guids_merge_map); // The two profile merges should be recorded in the map. @@ -4739,7 +4747,8 @@ TEST_F(PersonalDataManagerTest, UpdateCardsBillingAddressReference) { personal_data_->server_credit_cards_.push_back( std::unique_ptr<CreditCard>(credit_card4)); - personal_data_->UpdateCardsBillingAddressReference(guids_merge_map); + personal_data_->personal_data_manager_cleaner_for_testing() + ->UpdateCardsBillingAddressReferenceForTesting(guids_merge_map); // The first card's billing address should now be E. EXPECT_EQ("E", credit_card1->billing_address_id()); @@ -4755,6 +4764,9 @@ TEST_F(PersonalDataManagerTest, UpdateCardsBillingAddressReference) { // based on the deduped profiles. TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_CardsBillingAddressIdUpdated) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + // A set of 6 profiles will be created. They should merge in this way: // 1 -> 2 -> 3 // 4 -> 5 @@ -4856,11 +4868,8 @@ TEST_F(PersonalDataManagerTest, EXPECT_EQ(6U, personal_data_->GetProfiles().size()); EXPECT_EQ(3U, personal_data_->GetCreditCards().size()); - // Enable the profile cleanup now. Otherwise it would be triggered by the - // calls to AddProfile. - EnableAutofillProfileCleanup(); - - EXPECT_TRUE(personal_data_->ApplyDedupingRoutine()); + EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() + ->ApplyDedupingRoutineForTesting()); WaitForOnPersonalDataChanged(); // Get the profiles and cards sorted by frecency to have a deterministic @@ -4895,6 +4904,9 @@ TEST_F(PersonalDataManagerTest, // never lose information and keep the syntax of the profile with the higher // frecency score. TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + // Create a profile with a higher frecency score. AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", @@ -4926,13 +4938,10 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) { // Make sure the 3 profiles were saved; EXPECT_EQ(3U, personal_data_->GetProfiles().size()); - // Enable the profile cleanup now. Otherwise it would be triggered by the - // calls to AddProfile. - EnableAutofillProfileCleanup(); - base::HistogramTester histogram_tester; - EXPECT_TRUE(personal_data_->ApplyDedupingRoutine()); + EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() + ->ApplyDedupingRoutineForTesting()); WaitForOnPersonalDataChanged(); std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); @@ -4983,11 +4992,16 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) { // original data when deduping with similar profiles, even if it has a higher // frecency score. TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + // Create a verified profile with a higher frecency score. AutofillProfile profile1(base::GenerateGUID(), kSettingsOrigin); - test::SetProfileInfo(&profile1, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "", "12345678910"); + test::SetProfileInfo( + &profile1, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "", + "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", + "12345678910", /*finalize=*/true, + /*status=*/structured_address::VerificationStatus::kUserVerified); profile1.set_use_count(7); profile1.set_use_date(kMuchLaterTime); @@ -5014,13 +5028,10 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) { // Make sure the 3 profiles were saved. EXPECT_EQ(3U, personal_data_->GetProfiles().size()); - // Enable the profile cleanup now. Otherwise it would be triggered by the - // calls to AddProfile. - EnableAutofillProfileCleanup(); - base::HistogramTester histogram_tester; - EXPECT_TRUE(personal_data_->ApplyDedupingRoutine()); + EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() + ->ApplyDedupingRoutineForTesting()); WaitForOnPersonalDataChanged(); std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); @@ -5036,6 +5047,13 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) { histogram_tester.ExpectUniqueSample( "Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1); + // Although the profile was verified, the structure of the street address + // still evolved with future observations. In this case, the "." was added + // from a later observation. + profile1.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_NAME, base::UTF8ToUTF16("Evergreen Terrace"), + structured_address::VerificationStatus::kParsed); + // // Only the verified |profile1| with its original data should have been kept. EXPECT_EQ(profile1.guid(), profiles[0]->guid()); EXPECT_TRUE(profile1 == *profiles[0]); @@ -5047,6 +5065,9 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) { // original data when deduping with similar profiles, even if it has a lower // frecency score. TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + // Create a profile to dedupe with a higher frecency score. AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", @@ -5065,9 +5086,11 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) { // Create a similar verified profile with a lower frecency score. AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin); - test::SetProfileInfo(&profile3, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "", "12345678910"); + test::SetProfileInfo( + &profile3, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "", + "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", + "12345678910", /*finalize=*/true, + /*status=*/structured_address::VerificationStatus::kUserVerified); profile3.set_use_count(3); profile3.set_use_date(kArbitraryTime); @@ -5078,13 +5101,10 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) { // Make sure the 3 profiles were saved. EXPECT_EQ(3U, personal_data_->GetProfiles().size()); - // Enable the profile cleanup now. Otherwise it would be triggered by the - // calls to AddProfile. - EnableAutofillProfileCleanup(); - base::HistogramTester histogram_tester; - EXPECT_TRUE(personal_data_->ApplyDedupingRoutine()); + EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() + ->ApplyDedupingRoutineForTesting()); WaitForOnPersonalDataChanged(); std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); @@ -5110,6 +5130,9 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) { // Tests that ApplyDedupingRoutine does not merge unverified data into // a verified profile. Also tests that two verified profiles don't get merged. TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + // Create a profile to dedupe with a higher frecency score. AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", @@ -5120,17 +5143,22 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) { // Create a similar verified profile with a medium frecency score. AutofillProfile profile2(base::GenerateGUID(), kSettingsOrigin); - test::SetProfileInfo(&profile2, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); + test::SetProfileInfo( + &profile2, "Homer", "J", "Simpson", "homer.simpson@abc.com", "Fox", + "742 Evergreen Terrace.", "", "Springfield", "IL", "91601", "", "", + /*finalize=*/true, + /*status=*/structured_address::VerificationStatus::kUserVerified); + profile2.set_use_count(5); profile2.set_use_date(kSomeLaterTime); // Create a similar verified profile with a lower frecency score. AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin); - test::SetProfileInfo(&profile3, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "", "12345678910"); + test::SetProfileInfo( + &profile3, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "", + "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", + "12345678910", /*finalize=*/true, + /*status*/ structured_address::VerificationStatus::kUserVerified); profile3.set_use_count(3); profile3.set_use_date(kArbitraryTime); @@ -5141,19 +5169,23 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) { // Make sure the 3 profiles were saved. EXPECT_EQ(3U, personal_data_->GetProfiles().size()); - // Enable the profile cleanup now. Otherwise it would be triggered by the - // calls to AddProfile. - EnableAutofillProfileCleanup(); - base::HistogramTester histogram_tester; - EXPECT_TRUE(personal_data_->ApplyDedupingRoutine()); + EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() + ->ApplyDedupingRoutineForTesting()); WaitForOnPersonalDataChanged(); // Get the profiles, sorted by frecency to have a deterministic order. std::vector<AutofillProfile*> profiles = personal_data_->GetProfilesToSuggest(); + // Although the profile was verified, the structure of the street address + // still evolved with future observations. In this case, the "." was removed + // from a later observation. + profile2.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_NAME, base::UTF8ToUTF16("Evergreen Terrace"), + structured_address::VerificationStatus::kParsed); + // |profile1| should have been discarded because the saved profile with the // highest frecency score is verified (|profile2|). Therefore, |profile1|'s // data should not have been merged with |profile2|'s data. Then |profile2| @@ -5183,6 +5215,9 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) { // that the resulting profiles have the right values, has no effect on the other // profiles and that the data of verified profiles is not modified. TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + // Create a Homer home profile with a higher frecency score than other Homer // profiles. AutofillProfile Homer1(base::GenerateGUID(), test::kEmptyOrigin); @@ -5256,16 +5291,13 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { // Make sure the 7 profiles were saved; EXPECT_EQ(7U, personal_data_->GetProfiles().size()); - // Enable the profile cleanup now. Otherwise it would be triggered by the - // calls to AddProfile. - EnableAutofillProfileCleanup(); - base::HistogramTester histogram_tester; // |Homer1| should get merged into |Homer2| which should then be merged into // |Homer3|. |Marge2| should be discarded in favor of |Marge1| which is // verified. |Homer4| and |Barney| should not be deduped at all. - EXPECT_TRUE(personal_data_->ApplyDedupingRoutine()); + EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() + ->ApplyDedupingRoutineForTesting()); WaitForOnPersonalDataChanged(); // Get the profiles, sorted by frecency to have a deterministic order. @@ -5320,40 +5352,18 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { EXPECT_TRUE(Barney == *profiles[3]); } -// Tests that ApplyDedupingRoutine is not run if the feature is disabled. -TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_FeatureDisabled) { - // Create a profile to dedupe. - AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - - // Create a similar profile. - AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile2, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); - - AddProfileToPersonalDataManager(profile1); - AddProfileToPersonalDataManager(profile2); - - // Make sure both profiles were saved. - EXPECT_EQ(2U, personal_data_->GetProfiles().size()); - - // The deduping routine should not be run. - EXPECT_FALSE(personal_data_->ApplyDedupingRoutine()); - - // Both profiles should still be present. - EXPECT_EQ(2U, personal_data_->GetProfiles().size()); -} - TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_NopIfZeroProfiles) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); EXPECT_TRUE(personal_data_->GetProfiles().empty()); - EnableAutofillProfileCleanup(); - EXPECT_FALSE(personal_data_->ApplyDedupingRoutine()); + EXPECT_FALSE(personal_data_->personal_data_manager_cleaner_for_testing() + ->ApplyDedupingRoutineForTesting()); } TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_NopIfOneProfile) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + // Create a profile to dedupe. AutofillProfile profile(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&profile, "Homer", "J", "Simpson", @@ -5363,16 +5373,16 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_NopIfOneProfile) { AddProfileToPersonalDataManager(profile); EXPECT_EQ(1U, personal_data_->GetProfiles().size()); - - // Enable the profile cleanup now. Otherwise it would be triggered by the - // calls to AddProfile. - EnableAutofillProfileCleanup(); - EXPECT_FALSE(personal_data_->ApplyDedupingRoutine()); + EXPECT_FALSE(personal_data_->personal_data_manager_cleaner_for_testing() + ->ApplyDedupingRoutineForTesting()); } // Tests that ApplyDedupingRoutine is not run a second time on the same major // version. TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_OncePerVersion) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + // Create a profile to dedupe. AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", @@ -5390,12 +5400,9 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_OncePerVersion) { EXPECT_EQ(2U, personal_data_->GetProfiles().size()); - // Enable the profile cleanup now. Otherwise it would be triggered by the - // calls to AddProfile. - EnableAutofillProfileCleanup(); - // The deduping routine should be run a first time. - EXPECT_TRUE(personal_data_->ApplyDedupingRoutine()); + EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() + ->ApplyDedupingRoutineForTesting()); WaitForOnPersonalDataChanged(); std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); @@ -5414,11 +5421,9 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_OncePerVersion) { // Make sure |profile3| was saved. EXPECT_EQ(2U, personal_data_->GetProfiles().size()); - // Re-enable the profile cleanup now that the profile was added. - EnableAutofillProfileCleanup(); - // The deduping routine should not be run. - EXPECT_FALSE(personal_data_->ApplyDedupingRoutine()); + EXPECT_FALSE(personal_data_->personal_data_manager_cleaner_for_testing() + ->ApplyDedupingRoutineForTesting()); // The two duplicate profiles should still be present. EXPECT_EQ(2U, personal_data_->GetProfiles().size()); @@ -5493,7 +5498,8 @@ TEST_F(PersonalDataManagerTest, EXPECT_EQ(2U, personal_data_->GetCreditCards().size()); // DeleteDisusedAddresses should return true. - EXPECT_TRUE(personal_data_->DeleteDisusedAddresses()); + EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() + ->DeleteDisusedAddressesForTesting()); WaitForOnPersonalDataChanged(); EXPECT_EQ(3U, personal_data_->GetProfiles().size()); @@ -5571,8 +5577,7 @@ TEST_F(PersonalDataManagerTest, server_cards.push_back(credit_card5); server_cards.push_back(credit_card6); SetServerCards(server_cards); - personal_data_->UpdateServerCardMetadata(credit_card5); - personal_data_->UpdateServerCardMetadata(credit_card6); + personal_data_->UpdateServerCardsMetadata({credit_card5, credit_card6}); WaitForOnPersonalDataChanged(); EXPECT_EQ(6U, personal_data_->GetCreditCards().size()); @@ -5581,7 +5586,8 @@ TEST_F(PersonalDataManagerTest, base::HistogramTester histogram_tester; // DeleteDisusedCreditCards should return true to indicate it was run. - EXPECT_TRUE(personal_data_->DeleteDisusedCreditCards()); + EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() + ->DeleteDisusedCreditCardsForTesting()); // Wait for the data to be refreshed. WaitForOnPersonalDataChanged(); @@ -5670,7 +5676,7 @@ TEST_F(PersonalDataManagerTest, // Wallet only provides a full name, so the above first and last names // will be ignored when the profile is written to the DB. - if (!StructuredNames()) { + if (!StructuredNamesEnabled()) { server_profiles.back().SetRawInfo(NAME_FULL, base::ASCIIToUTF16("John Doe")); } @@ -5910,7 +5916,7 @@ TEST_F( // Wallet only provides a full name, so the above first and last names // will be ignored when the profile is written to the DB. // This step happens automatically for structured names. - if (!StructuredNames()) { + if (!StructuredNamesEnabled()) { server_profiles.back().SetRawInfo(NAME_FULL, base::ASCIIToUTF16("John Doe")); } @@ -6621,7 +6627,9 @@ TEST_F(PersonalDataManagerTest, CreateDataForTest) { // These tests are not applicable on Linux since it does not support full server // cards. -#if !defined(OS_LINUX) || defined(OS_CHROMEOS) +// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch +// of lacros-chrome is complete. +#if !(defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) // Test that calling OnSyncServiceInitialized with a null sync service remasks // full server cards. TEST_F(PersonalDataManagerTest, OnSyncServiceInitialized_NoSyncService) { @@ -6670,7 +6678,7 @@ TEST_F(PersonalDataManagerTest, OnSyncServiceInitialized_NotActiveSyncService) { // OnSyncServiceInitialized. personal_data_->OnSyncShutdown(&sync_service); } -#endif // !defined(OS_LINUX) || defined(OS_CHROMEOS) +#endif // !(defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) #if !defined(OS_ANDROID) TEST_F(PersonalDataManagerTest, ExcludeServerSideCards) { @@ -6752,8 +6760,8 @@ TEST_F(PersonalDataManagerTest, ServerCardsShowInTransportMode_NeedOptIn) { EXPECT_EQ(1U, personal_data_->GetLocalCreditCards().size()); EXPECT_EQ(2U, personal_data_->GetServerCreditCards().size()); } -#endif // defined(OS_WIN) || defined(OS_MAC) || - // defined(OS_LINUX) || defined(OS_CHROMEOS) +#endif // defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || + // defined(OS_CHROMEOS) // Tests that all the non settings origins of autofill profiles are cleared but // that the settings origins are untouched. @@ -6800,7 +6808,8 @@ TEST_F(PersonalDataManagerTest, ClearProfileNonSettingsOrigins) { EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) .Times(2); // The setting of profiles 0 and 2 will be cleared. - personal_data_->ClearProfileNonSettingsOrigins(); + personal_data_->personal_data_manager_cleaner_for_testing() + ->ClearProfileNonSettingsOriginsForTesting(); run_loop.Run(); ASSERT_EQ(4U, personal_data_->GetProfiles().size()); @@ -6850,7 +6859,8 @@ TEST_F(PersonalDataManagerTest, ClearCreditCardNonSettingsOrigins) { WaitForOnPersonalDataChanged(); ASSERT_EQ(4U, personal_data_->GetCreditCards().size()); - personal_data_->ClearCreditCardNonSettingsOrigins(); + personal_data_->personal_data_manager_cleaner_for_testing() + ->ClearCreditCardNonSettingsOriginsForTesting(); WaitForOnPersonalDataChanged(); ASSERT_EQ(4U, personal_data_->GetCreditCards().size()); @@ -7005,7 +7015,7 @@ TEST_F(PersonalDataManagerTest, UseCorrectStorageForDifferentCards) { // Set server card metadata. server_card.set_use_count(15); - personal_data_->UpdateServerCardMetadata(server_card); + personal_data_->UpdateServerCardsMetadata({server_card}); WaitForOnPersonalDataChanged(); @@ -7477,7 +7487,7 @@ TEST_F(PersonalDataManagerMigrationTest, } #endif // !defined(OS_ANDROID) && !defined(OS_IOS) -#if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS) +#if !defined(OS_ANDROID) && !defined(OS_IOS) && !BUILDFLAG(IS_CHROMEOS_ASH) TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { // The method should return false if one of these is not respected: // * The sync_service is not null @@ -7590,7 +7600,8 @@ TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { histogram_tester.ExpectTotalCount(kHistogramName, 0); } } -#else // !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS) +#else // !defined(OS_ANDROID) && !defined(OS_IOS) && + // !BUILDFLAG(IS_CHROMEOS_ASH) TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { // The method should return false if one of these is not respected: // * The sync_service is not null @@ -7664,7 +7675,8 @@ TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { personal_data_->SetSyncServiceForTest(nullptr); EXPECT_FALSE(personal_data_->ShouldShowCardsFromAccountOption()); } -#endif // !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS) +#endif // !defined(OS_ANDROID) && !defined(OS_IOS) && + // !BUILDFLAG(IS_CHROMEOS_ASH) TEST_F(PersonalDataManagerTest, GetSyncSigninState) { // Make a non-primary account available with both a refresh token and cookie @@ -7713,7 +7725,7 @@ TEST_F(PersonalDataManagerTest, GetSyncSigninState) { } // ClearPrimaryAccount is not supported on CrOS. -#if !defined(OS_CHROMEOS) +#if !BUILDFLAG(IS_CHROMEOS_ASH) // Check that the sync state is |SignedOut| when the account info is empty. { identity_test_env_.ClearPrimaryAccount(); @@ -7728,7 +7740,7 @@ TEST_F(PersonalDataManagerTest, GetSyncSigninState) { sync_service_.SetAuthenticatedAccountInfo(primary_account_info); sync_service_.SetIsAuthenticatedAccountPrimary(true); // MakePrimaryAccountAvailable is not supported on CrOS. -#if !defined(OS_CHROMEOS) +#if !BUILDFLAG(IS_CHROMEOS_ASH) identity_test_env_.MakePrimaryAccountAvailable(primary_account_info.email); #endif @@ -7771,7 +7783,7 @@ TEST_F(PersonalDataManagerTest, OnUserAcceptedUpstreamOffer) { // Account wallet storage only makes sense together with support for // unconsented primary accounts, i.e. on Win/Mac/Linux. -#if !defined(OS_CHROMEOS) +#if !BUILDFLAG(IS_CHROMEOS_ASH) { base::test::ScopedFeatureList scoped_features; scoped_features.InitAndEnableFeature( @@ -7830,7 +7842,7 @@ TEST_F(PersonalDataManagerTest, OnUserAcceptedUpstreamOffer) { EXPECT_FALSE(prefs::IsUserOptedInWalletSyncTransport( prefs_.get(), active_info.account_id)); } -#endif // !defined(OS_CHROMEOS) +#endif // !BUILDFLAG(IS_CHROMEOS_ASH) /////////////////////////////////////////////////////////// // kSignedInAndSyncFeature @@ -7947,16 +7959,6 @@ class PersonalDataManagerTestForSharingNickname base::string16 local_nickname_; base::string16 server_nickname_; base::string16 expected_nickname_; - - protected: - void SetUp() override { - PersonalDataManagerTest::SetUp(); - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillEnableCardNicknameManagement); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; }; INSTANTIATE_TEST_SUITE_P(, diff --git a/chromium/components/autofill/core/browser/proto/api_v1.proto b/chromium/components/autofill/core/browser/proto/api_v1.proto index 0b26c68c576..f0b2cf17214 100644 --- a/chromium/components/autofill/core/browser/proto/api_v1.proto +++ b/chromium/components/autofill/core/browser/proto/api_v1.proto @@ -65,12 +65,14 @@ message AutofillPageQueryRequest { message AutofillQueryResponse { // Next ID: 2 message FormSuggestion { - // Next ID: 6 + // Next ID: 7 message FieldSuggestion { // Prediction made on a field. message FieldPrediction { // The predicted field type. optional int32 type = 1; + // Indicates if the prediction is an override. + optional bool override = 2 [default = false]; } // Signature identifying the field that is the same as in the request. optional fixed32 field_signature = 1; @@ -85,6 +87,8 @@ message AutofillQueryResponse { // For fields of type NEW_PASSWORD and ACCOUNT_CREATION_PASSWORD, this may // specify requirements for the generation of passwords. optional .autofill.PasswordRequirementsSpec password_requirements = 5; + // Indicates that the primary prediction is an override. + optional bool primary_type_prediction_is_override = 6 [default = false]; }; // Suggestions on the fields in the same form. repeated FieldSuggestion field_suggestions = 1; diff --git a/chromium/components/autofill/core/browser/proto/server.proto b/chromium/components/autofill/core/browser/proto/server.proto index ea2d2a5f4b8..b84518378d1 100644 --- a/chromium/components/autofill/core/browser/proto/server.proto +++ b/chromium/components/autofill/core/browser/proto/server.proto @@ -229,7 +229,7 @@ message AutofillUploadContents { // The secondary form signature is calculated based on field types instead of // names and is used if the primary one is unstable, i.e. the field names - // change on every page load. + // change on every page load. Not used currently. optional fixed64 secondary_form_signature = 34; // True if the autofill feature was used to fill this form, false otherwise. @@ -329,13 +329,14 @@ message AutofillUploadContents { message AutofillTypeValiditiesPair { // Type of the field, e.g. what type of personal data the user entered in // that field before form submission. A list of all data types can be - // found in: + // found in: components/autofill/core/browser/field_types.h required int32 type = 1; // The validity of the type, which is determined based on the validity of // the user's profile data. A list of all validity states can be found - // here: components/autofill/core/browser/field_types.h + // here: components/autofill/core/browser/data_model/autofill_data_model.h // The validity state of a type is used to experiment if only using valid // data would result in better predictions. + // TODO(crbug.com/1174203): Remove the validity. repeated int32 validity = 2; } // A list of possible types for the field with their corresponding validity @@ -350,9 +351,9 @@ message AutofillUploadContents { // Signature of the form action host (e.g. Hash64Bit("example.com")). optional fixed64 action_signature = 13; - // Signature of the form which is used for password generation debugging. - // Currently is used when password generated on a password field of a - // registration form is used on a password field of a login form. + // Signature of the form. This is currently used when password generated on a + // password field of a registration form is used on a password field of a + // login form. optional fixed64 login_form_signature = 14; // Whether a form submission event was observed. diff --git a/chromium/components/autofill/core/browser/test_autofill_client.cc b/chromium/components/autofill/core/browser/test_autofill_client.cc index 952ffdfb555..98beaeb88a0 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.cc +++ b/chromium/components/autofill/core/browser/test_autofill_client.cc @@ -4,11 +4,13 @@ #include "components/autofill/core/browser/test_autofill_client.h" +#include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/payments/local_card_migration_manager.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" +#include "components/version_info/channel.h" #if !defined(OS_IOS) #include "components/autofill/core/browser/payments/test_internal_authenticator.h" @@ -22,6 +24,10 @@ TestAutofillClient::TestAutofillClient() TestAutofillClient::~TestAutofillClient() {} +version_info::Channel TestAutofillClient::GetChannel() const { + return channel_for_testing_; +} + PersonalDataManager* TestAutofillClient::GetPersonalDataManager() { return &test_personal_data_manager_; } @@ -32,6 +38,10 @@ TestAutofillClient::GetAutocompleteHistoryManager() { } PrefService* TestAutofillClient::GetPrefs() { + return const_cast<PrefService*>(base::as_const(*this).GetPrefs()); +} + +const PrefService* TestAutofillClient::GetPrefs() const { return prefs_.get(); } @@ -75,7 +85,7 @@ AutofillOfferManager* TestAutofillClient::GetAutofillOfferManager() { return autofill_offer_manager_.get(); } -const GURL& TestAutofillClient::GetLastCommittedURL() { +const GURL& TestAutofillClient::GetLastCommittedURL() const { return last_committed_url_; } @@ -88,6 +98,10 @@ translate::LanguageState* TestAutofillClient::GetLanguageState() { return &mock_translate_driver_.GetLanguageState(); } +translate::TranslateDriver* TestAutofillClient::GetTranslateDriver() { + return &mock_translate_driver_; +} + #if !defined(OS_IOS) std::unique_ptr<InternalAuthenticator> TestAutofillClient::CreateCreditCardInternalAuthenticator( @@ -206,6 +220,10 @@ void TestAutofillClient::ConfirmCreditCardFillAssist( std::move(callback).Run(); } +void TestAutofillClient::ConfirmSaveAddressProfile( + const AutofillProfile& profile, + AddressProfileSavePromptCallback callback) {} + bool TestAutofillClient::HasCreditCardScanFeature() { return false; } @@ -247,7 +265,7 @@ void TestAutofillClient::DidFillOrPreviewField( const base::string16& autofilled_value, const base::string16& profile_full_name) {} -bool TestAutofillClient::IsContextSecure() { +bool TestAutofillClient::IsContextSecure() const { // Simplified secure context check for tests. return form_origin_.SchemeIs("https"); } @@ -256,7 +274,7 @@ bool TestAutofillClient::ShouldShowSigninPromo() { return false; } -bool TestAutofillClient::AreServerCardsSupported() { +bool TestAutofillClient::AreServerCardsSupported() const { return true; } diff --git a/chromium/components/autofill/core/browser/test_autofill_client.h b/chromium/components/autofill/core/browser/test_autofill_client.h index 64c139007d2..682274a1759 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.h +++ b/chromium/components/autofill/core/browser/test_autofill_client.h @@ -28,6 +28,7 @@ #include "components/translate/core/browser/language_state.h" #include "components/translate/core/browser/mock_translate_driver.h" #include "components/ukm/test_ukm_recorder.h" +#include "components/version_info/channel.h" #include "services/metrics/public/cpp/delegating_ukm_recorder.h" #if !defined(OS_IOS) @@ -43,9 +44,11 @@ class TestAutofillClient : public AutofillClient { ~TestAutofillClient() override; // AutofillClient: + version_info::Channel GetChannel() const override; PersonalDataManager* GetPersonalDataManager() override; AutocompleteHistoryManager* GetAutocompleteHistoryManager() override; PrefService* GetPrefs() override; + const PrefService* GetPrefs() const override; syncer::SyncService* GetSyncService() override; signin::IdentityManager* GetIdentityManager() override; FormDataImporter* GetFormDataImporter() override; @@ -55,9 +58,10 @@ class TestAutofillClient : public AutofillClient { ukm::SourceId GetUkmSourceId() override; AddressNormalizer* GetAddressNormalizer() override; AutofillOfferManager* GetAutofillOfferManager() override; - const GURL& GetLastCommittedURL() override; + const GURL& GetLastCommittedURL() const override; security_state::SecurityLevel GetSecurityLevelForUmaHistograms() override; translate::LanguageState* GetLanguageState() override; + translate::TranslateDriver* GetTranslateDriver() override; #if !defined(OS_IOS) std::unique_ptr<InternalAuthenticator> CreateCreditCardInternalAuthenticator( content::RenderFrameHost* rfh) override; @@ -118,6 +122,9 @@ class TestAutofillClient : public AutofillClient { void CreditCardUploadCompleted(bool card_saved) override; void ConfirmCreditCardFillAssist(const CreditCard& card, base::OnceClosure callback) override; + void ConfirmSaveAddressProfile( + const AutofillProfile& profile, + AddressProfileSavePromptCallback callback) override; bool HasCreditCardScanFeature() override; void ScanCreditCard(CreditCardScanCallback callback) override; void ShowAutofillPopup( @@ -141,9 +148,9 @@ class TestAutofillClient : public AutofillClient { // By default, TestAutofillClient will report that the context is // secure. This can be adjusted by calling set_form_origin() with an // http:// URL. - bool IsContextSecure() override; + bool IsContextSecure() const override; bool ShouldShowSigninPromo() override; - bool AreServerCardsSupported() override; + bool AreServerCardsSupported() const override; void ExecuteCommand(int id) override; // RiskDataLoader: @@ -230,6 +237,10 @@ class TestAutofillClient : public AutofillClient { autofill_offer_manager_ = std::move(autofill_offer_manager); } + void set_channel_for_testing(const version_info::Channel channel) { + channel_for_testing_ = channel; + } + GURL form_origin() { return form_origin_; } ukm::TestUkmRecorder* GetTestUkmRecorder(); @@ -265,6 +276,8 @@ class TestAutofillClient : public AutofillClient { // otherwise. base::Optional<bool> credit_card_name_fix_flow_bubble_was_shown_; + version_info::Channel channel_for_testing_ = version_info::Channel::UNKNOWN; + // Populated if local save or upload was offered. base::Optional<SaveCreditCardOptions> save_credit_card_options_; diff --git a/chromium/components/autofill/core/browser/test_autofill_provider.cc b/chromium/components/autofill/core/browser/test_autofill_provider.cc deleted file mode 100644 index ac9248b8d08..00000000000 --- a/chromium/components/autofill/core/browser/test_autofill_provider.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2017 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 "components/autofill/core/browser/test_autofill_provider.h" - -namespace autofill { - -void TestAutofillProvider::OnQueryFormFieldAutofill( - AutofillHandlerProxy* handler, - int32_t id, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box, - bool autoselect_first_suggestion) {} - -void TestAutofillProvider::OnTextFieldDidChange( - AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box, - const base::TimeTicks timestamp) {} - -void TestAutofillProvider::OnTextFieldDidScroll( - AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) {} - -void TestAutofillProvider::OnSelectControlDidChange( - AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) {} - -void TestAutofillProvider::OnFocusNoLongerOnForm(AutofillHandlerProxy* handler, - bool had_interacted_form) {} - -void TestAutofillProvider::OnFocusOnFormField(AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) {} - -void TestAutofillProvider::OnDidFillAutofillFormData( - AutofillHandlerProxy* handler, - const FormData& form, - base::TimeTicks timestamp) {} - -void TestAutofillProvider::OnFormsSeen(AutofillHandlerProxy* handler, - const std::vector<FormData>& forms, - const base::TimeTicks timestamp) {} - -void TestAutofillProvider::OnHidePopup(AutofillHandlerProxy* handler) {} - -void TestAutofillProvider::Reset(AutofillHandlerProxy* handler) {} - -} // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_autofill_provider.h b/chromium/components/autofill/core/browser/test_autofill_provider.h index db7234195fa..1c498e1bc5b 100644 --- a/chromium/components/autofill/core/browser/test_autofill_provider.h +++ b/chromium/components/autofill/core/browser/test_autofill_provider.h @@ -19,38 +19,40 @@ class TestAutofillProvider : public AutofillProvider { const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, - bool autoselect_first_suggestion) override; + bool autoselect_first_suggestion) override {} void OnTextFieldDidChange(AutofillHandlerProxy* handler, const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, - const base::TimeTicks timestamp) override; + const base::TimeTicks timestamp) override {} void OnTextFieldDidScroll(AutofillHandlerProxy* handler, const FormData& form, const FormFieldData& field, - const gfx::RectF& bounding_box) override; + const gfx::RectF& bounding_box) override {} void OnSelectControlDidChange(AutofillHandlerProxy* handler, const FormData& form, const FormFieldData& field, - const gfx::RectF& bounding_box) override; + const gfx::RectF& bounding_box) override {} void OnFormSubmitted(AutofillHandlerProxy* handler, const FormData& form, bool known_success, mojom::SubmissionSource source) override {} void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler, - bool had_interacted_form) override; + bool had_interacted_form) override {} void OnFocusOnFormField(AutofillHandlerProxy* handler, const FormData& form, const FormFieldData& field, - const gfx::RectF& bounding_box) override; + const gfx::RectF& bounding_box) override {} void OnDidFillAutofillFormData(AutofillHandlerProxy* handler, const FormData& form, - base::TimeTicks timestamp) override; + base::TimeTicks timestamp) override {} void OnFormsSeen(AutofillHandlerProxy* handler, - const std::vector<FormData>& forms, - const base::TimeTicks timestamp) override; - void OnHidePopup(AutofillHandlerProxy* handler) override; - void Reset(AutofillHandlerProxy* handler) override; + const std::vector<FormData>& forms) override {} + void OnHidePopup(AutofillHandlerProxy* handler) override {} + void OnServerPredictionsAvailable(AutofillHandlerProxy* handler) override {} + void OnServerQueryRequestError(AutofillHandlerProxy* handler, + FormSignature form_signature) override {} + void Reset(AutofillHandlerProxy* handler) override {} }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.cc b/chromium/components/autofill/core/browser/test_personal_data_manager.cc index 79b2f682339..0913a4a265d 100644 --- a/chromium/components/autofill/core/browser/test_personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/test_personal_data_manager.cc @@ -23,14 +23,24 @@ AutofillSyncSigninState TestPersonalDataManager::GetSyncSigninState() const { return sync_and_signin_state_; } -void TestPersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) { - CreditCard* credit_card = GetCreditCardWithGUID(data_model.guid().c_str()); - if (credit_card) - credit_card->RecordAndLogUse(); - - AutofillProfile* profile = GetProfileWithGUID(data_model.guid().c_str()); - if (profile) - profile->RecordAndLogUse(); +void TestPersonalDataManager::RecordUseOf( + absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card) { + if (absl::holds_alternative<const CreditCard*>(profile_or_credit_card)) { + CreditCard* credit_card = GetCreditCardByGUID( + absl::get<const CreditCard*>(profile_or_credit_card)->guid()); + + if (credit_card) + credit_card->RecordAndLogUse(); + } + + if (absl::holds_alternative<const AutofillProfile*>(profile_or_credit_card)) { + AutofillProfile* profile = GetProfileByGUID( + absl::get<const AutofillProfile*>(profile_or_credit_card)->guid()); + + if (profile) + profile->RecordAndLogUse(); + } } std::string TestPersonalDataManager::SaveImportedProfile( diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.h b/chromium/components/autofill/core/browser/test_personal_data_manager.h index fe773963e63..6524c6b87d5 100644 --- a/chromium/components/autofill/core/browser/test_personal_data_manager.h +++ b/chromium/components/autofill/core/browser/test_personal_data_manager.h @@ -32,7 +32,8 @@ class TestPersonalDataManager : public PersonalDataManager { // or to make things easier in general to toggle. void OnSyncServiceInitialized(syncer::SyncService* sync_service) override; AutofillSyncSigninState GetSyncSigninState() const override; - void RecordUseOf(const AutofillDataModel& data_model) override; + void RecordUseOf(absl::variant<const AutofillProfile*, const CreditCard*> + profile_or_credit_card) override; std::string SaveImportedProfile( const AutofillProfile& imported_profile) override; std::string SaveImportedCreditCard( diff --git a/chromium/components/autofill/core/browser/ui/accessory_sheet_data.cc b/chromium/components/autofill/core/browser/ui/accessory_sheet_data.cc index 1728083a998..89675db21d3 100644 --- a/chromium/components/autofill/core/browser/ui/accessory_sheet_data.cc +++ b/chromium/components/autofill/core/browser/ui/accessory_sheet_data.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/strings/string_piece.h" +#include "base/trace_event/memory_usage_estimator.h" #include "components/autofill/core/browser/ui/accessory_sheet_enums.h" namespace autofill { @@ -17,7 +18,10 @@ UserInfo::Field::Field(base::string16 display_text, : display_text_(std::move(display_text)), a11y_description_(std::move(a11y_description)), is_obfuscated_(is_obfuscated), - selectable_(selectable) {} + selectable_(selectable), + estimated_memory_use_by_strings_( + base::trace_event::EstimateMemoryUsage(display_text_) + + base::trace_event::EstimateMemoryUsage(a11y_description_)) {} UserInfo::Field::Field(base::string16 display_text, base::string16 a11y_description, @@ -28,7 +32,11 @@ UserInfo::Field::Field(base::string16 display_text, a11y_description_(std::move(a11y_description)), id_(std::move(id)), is_obfuscated_(is_obfuscated), - selectable_(selectable) {} + selectable_(selectable), + estimated_memory_use_by_strings_( + base::trace_event::EstimateMemoryUsage(display_text_) + + base::trace_event::EstimateMemoryUsage(a11y_description_) + + base::trace_event::EstimateMemoryUsage(id_)) {} UserInfo::Field::Field(const Field& field) = default; @@ -47,6 +55,10 @@ bool UserInfo::Field::operator==(const UserInfo::Field& field) const { selectable_ == field.selectable_; } +size_t UserInfo::Field::EstimateMemoryUsage() const { + return sizeof(UserInfo::Field) + estimated_memory_use_by_strings_; +} + std::ostream& operator<<(std::ostream& os, const UserInfo::Field& field) { os << "(display text: \"" << field.display_text() << "\", " << "a11y_description: \"" << field.a11y_description() << "\", " @@ -62,7 +74,10 @@ UserInfo::UserInfo(std::string origin) : UserInfo(std::move(origin), IsPslMatch(false)) {} UserInfo::UserInfo(std::string origin, IsPslMatch is_psl_match) - : origin_(std::move(origin)), is_psl_match_(is_psl_match) {} + : origin_(std::move(origin)), + is_psl_match_(is_psl_match), + estimated_dynamic_memory_use_( + base::trace_event::EstimateMemoryUsage(origin_)) {} UserInfo::UserInfo(const UserInfo& user_info) = default; @@ -79,6 +94,10 @@ bool UserInfo::operator==(const UserInfo& user_info) const { is_psl_match_ == user_info.is_psl_match_; } +size_t UserInfo::EstimateMemoryUsage() const { + return sizeof(UserInfo) + estimated_dynamic_memory_use_; +} + std::ostream& operator<<(std::ostream& os, const UserInfo& user_info) { os << "origin: \"" << user_info.origin() << "\", " << "is_psl_match: " << std::boolalpha << user_info.is_psl_match() << ", " @@ -91,7 +110,10 @@ std::ostream& operator<<(std::ostream& os, const UserInfo& user_info) { FooterCommand::FooterCommand(base::string16 display_text, autofill::AccessoryAction action) - : display_text_(std::move(display_text)), accessory_action_(action) {} + : display_text_(std::move(display_text)), + accessory_action_(action), + estimated_memory_use_by_strings_( + base::trace_event::EstimateMemoryUsage(display_text_)) {} FooterCommand::FooterCommand(const FooterCommand& footer_command) = default; @@ -110,6 +132,10 @@ bool FooterCommand::operator==(const FooterCommand& fc) const { accessory_action_ == fc.accessory_action_; } +size_t FooterCommand::EstimateMemoryUsage() const { + return sizeof(FooterCommand) + estimated_memory_use_by_strings_; +} + std::ostream& operator<<(std::ostream& os, const FooterCommand& fc) { return os << "(display text: \"" << fc.display_text() << "\", " << "action: " << static_cast<int>(fc.accessory_action()) << ")"; @@ -120,7 +146,9 @@ OptionToggle::OptionToggle(base::string16 display_text, autofill::AccessoryAction action) : display_text_(display_text), enabled_(enabled), - accessory_action_(action) {} + accessory_action_(action), + estimated_memory_use_by_strings_( + base::trace_event::EstimateMemoryUsage(display_text_)) {} OptionToggle::OptionToggle(const OptionToggle& option_toggle) = default; @@ -139,6 +167,10 @@ bool OptionToggle::operator==(const OptionToggle& option_toggle) const { accessory_action_ == option_toggle.accessory_action_; } +size_t OptionToggle::EstimateMemoryUsage() const { + return sizeof(OptionToggle) + estimated_memory_use_by_strings_; +} + std::ostream& operator<<(std::ostream& os, const OptionToggle& ot) { return os << "(display text: \"" << ot.display_text() << "\", " << "state: " << ot.is_enabled() << ", " @@ -193,6 +225,17 @@ bool AccessorySheetData::operator==(const AccessorySheetData& data) const { footer_commands_ == data.footer_commands_; } +size_t AccessorySheetData::EstimateMemoryUsage() const { + return sizeof(AccessorySheetData) + + base::trace_event::EstimateMemoryUsage(title_) + + base::trace_event::EstimateMemoryUsage(warning_) + + (option_toggle_ + ? base::trace_event::EstimateMemoryUsage(option_toggle_.value()) + : 0) + + base::trace_event::EstimateIterableMemoryUsage(user_info_list_) + + base::trace_event::EstimateIterableMemoryUsage(footer_commands_); +} + std::ostream& operator<<(std::ostream& os, const AccessorySheetData& data) { os << data.get_sheet_type() << " with title: \"" << data.title(); if (data.option_toggle().has_value()) { diff --git a/chromium/components/autofill/core/browser/ui/accessory_sheet_data.h b/chromium/components/autofill/core/browser/ui/accessory_sheet_data.h index 32bc5e00016..38408e17550 100644 --- a/chromium/components/autofill/core/browser/ui/accessory_sheet_data.h +++ b/chromium/components/autofill/core/browser/ui/accessory_sheet_data.h @@ -10,7 +10,7 @@ #include "base/optional.h" #include "base/strings/string16.h" -#include "base/util/type_safety/strong_alias.h" +#include "base/types/strong_alias.h" #include "components/autofill/core/browser/ui/accessory_sheet_enums.h" namespace password_manager { @@ -56,16 +56,23 @@ class UserInfo { bool operator==(const UserInfo::Field& field) const; + // Estimates dynamic memory usage. + // See base/trace_event/memory_usage_estimator.h for more info. + size_t EstimateMemoryUsage() const; + private: + // IMPORTANT(https://crbug.com/1169167): Add the size of newly added strings + // to the memory estimation member! base::string16 display_text_; base::string16 a11y_description_; std::string id_; // Optional, if needed to complete filling. bool is_obfuscated_; bool selectable_; + size_t estimated_memory_use_by_strings_ = 0; }; using IsPslMatch = - util::StrongAlias<password_manager::IsPublicSuffixMatchTag, bool>; + base::StrongAlias<password_manager::IsPublicSuffixMatchTag, bool>; UserInfo(); explicit UserInfo(std::string origin); @@ -78,7 +85,10 @@ class UserInfo { UserInfo& operator=(const UserInfo& user_info); UserInfo& operator=(UserInfo&& user_info); - void add_field(Field field) { fields_.push_back(std::move(field)); } + void add_field(Field field) { + estimated_dynamic_memory_use_ += field.EstimateMemoryUsage(); + fields_.push_back(std::move(field)); + } const std::vector<Field>& fields() const { return fields_; } const std::string& origin() const { return origin_; } @@ -86,10 +96,17 @@ class UserInfo { bool operator==(const UserInfo& user_info) const; + // Estimates dynamic memory usage. + // See base/trace_event/memory_usage_estimator.h for more info. + size_t EstimateMemoryUsage() const; + private: + // IMPORTANT(https://crbug.com/1169167): Add the size of newly added strings + // to the memory estimation member! std::string origin_; IsPslMatch is_psl_match_{false}; std::vector<Field> fields_; + size_t estimated_dynamic_memory_use_ = 0; }; std::ostream& operator<<(std::ostream& out, const UserInfo::Field& field); @@ -115,9 +132,16 @@ class FooterCommand { bool operator==(const FooterCommand& fc) const; + // Estimates dynamic memory usage. + // See base/trace_event/memory_usage_estimator.h for more info. + size_t EstimateMemoryUsage() const; + private: + // IMPORTANT(https://crbug.com/1169167): Add the size of newly added strings + // to the memory estimation member! base::string16 display_text_; autofill::AccessoryAction accessory_action_; + size_t estimated_memory_use_by_strings_ = 0; }; std::ostream& operator<<(std::ostream& out, const FooterCommand& fc); @@ -147,10 +171,17 @@ class OptionToggle { bool operator==(const OptionToggle& option_toggle) const; + // Estimates dynamic memory usage. + // See base/trace_event/memory_usage_estimator.h for more info. + size_t EstimateMemoryUsage() const; + private: + // IMPORTANT(https://crbug.com/1169167): Add the size of newly added strings + // to the memory estimation member! base::string16 display_text_; bool enabled_; autofill::AccessoryAction accessory_action_; + size_t estimated_memory_use_by_strings_ = 0; }; // Represents the contents of a bottom sheet tab below the keyboard accessory, @@ -204,6 +235,10 @@ class AccessorySheetData { bool operator==(const AccessorySheetData& data) const; + // Estimates dynamic memory usage. + // See base/trace_event/memory_usage_estimator.h for more info. + size_t EstimateMemoryUsage() const; + private: AccessoryTabType sheet_type_; base::string16 title_; diff --git a/chromium/components/autofill/core/browser/ui/address_contact_form_label_formatter.cc b/chromium/components/autofill/core/browser/ui/address_contact_form_label_formatter.cc index 3284654bdd1..61fe081b4a5 100644 --- a/chromium/components/autofill/core/browser/ui/address_contact_form_label_formatter.cc +++ b/chromium/components/autofill/core/browser/ui/address_contact_form_label_formatter.cc @@ -34,14 +34,15 @@ base::string16 AddressContactFormLabelFormatter::GetLabelForProfile( FieldTypeGroup focused_group) const { std::vector<base::string16> label_parts; - bool street_address_is_focused = focused_group == ADDRESS_HOME && - IsStreetAddressPart(focused_field_type()); + bool street_address_is_focused = + focused_group == FieldTypeGroup::kAddressHome && + IsStreetAddressPart(focused_field_type()); bool non_street_address_is_focused = - focused_group == ADDRESS_HOME && + focused_group == FieldTypeGroup::kAddressHome && !IsStreetAddressPart(focused_field_type()); - if (focused_group != NAME && !non_street_address_is_focused && - data_util::ContainsName(groups())) { + if (focused_group != FieldTypeGroup::kName && + !non_street_address_is_focused && data_util::ContainsName(groups())) { AddLabelPartIfNotEmpty( GetLabelName(field_types_for_labels(), profile, app_locale()), &label_parts); @@ -54,11 +55,11 @@ base::string16 AddressContactFormLabelFormatter::GetLabelForProfile( &label_parts); } - if (focused_group != PHONE_HOME && phone_disambiguates_) { + if (focused_group != FieldTypeGroup::kPhoneHome && phone_disambiguates_) { AddLabelPartIfNotEmpty(GetLabelPhone(profile, app_locale()), &label_parts); } - if (focused_group != EMAIL && email_disambiguates_) { + if (focused_group != FieldTypeGroup::kEmail && email_disambiguates_) { AddLabelPartIfNotEmpty(GetLabelEmail(profile, app_locale()), &label_parts); } diff --git a/chromium/components/autofill/core/browser/ui/address_email_form_label_formatter.cc b/chromium/components/autofill/core/browser/ui/address_email_form_label_formatter.cc index 3f41ec73c3a..df9e38e2f7a 100644 --- a/chromium/components/autofill/core/browser/ui/address_email_form_label_formatter.cc +++ b/chromium/components/autofill/core/browser/ui/address_email_form_label_formatter.cc @@ -27,7 +27,7 @@ AddressEmailFormLabelFormatter::~AddressEmailFormLabelFormatter() {} base::string16 AddressEmailFormLabelFormatter::GetLabelForProfile( const AutofillProfile& profile, FieldTypeGroup focused_group) const { - return focused_group == ADDRESS_HOME && + return focused_group == FieldTypeGroup::kAddressHome && !IsStreetAddressPart(focused_field_type()) ? GetLabelForProfileOnFocusedNonStreetAddress( form_has_street_address_, profile, app_locale(), @@ -46,20 +46,21 @@ base::string16 AddressEmailFormLabelFormatter:: FieldTypeGroup focused_group) const { std::vector<base::string16> label_parts; - if (focused_group != NAME && data_util::ContainsName(groups())) { + if (focused_group != FieldTypeGroup::kName && + data_util::ContainsName(groups())) { AddLabelPartIfNotEmpty( GetLabelName(field_types_for_labels(), profile, app_locale()), &label_parts); } - if (focused_group != ADDRESS_HOME) { + if (focused_group != FieldTypeGroup::kAddressHome) { AddLabelPartIfNotEmpty( GetLabelAddress(form_has_street_address_, profile, app_locale(), field_types_for_labels()), &label_parts); } - if (focused_group != EMAIL) { + if (focused_group != FieldTypeGroup::kEmail) { AddLabelPartIfNotEmpty(GetLabelEmail(profile, app_locale()), &label_parts); } diff --git a/chromium/components/autofill/core/browser/ui/address_form_label_formatter.cc b/chromium/components/autofill/core/browser/ui/address_form_label_formatter.cc index 3f9665f3b05..4b958ea3157 100644 --- a/chromium/components/autofill/core/browser/ui/address_form_label_formatter.cc +++ b/chromium/components/autofill/core/browser/ui/address_form_label_formatter.cc @@ -27,7 +27,7 @@ AddressFormLabelFormatter::~AddressFormLabelFormatter() {} base::string16 AddressFormLabelFormatter::GetLabelForProfile( const AutofillProfile& profile, FieldTypeGroup focused_group) const { - if (focused_group != ADDRESS_HOME) { + if (focused_group != FieldTypeGroup::kAddressHome) { return GetLabelNationalAddress(field_types_for_labels(), profile, app_locale()); } else { diff --git a/chromium/components/autofill/core/browser/ui/address_phone_form_label_formatter.cc b/chromium/components/autofill/core/browser/ui/address_phone_form_label_formatter.cc index 543c86c312f..e39cf7ec209 100644 --- a/chromium/components/autofill/core/browser/ui/address_phone_form_label_formatter.cc +++ b/chromium/components/autofill/core/browser/ui/address_phone_form_label_formatter.cc @@ -27,7 +27,7 @@ AddressPhoneFormLabelFormatter::~AddressPhoneFormLabelFormatter() {} base::string16 AddressPhoneFormLabelFormatter::GetLabelForProfile( const AutofillProfile& profile, FieldTypeGroup focused_group) const { - return focused_group == ADDRESS_HOME && + return focused_group == FieldTypeGroup::kAddressHome && !IsStreetAddressPart(focused_field_type()) ? GetLabelForProfileOnFocusedNonStreetAddress( form_has_street_address_, profile, app_locale(), @@ -45,17 +45,18 @@ base::string16 AddressPhoneFormLabelFormatter:: const AutofillProfile& profile, FieldTypeGroup focused_group) const { std::vector<base::string16> label_parts; - if (focused_group != NAME && data_util::ContainsName(groups())) { + if (focused_group != FieldTypeGroup::kName && + data_util::ContainsName(groups())) { AddLabelPartIfNotEmpty( GetLabelName(field_types_for_labels(), profile, app_locale()), &label_parts); } - if (focused_group != PHONE_HOME) { + if (focused_group != FieldTypeGroup::kPhoneHome) { AddLabelPartIfNotEmpty(GetLabelPhone(profile, app_locale()), &label_parts); } - if (focused_group != ADDRESS_HOME) { + if (focused_group != FieldTypeGroup::kAddressHome) { AddLabelPartIfNotEmpty( GetLabelAddress(form_has_street_address_, profile, app_locale(), field_types_for_labels()), diff --git a/chromium/components/autofill/core/browser/ui/contact_form_label_formatter.cc b/chromium/components/autofill/core/browser/ui/contact_form_label_formatter.cc index 163f8d0d1c8..90fbf0c7033 100644 --- a/chromium/components/autofill/core/browser/ui/contact_form_label_formatter.cc +++ b/chromium/components/autofill/core/browser/ui/contact_form_label_formatter.cc @@ -30,17 +30,18 @@ base::string16 ContactFormLabelFormatter::GetLabelForProfile( const AutofillProfile& profile, FieldTypeGroup focused_group) const { std::vector<base::string16> label_parts; - if (focused_group != NAME && data_util::ContainsName(groups())) { + if (focused_group != FieldTypeGroup::kName && + data_util::ContainsName(groups())) { AddLabelPartIfNotEmpty( GetLabelName(field_types_for_labels(), profile, app_locale()), &label_parts); } - if (focused_group != PHONE_HOME) { + if (focused_group != FieldTypeGroup::kPhoneHome) { AddLabelPartIfNotEmpty(MaybeGetPhone(profile), &label_parts); } - if (focused_group != EMAIL) { + if (focused_group != FieldTypeGroup::kEmail) { AddLabelPartIfNotEmpty(MaybeGetEmail(profile), &label_parts); } diff --git a/chromium/components/autofill/core/browser/ui/label_formatter.cc b/chromium/components/autofill/core/browser/ui/label_formatter.cc index 5f87d687d7b..3044f0f6685 100644 --- a/chromium/components/autofill/core/browser/ui/label_formatter.cc +++ b/chromium/components/autofill/core/browser/ui/label_formatter.cc @@ -18,6 +18,7 @@ #include "components/autofill/core/browser/ui/contact_form_label_formatter.h" #include "components/autofill/core/browser/ui/label_formatter_utils.h" #include "components/autofill/core/browser/ui/mobile_label_formatter.h" +#include "components/autofill/core/common/dense_set.h" namespace autofill { @@ -40,14 +41,15 @@ LabelFormatter::LabelFormatter(const std::vector<AutofillProfile*>& profiles, focused_field_type_(focused_field_type), groups_(groups) { const FieldTypeGroup focused_group = GetFocusedNonBillingGroup(); - std::set<FieldTypeGroup> groups_for_labels{NAME, ADDRESS_HOME, EMAIL, - PHONE_HOME}; + DenseSet<FieldTypeGroup> groups_for_labels{ + FieldTypeGroup::kName, FieldTypeGroup::kAddressHome, + FieldTypeGroup::kEmail, FieldTypeGroup::kPhoneHome}; // If a user is focused on an address field, then parts of the address may be // shown in the label. For example, if the user is focusing on a street // address field, then it may be helpful to show the city in the label. // Otherwise, the focused field should not appear in the label. - if (focused_group != ADDRESS_HOME) { + if (focused_group != FieldTypeGroup::kAddressHome) { groups_for_labels.erase(focused_group); } diff --git a/chromium/components/autofill/core/browser/ui/label_formatter.h b/chromium/components/autofill/core/browser/ui/label_formatter.h index 4ca3c891aa8..ab19f4fc103 100644 --- a/chromium/components/autofill/core/browser/ui/label_formatter.h +++ b/chromium/components/autofill/core/browser/ui/label_formatter.h @@ -53,7 +53,7 @@ class LabelFormatter { // Returns the FieldTypeGroup with which |focused_field_type_| is associated. // Billing field types are mapped to their corresponding home address field // types. For example, if focused_field_type_ is ADDRESS_BILLING_ZIP, then - // the resulting FieldTypeGroup is ADDRESS_HOME instead of ADDRESS_BILLING. + // the resulting FieldTypeGroup is kAddressHome instead of kAddressBilling. FieldTypeGroup GetFocusedNonBillingGroup() const; const std::string& app_locale() const { return app_locale_; } diff --git a/chromium/components/autofill/core/browser/ui/label_formatter_utils.cc b/chromium/components/autofill/core/browser/ui/label_formatter_utils.cc index 91f1a6ebf7a..36fb2647450 100644 --- a/chromium/components/autofill/core/browser/ui/label_formatter_utils.cc +++ b/chromium/components/autofill/core/browser/ui/label_formatter_utils.cc @@ -135,7 +135,7 @@ std::vector<ServerFieldType> ExtractSpecifiedAddressFieldTypes( auto should_be_extracted = [&extract_street_address_types](ServerFieldType type) -> bool { return AutofillType(AutofillType(type).GetStorableType()).group() == - ADDRESS_HOME && + FieldTypeGroup::kAddressHome && (extract_street_address_types ? IsStreetAddressPart(type) : !IsStreetAddressPart(type)); }; @@ -148,21 +148,6 @@ std::vector<ServerFieldType> ExtractSpecifiedAddressFieldTypes( return extracted_address_types; } -std::vector<ServerFieldType> ExtractAddressFieldTypes( - const std::vector<ServerFieldType>& types) { - std::vector<ServerFieldType> only_address_types; - - // Note that GetStorableType maps billing fields to their corresponding non- - // billing fields, e.g. ADDRESS_HOME_ZIP is mapped to ADDRESS_BILLING_ZIP. - std::copy_if( - types.begin(), types.end(), std::back_inserter(only_address_types), - [](ServerFieldType type) { - return AutofillType(AutofillType(type).GetStorableType()).group() == - ADDRESS_HOME; - }); - return only_address_types; -} - std::vector<ServerFieldType> TypesWithoutFocusedField( const std::vector<ServerFieldType>& types, ServerFieldType field_type_to_remove) { @@ -301,7 +286,8 @@ base::string16 GetLabelName(const std::vector<ServerFieldType>& types, // The form contains neither a full name field nor a first name field, // so choose some name field in the form and make it the label text. for (const ServerFieldType type : types) { - if (AutofillType(AutofillType(type).GetStorableType()).group() == NAME) { + if (AutofillType(AutofillType(type).GetStorableType()).group() == + FieldTypeGroup::kName) { return profile.GetInfo(AutofillType(type), app_locale); } } @@ -390,31 +376,34 @@ bool HaveSameStreetAddresses(const std::vector<AutofillProfile*>& profiles, bool HasUnfocusedEmailField(FieldTypeGroup focused_group, uint32_t form_groups) { - return ContainsEmail(form_groups) && focused_group != EMAIL; + return ContainsEmail(form_groups) && focused_group != FieldTypeGroup::kEmail; } bool HasUnfocusedNameField(FieldTypeGroup focused_group, uint32_t form_groups) { - return ContainsName(form_groups) && focused_group != NAME; + return ContainsName(form_groups) && focused_group != FieldTypeGroup::kName; } bool HasUnfocusedNonStreetAddressField( ServerFieldType focused_field, FieldTypeGroup focused_group, const std::vector<ServerFieldType>& types) { - return HasNonStreetAddress(types) && (focused_group != ADDRESS_HOME || - !IsNonStreetAddressPart(focused_field)); + return HasNonStreetAddress(types) && + (focused_group != FieldTypeGroup::kAddressHome || + !IsNonStreetAddressPart(focused_field)); } bool HasUnfocusedPhoneField(FieldTypeGroup focused_group, uint32_t form_groups) { - return ContainsPhone(form_groups) && focused_group != PHONE_HOME; + return ContainsPhone(form_groups) && + focused_group != FieldTypeGroup::kPhoneHome; } bool HasUnfocusedStreetAddressField(ServerFieldType focused_field, FieldTypeGroup focused_group, const std::vector<ServerFieldType>& types) { return HasStreetAddress(types) && - (focused_group != ADDRESS_HOME || !IsStreetAddressPart(focused_field)); + (focused_group != FieldTypeGroup::kAddressHome || + !IsStreetAddressPart(focused_field)); } bool FormHasOnlyNonStreetAddressFields( diff --git a/chromium/components/autofill/core/browser/ui/label_formatter_utils.h b/chromium/components/autofill/core/browser/ui/label_formatter_utils.h index 1528e4c48ac..14b33ac0139 100644 --- a/chromium/components/autofill/core/browser/ui/label_formatter_utils.h +++ b/chromium/components/autofill/core/browser/ui/label_formatter_utils.h @@ -52,11 +52,6 @@ std::vector<ServerFieldType> ExtractSpecifiedAddressFieldTypes( bool extract_street_address_types, const std::vector<ServerFieldType>& types); -// Returns a collection of the types in |types| that belong to the -// ADDRESS_HOME or ADDRESS_BILLING FieldTypeGroups. -std::vector<ServerFieldType> ExtractAddressFieldTypes( - const std::vector<ServerFieldType>& types); - // Returns a collection of the types in |types| without |field_type_to_remove|. std::vector<ServerFieldType> TypesWithoutFocusedField( const std::vector<ServerFieldType>& types, diff --git a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc index 5c0830950ce..5d43b44cf42 100644 --- a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc +++ b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc @@ -182,6 +182,12 @@ base::string16 CardUnmaskPromptControllerImpl::GetInstructionsMessage() const { return l10n_util::GetStringFUTF16( ids, card_.CardIdentifierStringForAutofillDisplay()); #else + // For Google Pay Plex cards, show a specific message that include + // instructions to find the CVC for their Plex card. + if (card_.IsGoogleIssuedCard()) { + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_GOOGLE_ISSUED_CARD); + } return l10n_util::GetStringUTF16( card_.record_type() == autofill::CreditCard::LOCAL_CARD ? IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_LOCAL_CARD diff --git a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc index cf32d1ecbe4..f6eec72b843 100644 --- a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc @@ -308,6 +308,10 @@ class LoggingValidationTestForNickname CardUnmaskPromptControllerImplGenericTest::SetUp(); pref_service_->registry()->RegisterBooleanPref( prefs::kAutofillWalletImportStorageCheckboxState, false); +#if defined(OS_ANDROID) + pref_service_->registry()->RegisterBooleanPref( + prefs::kAutofillCreditCardFidoAuthOfferCheckboxState, true); +#endif SetCreditCardForTesting(card_has_nickname_ ? test::GetMaskedServerCardWithNickname() : test::GetMaskedServerCard()); @@ -635,6 +639,10 @@ class CvcInputValidationTest : public CardUnmaskPromptControllerImplGenericTest, CardUnmaskPromptControllerImplGenericTest::SetUp(); pref_service_->registry()->RegisterBooleanPref( prefs::kAutofillWalletImportStorageCheckboxState, false); +#if defined(OS_ANDROID) + pref_service_->registry()->RegisterBooleanPref( + prefs::kAutofillCreditCardFidoAuthOfferCheckboxState, true); +#endif } private: @@ -674,6 +682,10 @@ class CvcInputAmexValidationTest CardUnmaskPromptControllerImplGenericTest::SetUp(); pref_service_->registry()->RegisterBooleanPref( prefs::kAutofillWalletImportStorageCheckboxState, false); +#if defined(OS_ANDROID) + pref_service_->registry()->RegisterBooleanPref( + prefs::kAutofillCreditCardFidoAuthOfferCheckboxState, true); +#endif } private: @@ -722,6 +734,10 @@ class ExpirationDateValidationTest CardUnmaskPromptControllerImplGenericTest::SetUp(); pref_service_->registry()->RegisterBooleanPref( prefs::kAutofillWalletImportStorageCheckboxState, false); +#if defined(OS_ANDROID) + pref_service_->registry()->RegisterBooleanPref( + prefs::kAutofillCreditCardFidoAuthOfferCheckboxState, true); +#endif } private: diff --git a/chromium/components/autofill/core/browser/ui/suggestion.h b/chromium/components/autofill/core/browser/ui/suggestion.h index c69aa387f65..8c9c505888d 100644 --- a/chromium/components/autofill/core/browser/ui/suggestion.h +++ b/chromium/components/autofill/core/browser/ui/suggestion.h @@ -9,13 +9,13 @@ #include "base/strings/string16.h" #include "base/strings/string_piece.h" -#include "base/util/type_safety/strong_alias.h" +#include "base/types/strong_alias.h" #include "ui/gfx/image/image.h" namespace autofill { struct Suggestion { - using IsLoading = util::StrongAlias<class IsLoadingTag, bool>; + using IsLoading = base::StrongAlias<class IsLoadingTag, bool>; enum MatchMode { PREFIX_MATCH, // for prefix matched suggestions; diff --git a/chromium/components/autofill/core/browser/ui/suggestion_selection.cc b/chromium/components/autofill/core/browser/ui/suggestion_selection.cc index 1281ad65ae9..fe7e487bd12 100644 --- a/chromium/components/autofill/core/browser/ui/suggestion_selection.cc +++ b/chromium/components/autofill/core/browser/ui/suggestion_selection.cc @@ -108,7 +108,7 @@ std::vector<Suggestion> GetPrefixMatchedSuggestions( /* is_masked_server_card= */ false, &prefix_matched_suggestion)) { matched_profiles->push_back(profile); - if (type.group() == PHONE_HOME) { + if (type.group() == FieldTypeGroup::kPhoneHome) { bool format_phone; #if defined(OS_ANDROID) || defined(OS_IOS) @@ -213,7 +213,8 @@ bool IsValidSuggestionForFieldContents(base::string16 suggestion_canon, // Phones should do a substring match because they can be trimmed to remove // the first parts (e.g. country code or prefix). It is still considered a // prefix match in order to put it at the top of the suggestions. - if ((type.group() == PHONE_HOME || type.group() == PHONE_BILLING) && + if ((type.group() == FieldTypeGroup::kPhoneHome || + type.group() == FieldTypeGroup::kPhoneBilling) && suggestion_canon.find(field_contents_canon) != base::string16::npos) { return true; } diff --git a/chromium/components/autofill/core/browser/validation.cc b/chromium/components/autofill/core/browser/validation.cc index deee50f2db7..7eb2b39ff0c 100644 --- a/chromium/components/autofill/core/browser/validation.cc +++ b/chromium/components/autofill/core/browser/validation.cc @@ -204,9 +204,9 @@ bool IsSSN(const base::string16& text) { return false; int area; - if (!base::StringToInt( - base::StringPiece16(number_string.begin(), number_string.begin() + 3), - &area)) { + if (!base::StringToInt(base::MakeStringPiece16(number_string.begin(), + number_string.begin() + 3), + &area)) { return false; } if (area < 1 || area == 666 || area >= 900) { @@ -214,16 +214,16 @@ bool IsSSN(const base::string16& text) { } int group; - if (!base::StringToInt(base::StringPiece16(number_string.begin() + 3, - number_string.begin() + 5), + if (!base::StringToInt(base::MakeStringPiece16(number_string.begin() + 3, + number_string.begin() + 5), &group) || group == 0) { return false; } int serial; - if (!base::StringToInt(base::StringPiece16(number_string.begin() + 5, - number_string.begin() + 9), + if (!base::StringToInt(base::MakeStringPiece16(number_string.begin() + 5, + number_string.begin() + 9), &serial) || serial == 0) { return false; diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc index 9cda0484f05..eaa0208daf3 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc @@ -18,11 +18,11 @@ #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/common/autofill_features.h" -#include "components/sync/model/entity_data.h" +#include "components/sync/engine/entity_data.h" +#include "components/sync/model/client_tag_based_model_type_processor.h" #include "components/sync/model/model_type_change_processor.h" #include "components/sync/model/mutable_data_batch.h" -#include "components/sync/model_impl/client_tag_based_model_type_processor.h" -#include "components/sync/model_impl/sync_metadata_store_change_list.h" +#include "components/sync/model/sync_metadata_store_change_list.h" #include "net/base/escape.h" using base::Optional; @@ -306,7 +306,7 @@ AutocompleteSyncBridge::AutocompleteSyncBridge( web_data_backend_(backend) { DCHECK(web_data_backend_); - scoped_observer_.Add(web_data_backend_); + scoped_observation_.Observe(web_data_backend_); LoadMetadata(); } diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h index 197e46af932..a700a7aba1a 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h @@ -10,7 +10,7 @@ #include "base/macros.h" #include "base/optional.h" -#include "base/scoped_observer.h" +#include "base/scoped_observation.h" #include "base/supports_user_data.h" #include "base/threading/thread_checker.h" #include "components/autofill/core/browser/webdata/autofill_change.h" @@ -79,9 +79,9 @@ class AutocompleteSyncBridge // SupportsUserData, so it's guaranteed to outlive |this|. AutofillWebDataBackend* const web_data_backend_; - ScopedObserver<AutofillWebDataBackend, - AutofillWebDataServiceObserverOnDBSequence> - scoped_observer_{this}; + base::ScopedObservation<AutofillWebDataBackend, + AutofillWebDataServiceObserverOnDBSequence> + scoped_observation_{this}; DISALLOW_COPY_AND_ASSIGN(AutocompleteSyncBridge); }; diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc index 4c580c90dc0..44fc22b4c55 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc @@ -24,13 +24,13 @@ #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h" #include "components/sync/base/client_tag_hash.h" +#include "components/sync/model/client_tag_based_model_type_processor.h" #include "components/sync/model/data_batch.h" #include "components/sync/model/data_type_activation_request.h" #include "components/sync/model/metadata_batch.h" #include "components/sync/model/model_error.h" -#include "components/sync/model_impl/client_tag_based_model_type_processor.h" #include "components/sync/test/model/mock_model_type_change_processor.h" -#include "components/sync/test/test_matchers.h" +#include "components/sync/test/model/test_matchers.h" #include "components/webdata/common/web_database.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc index d6f098f58b0..02d8bc8b044 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc @@ -10,8 +10,8 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/containers/contains.h" #include "base/guid.h" -#include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_profile_sync_util.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" @@ -21,13 +21,13 @@ #include "components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" -#include "components/sync/model/entity_data.h" +#include "components/sync/engine/entity_data.h" +#include "components/sync/model/client_tag_based_model_type_processor.h" #include "components/sync/model/metadata_change_list.h" #include "components/sync/model/model_error.h" #include "components/sync/model/model_type_change_processor.h" #include "components/sync/model/mutable_data_batch.h" -#include "components/sync/model_impl/client_tag_based_model_type_processor.h" -#include "components/sync/model_impl/sync_metadata_store_change_list.h" +#include "components/sync/model/sync_metadata_store_change_list.h" using base::Optional; using base::UTF16ToUTF8; @@ -82,7 +82,7 @@ AutofillProfileSyncBridge::AutofillProfileSyncBridge( web_data_backend_(backend) { DCHECK(web_data_backend_); - scoped_observer_.Add(web_data_backend_); + scoped_observation_.Observe(web_data_backend_); LoadMetadata(); } diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h index d03693a76ea..be44a88d238 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h @@ -10,7 +10,7 @@ #include "base/macros.h" #include "base/optional.h" -#include "base/scoped_observer.h" +#include "base/scoped_observation.h" #include "base/supports_user_data.h" #include "base/threading/thread_checker.h" #include "components/autofill/core/browser/webdata/autofill_change.h" @@ -107,9 +107,9 @@ class AutofillProfileSyncBridge // SupportsUserData, so it's guaranteed to outlive |this|. AutofillWebDataBackend* const web_data_backend_; - ScopedObserver<AutofillWebDataBackend, - AutofillWebDataServiceObserverOnDBSequence> - scoped_observer_{this}; + base::ScopedObservation<AutofillWebDataBackend, + AutofillWebDataServiceObserverOnDBSequence> + scoped_observation_{this}; DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncBridge); }; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc index 171f079d9f8..7c56186a303 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc @@ -32,12 +32,12 @@ #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_features.h" #include "components/sync/base/client_tag_hash.h" +#include "components/sync/engine/entity_data.h" +#include "components/sync/model/client_tag_based_model_type_processor.h" #include "components/sync/model/data_batch.h" #include "components/sync/model/data_type_activation_request.h" -#include "components/sync/model/entity_data.h" #include "components/sync/model/sync_data.h" #include "components/sync/model/sync_error_factory.h" -#include "components/sync/model_impl/client_tag_based_model_type_processor.h" #include "components/sync/protocol/sync.pb.h" #include "components/sync/test/model/mock_model_type_change_processor.h" #include "components/sync/test/model/sync_error_factory_mock.h" @@ -183,6 +183,7 @@ AutofillProfile ConstructCompleteProfile() { profile.SetRawInfo(ADDRESS_HOME_PREMISE_NAME, ASCIIToUTF16("Premise")); profile.set_language_code("en"); profile.SetClientValidityFromBitfieldValue(kValidityStateBitfield); + profile.FinalizeAfterImport(); return profile; } @@ -415,6 +416,7 @@ TEST_P(AutofillProfileSyncBridgeTest, AutofillProfileChanged_Added) { AutofillProfile local(kGuidA, kHttpsOrigin); local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane")); + local.FinalizeAfterImport(); AutofillProfileChange change(AutofillProfileChange::ADD, kGuidA, &local); EXPECT_CALL( @@ -560,9 +562,11 @@ TEST_P(AutofillProfileSyncBridgeTest, GetAllDataForDebugging) { AutofillProfile local1 = AutofillProfile(kGuidA, kHttpsOrigin); local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local1.FinalizeAfterImport(); AutofillProfile local2 = AutofillProfile(kGuidB, kHttpsOrigin); local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); + local2.FinalizeAfterImport(); AddAutofillProfilesToTable({local1, local2}); EXPECT_THAT(GetAllLocalData(), UnorderedElementsAre(local1, local2)); @@ -572,9 +576,11 @@ TEST_P(AutofillProfileSyncBridgeTest, GetData) { AutofillProfile local1 = AutofillProfile(kGuidA, kHttpsOrigin); local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local1.FinalizeAfterImport(); AutofillProfile local2 = AutofillProfile(kGuidB, kHttpsOrigin); local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); + local2.FinalizeAfterImport(); AddAutofillProfilesToTable({local1, local2}); std::vector<AutofillProfile> data; @@ -595,10 +601,11 @@ TEST_P(AutofillProfileSyncBridgeTest, MergeSyncData) { AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin); local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); - + local1.FinalizeAfterImport(); AutofillProfile local2 = AutofillProfile(kGuidB, std::string()); local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); + local2.FinalizeAfterImport(); AddAutofillProfilesToTable({local1, local2}); @@ -688,6 +695,7 @@ TEST_P(AutofillProfileSyncBridgeTest, ProfileMigration) { // the server. TEST_P(AutofillProfileSyncBridgeTest, MergeSyncData_SyncAllFieldsToServer) { AutofillProfile local = ConstructCompleteProfile(); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // This complete profile is fully uploaded to sync. @@ -714,26 +722,42 @@ TEST_P(AutofillProfileSyncBridgeTest, MergeSyncData_SyncAllFieldsToClient) { TEST_P(AutofillProfileSyncBridgeTest, MergeSyncData_IdenticalProfiles) { AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin); - local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); - local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local1.SetRawInfoWithVerificationStatus( + NAME_FIRST, ASCIIToUTF16("John"), + structured_address::VerificationStatus::kObserved); + local1.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"), + structured_address::VerificationStatus::kObserved); + local1.FinalizeAfterImport(); AutofillProfile local2 = AutofillProfile(kGuidB, kSettingsOrigin); - local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); - local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); - + local2.SetRawInfoWithVerificationStatus( + NAME_FIRST, ASCIIToUTF16("Tom"), + structured_address::VerificationStatus::kObserved); + local2.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st"), + structured_address::VerificationStatus::kObserved); + local2.FinalizeAfterImport(); AddAutofillProfilesToTable({local1, local2}); // The synced profiles are identical to the local ones, except that the guids // are different. - AutofillProfile remote1 = AutofillProfile(kGuidC, kHttpsOrigin); - remote1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); - remote1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + remote1.SetRawInfoWithVerificationStatus( + NAME_FIRST, ASCIIToUTF16("John"), + structured_address::VerificationStatus::kObserved); + remote1.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"), + structured_address::VerificationStatus::kObserved); remote1.FinalizeAfterImport(); AutofillProfile remote2 = AutofillProfile(kGuidD, kHttpsOrigin); - remote2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); - remote2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); + remote2.SetRawInfoWithVerificationStatus( + NAME_FIRST, ASCIIToUTF16("Tom"), + structured_address::VerificationStatus::kObserved); + remote2.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st"), + structured_address::VerificationStatus::kObserved); remote2.FinalizeAfterImport(); AutofillProfileSpecifics remote1_specifics = @@ -762,6 +786,7 @@ TEST_P(AutofillProfileSyncBridgeTest, MergeSyncData_NonSimilarProfiles) { local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); local.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("K.")); local.SetRawInfo(NAME_LAST, ASCIIToUTF16("Doe")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile are not similar as the names are different (all other @@ -937,6 +962,7 @@ TEST_P(AutofillProfileSyncBridgeTest, MergeSyncData_SimilarProfiles_LocalOriginPreserved) { AutofillProfile local(kGuidA, kHttpsOrigin); local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); AutofillProfile remote_profile = AutofillProfile(kGuidB, kHttpOrigin); @@ -964,6 +990,7 @@ TEST_P(AutofillProfileSyncBridgeTest, TEST_P(AutofillProfileSyncBridgeTest, MergeSyncData_SimilarProfiles_LocalExistingOriginPreserved) { AutofillProfile local(kGuidA, kHttpsOrigin); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // Remote data does not have an origin value. @@ -980,6 +1007,7 @@ TEST_P(AutofillProfileSyncBridgeTest, // Expect the local autofill profile to still have an origin after sync. AutofillProfile merged(local); merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + merged.FinalizeAfterImport(); EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); } @@ -991,6 +1019,7 @@ TEST_P(AutofillProfileSyncBridgeTest, MergeSyncData_SimilarProfiles_LocalMissingOriginPreserved) { AutofillProfile local = AutofillProfile(kGuidA, std::string()); local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // Create a Sync profile identical to |local|, except with no origin set. @@ -1010,6 +1039,7 @@ TEST_P(AutofillProfileSyncBridgeTest, TEST_P(AutofillProfileSyncBridgeTest, ApplySyncChanges) { AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); StartSyncing({}); @@ -1106,17 +1136,25 @@ TEST_P(AutofillProfileSyncBridgeTest, remote.set_address_home_street_address( "456 El Camino Real\n" "Suite #1337"); + remote.set_address_home_street_address_status( + sync_pb::AutofillProfileSpecifics_VerificationStatus_OBSERVED); EXPECT_CALL(*backend(), CommitChanges()); - StartSyncing({remote}); // Verify that full street address takes precedence over address lines. AutofillProfile local(kGuidA, kHttpsOrigin); - local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, - ASCIIToUTF16("456 El Camino Real\n" - "Suite #1337")); - local.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("456 El Camino Real")); - local.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Suite #1337")); + local.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, + ASCIIToUTF16("456 El Camino Real\n" + "Suite #1337"), + structured_address::VerificationStatus::kObserved); + local.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_LINE1, ASCIIToUTF16("456 El Camino Real"), + structured_address::VerificationStatus::kObserved); + local.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_LINE2, ASCIIToUTF16("Suite #1337"), + structured_address::VerificationStatus::kObserved); + local.FinalizeAfterImport(); EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); } @@ -1128,8 +1166,10 @@ TEST_P(AutofillProfileSyncBridgeTest, TEST_P(AutofillProfileSyncBridgeTest, RemoteWithSameGuid_StreetAddress_NoUpdateToEmptyStreetAddressSyncedUp) { AutofillProfile local(kGuidA, kHttpsOrigin); - local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Example St.\n" - "Apt. 42")); + local.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Example St.\nApt. 42"), + structured_address::VerificationStatus::kObserved); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // Create a Sync profile identical to |profile|, except without street address @@ -1310,6 +1350,7 @@ TEST_P(AutofillProfileSyncBridgeTest, // Expect local autofill profile to still have the validity state after. AutofillProfile merged(local); merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + merged.FinalizeAfterImport(); // No update to sync, the local validity bitfield should stay untouched. EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); @@ -1324,6 +1365,7 @@ TEST_P(AutofillProfileSyncBridgeTest, // Local autofill profile has an empty full name. AutofillProfile local(kGuidA, kHttpsOrigin); local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // Remote data does not have a full name value. diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc index 6718b8c760b..d31aa724c7d 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc @@ -4,6 +4,7 @@ #include "components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h" +#include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_profile_sync_util.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc index 03f8b7baef7..6ce404382e4 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc @@ -139,11 +139,13 @@ TEST_F(AutofillProfileSyncDifferenceTrackerTest, IncorporateRemoteProfileShouldOverwriteProfileWithSameKey) { AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin); local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile is completely different but it has the same key. AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin); remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); + remote.FinalizeAfterImport(); IncorporateRemoteProfile(remote); @@ -158,11 +160,13 @@ TEST_F(AutofillProfileSyncDifferenceTrackerTest, IncorporateRemoteProfileShouldOverwriteUnverifiedProfileByVerified) { AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpsOrigin); local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile has the same key but it is not verified. AutofillProfile remote = AutofillProfile(kSmallerGuid, kSettingsOrigin); remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); + remote.FinalizeAfterImport(); IncorporateRemoteProfile(remote); @@ -177,12 +181,13 @@ TEST_F(AutofillProfileSyncDifferenceTrackerTest, IncorporateRemoteProfileShouldNotOverwriteVerifiedProfileByUnverified) { AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin); local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile has the same key but it is not verified. AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin); remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); - + remote.FinalizeAfterImport(); IncorporateRemoteProfile(remote); // Nothing gets uploaded to sync and the local profile wins. @@ -196,6 +201,7 @@ TEST_F(AutofillProfileSyncDifferenceTrackerTest, IncorporateRemoteProfileShouldNotOverwriteFullNameByEmptyString) { AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin); local.SetRawInfo(NAME_FULL, ASCIIToUTF16("John")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile has the same key. @@ -204,7 +210,7 @@ TEST_F(AutofillProfileSyncDifferenceTrackerTest, AutofillProfile merged(remote); merged.SetRawInfo(NAME_FULL, ASCIIToUTF16("John")); - + merged.FinalizeAfterImport(); IncorporateRemoteProfile(remote); // Nothing gets uploaded to sync and the remote profile wins except for the @@ -221,6 +227,7 @@ TEST_F( AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin); local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile is identical to the local one, except that the guids and @@ -228,7 +235,7 @@ TEST_F( AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpsOrigin); remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); - + remote.FinalizeAfterImport(); IncorporateRemoteProfile(remote); // Nothing gets uploaded to sync and the remote profile wins. @@ -243,14 +250,24 @@ TEST_F( AutofillProfileSyncDifferenceTrackerTest, IncorporateRemoteProfileShouldKeepRemoteKeyAndLocalOriginWhenMergingDuplicateProfileWithBiggerKey) { AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin); - local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); - local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local.SetRawInfoWithVerificationStatus( + NAME_FIRST, ASCIIToUTF16("John"), + structured_address::VerificationStatus::kObserved); + local.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"), + structured_address::VerificationStatus::kObserved); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile has the same key. AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpsOrigin); - remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); - remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + remote.SetRawInfoWithVerificationStatus( + NAME_FIRST, ASCIIToUTF16("John"), + structured_address::VerificationStatus::kObserved); + remote.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"), + structured_address::VerificationStatus::kObserved); + remote.FinalizeAfterImport(); AutofillProfile merged(remote); merged.set_origin(kSettingsOrigin); @@ -272,6 +289,7 @@ TEST_F( AutofillProfile local = AutofillProfile(kBiggerGuid, kHttpOrigin); local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile is identical to the local one, except that the guids and @@ -279,7 +297,7 @@ TEST_F( AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin); remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); - + remote.FinalizeAfterImport(); IncorporateRemoteProfile(remote); // Nothing gets uploaded to sync and the remote profile wins. @@ -294,18 +312,27 @@ TEST_F( AutofillProfileSyncDifferenceTrackerTest, IncorporateRemoteProfileShouldKeepLocalKeyAndRemoteOriginWhenMergingDuplicateProfileWithSmallerKey) { AutofillProfile local = AutofillProfile(kBiggerGuid, kHttpsOrigin); - local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); - local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local.SetRawInfoWithVerificationStatus( + NAME_FIRST, ASCIIToUTF16("John"), + structured_address::VerificationStatus::kUserVerified); + local.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"), + structured_address::VerificationStatus::kUserVerified); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile has the same key. AutofillProfile remote = AutofillProfile(kSmallerGuid, kSettingsOrigin); - remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); - remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + remote.SetRawInfoWithVerificationStatus( + NAME_FIRST, ASCIIToUTF16("John"), + structured_address::VerificationStatus::kUserVerified); + remote.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"), + structured_address::VerificationStatus::kUserVerified); + remote.FinalizeAfterImport(); AutofillProfile merged(local); merged.set_origin(kSettingsOrigin); - IncorporateRemoteProfile(remote); // Nothing gets uploaded to sync and the remote profile wins except for the @@ -363,6 +390,7 @@ TEST_F(AutofillProfileSyncDifferenceTrackerTest, AutofillProfile remote = AutofillProfile(kSmallerGuid, kHttpsOrigin); remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + remote.FinalizeAfterImport(); IncorporateRemoteProfile(remote); MockCallback<base::OnceClosure> autofill_changes_callback; @@ -400,6 +428,7 @@ TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin); local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); local.set_use_count(27); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile matches the local one (except for origin and use count). @@ -407,7 +436,7 @@ TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); remote.set_use_count(13); - + remote.FinalizeAfterImport(); // The remote profile wins (as regards the storage key). AutofillProfile merged(remote); // The local origin wins when merging. @@ -430,6 +459,7 @@ TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin); local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); local.set_use_count(13); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile matches the local one and has some additional data. @@ -438,7 +468,7 @@ TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); // Merging two profile takes their max use count, so use count of 27 is taken. remote.set_use_count(27); - + remote.FinalizeAfterImport(); IncorporateRemoteProfile(remote); MergeSimilarEntriesForInitialSync(); @@ -454,13 +484,14 @@ TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin); local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); local.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile has a different street address. AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpOrigin); remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2st st")); remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); - + remote.FinalizeAfterImport(); IncorporateRemoteProfile(remote); MergeSimilarEntriesForInitialSync(); @@ -477,13 +508,14 @@ TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, // The local entry is verified, should not get merged. AutofillProfile local = AutofillProfile(kSmallerGuid, kSettingsOrigin); local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile is similar to the local one. AutofillProfile remote = AutofillProfile(kBiggerGuid, kHttpOrigin); remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); - + remote.FinalizeAfterImport(); IncorporateRemoteProfile(remote); MergeSimilarEntriesForInitialSync(); @@ -499,6 +531,7 @@ TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, MergeSimilarEntriesForInitialSyncDoesNotMatchRemoteVerifiedEntry) { AutofillProfile local = AutofillProfile(kSmallerGuid, kHttpOrigin); local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); + local.FinalizeAfterImport(); AddAutofillProfilesToTable({local}); // The remote profile is similar to the local one but is verified and thus it @@ -506,7 +539,7 @@ TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest, AutofillProfile remote = AutofillProfile(kBiggerGuid, kSettingsOrigin); remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc.")); - + remote.FinalizeAfterImport(); IncorporateRemoteProfile(remote); MergeSimilarEntriesForInitialSync(); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc index e7a4a3a53d8..374a557d888 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc @@ -16,7 +16,7 @@ #include "components/autofill/core/browser/payments/payments_customer_data.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/common/autofill_util.h" -#include "components/sync/model/entity_data.h" +#include "components/sync/engine/entity_data.h" using autofill::data_util::TruncateUTF8; using sync_pb::AutofillWalletSpecifics; @@ -373,6 +373,9 @@ AutofillProfile ProfileFromSpecifics( profile.GenerateServerProfileIdentifier(); + // Call the finalization routine to parse the structure of the name and the + // address into their components. + profile.FinalizeAfterImport(); return profile; } diff --git a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h index ed222801aee..fd4759dc6a2 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h @@ -8,8 +8,8 @@ #include <memory> #include <string> +#include "components/sync/engine/entity_data.h" #include "components/sync/model/entity_change.h" -#include "components/sync/model/entity_data.h" namespace autofill { diff --git a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc index b22bdd06e51..ac068999eca 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc @@ -18,7 +18,7 @@ #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/sync/base/client_tag_hash.h" -#include "components/sync/model/entity_data.h" +#include "components/sync/engine/entity_data.h" #include "components/sync/protocol/sync.pb.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc index 2d14512f1f7..52ea59d94bb 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc @@ -213,8 +213,10 @@ bool AddAutofillProfileNames(const AutofillProfile& profile, "conjunction_last_name, conjunction_last_name_status, " "second_last_name, second_last_name_status, " "last_name, last_name_status, " - "full_name, full_name_status) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); + "full_name, full_name_status, " + "full_name_with_honorific_prefix, " + "full_name_with_honorific_prefix_status) " + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); s.BindString(0, profile.guid()); s.BindString16(1, profile.GetRawInfo(NAME_HONORIFIC_PREFIX)); s.BindInt(2, profile.GetVerificationStatusInt(NAME_HONORIFIC_PREFIX)); @@ -232,6 +234,9 @@ bool AddAutofillProfileNames(const AutofillProfile& profile, s.BindInt(14, profile.GetVerificationStatusInt(NAME_LAST)); s.BindString16(15, profile.GetRawInfo(NAME_FULL)); s.BindInt(16, profile.GetVerificationStatusInt(NAME_FULL)); + s.BindString16(17, profile.GetRawInfo(NAME_FULL_WITH_HONORIFIC_PREFIX)); + s.BindInt( + 18, profile.GetVerificationStatusInt(NAME_FULL_WITH_HONORIFIC_PREFIX)); return s.Run(); } // Add the new name. @@ -271,8 +276,10 @@ bool AddAutofillProfileAddresses(const AutofillProfile& profile, "state, state_status, " "zip_code, zip_code_status, " "sorting_code, sorting_code_status, " - "country_code, country_code_status) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); + "country_code, country_code_status, " + "apartment_number, apartment_number_status, " + "floor, floor_status) " + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); s.BindString(0, profile.guid()); s.BindString16(1, profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); @@ -301,6 +308,10 @@ bool AddAutofillProfileAddresses(const AutofillProfile& profile, s.BindInt(22, profile.GetVerificationStatusInt(ADDRESS_HOME_SORTING_CODE)); s.BindString16(23, profile.GetRawInfo(ADDRESS_HOME_COUNTRY)); s.BindInt(24, profile.GetVerificationStatusInt(ADDRESS_HOME_COUNTRY)); + s.BindString16(25, profile.GetRawInfo(ADDRESS_HOME_APT_NUM)); + s.BindInt(26, profile.GetVerificationStatusInt(ADDRESS_HOME_APT_NUM)); + s.BindString16(27, profile.GetRawInfo(ADDRESS_HOME_FLOOR)); + s.BindInt(28, profile.GetVerificationStatusInt(ADDRESS_HOME_FLOOR)); return s.Run(); } @@ -319,7 +330,8 @@ bool AddAutofillProfileNamesToProfile(sql::Database* db, "conjunction_last_name, conjunction_last_name_status, " "second_last_name, second_last_name_status, " "last_name, last_name_status, " - "full_name, full_name_status " + "full_name, full_name_status, " + "full_name_with_honorific_prefix, full_name_with_honorific_prefix_status " "FROM autofill_profile_names " "WHERE guid=? " "LIMIT 1")); @@ -352,6 +364,9 @@ bool AddAutofillProfileNamesToProfile(sql::Database* db, NAME_LAST, s.ColumnString16(13), s.ColumnInt(14)); profile->SetRawInfoWithVerificationStatusInt( NAME_FULL, s.ColumnString16(15), s.ColumnInt(16)); + profile->SetRawInfoWithVerificationStatusInt( + NAME_FULL_WITH_HONORIFIC_PREFIX, s.ColumnString16(17), + s.ColumnInt(18)); } else { // If structured components are not enabled, only use the legacy // structure. @@ -388,7 +403,9 @@ bool AddAutofillProfileAddressesToProfile(sql::Database* db, "state, state_status, " "zip_code, zip_code_status, " "sorting_code, sorting_code_status, " - "country_code, country_code_status " + "country_code, country_code_status, " + "apartment_number, apartment_number_status, " + "floor, floor_status " "FROM autofill_profile_addresses " "WHERE guid=? " "LIMIT 1")); @@ -455,6 +472,10 @@ bool AddAutofillProfileAddressesToProfile(sql::Database* db, ADDRESS_HOME_SORTING_CODE, sorting_code, s.ColumnInt(22)); profile->SetRawInfoWithVerificationStatusInt(ADDRESS_HOME_COUNTRY, country, s.ColumnInt(24)); + profile->SetRawInfoWithVerificationStatusInt( + ADDRESS_HOME_APT_NUM, s.ColumnString16(25), s.ColumnInt(26)); + profile->SetRawInfoWithVerificationStatusInt( + ADDRESS_HOME_FLOOR, s.ColumnString16(27), s.ColumnInt(28)); } else { // Remove the structured information from the table for // eventual deletion consistency. @@ -759,6 +780,12 @@ bool AutofillTable::MigrateToVersion(int version, case 90: *update_compatible_version = false; return MigrateToVersion90AddNewStructuredAddressColumns(); + case 91: + *update_compatible_version = false; + return MigrateToVersion91AddMoreStructuredAddressColumns(); + case 92: + *update_compatible_version = false; + return MigrateToVersion92AddNewPrefixedNameColumn(); } return true; } @@ -3260,6 +3287,27 @@ bool AutofillTable::MigrateToVersion88AddNewNameColumns() { return true; } +bool AutofillTable::MigrateToVersion92AddNewPrefixedNameColumn() { + if (!db_->DoesColumnExist("autofill_profile_names", + "full_name_with_honorific_prefix") && + !db_->Execute( + base::StrCat({"ALTER TABLE autofill_profile_names ADD COLUMN ", + "full_name_with_honorific_prefix", " VARCHAR"}) + .c_str())) { + return false; + } + if (!db_->DoesColumnExist("autofill_profile_names", + "full_name_with_honorific_prefix_status") && + !db_->Execute( + base::StrCat({"ALTER TABLE autofill_profile_names ADD COLUMN ", + "full_name_with_honorific_prefix_status", + " INTEGER DEFAULT 0"}) + .c_str())) { + return false; + } + return true; +} + bool AutofillTable::MigrateToVersion86RemoveUnmaskedCreditCardsUseColumns() { // Sqlite does not support "alter table drop column" syntax, so it has be // done manually. @@ -3317,6 +3365,35 @@ bool AutofillTable::MigrateToVersion90AddNewStructuredAddressColumns() { } return true; } + +bool AutofillTable::MigrateToVersion91AddMoreStructuredAddressColumns() { + if (!db_->DoesTableExist("autofill_profile_addresses")) + InitProfileAddressesTable(); + + for (const char* column : {"apartment_number", "floor"}) { + if (!db_->DoesColumnExist("autofill_profile_addresses", column) && + !db_->Execute( + base::StrCat({"ALTER TABLE autofill_profile_addresses ADD COLUMN ", + column, " VARCHAR"}) + .c_str())) { + return false; + } + } + + for (const char* column : {"apartment_number_status", "floor_status"}) { + // The default value of 0 corresponds to the verification status + // |kNoStatus|. + if (!db_->DoesColumnExist("autofill_profile_addresses", column) && + !db_->Execute( + base::StrCat({"ALTER TABLE autofill_profile_addresses ADD COLUMN ", + column, " INTEGER DEFAULT 0"}) + .c_str())) { + return false; + } + } + return true; +} + bool AutofillTable:: MigrateToVersion89AddInstrumentIdColumnToMaskedCreditCard() { // Add the new instrument_id column to the masked_credit_cards table and set @@ -3633,24 +3710,27 @@ bool AutofillTable::InitProfileNamesTable() { if (!db_->DoesTableExist("autofill_profile_names")) { // The default value of 0 corresponds to the verification status // |kNoStatus|. - if (!db_->Execute("CREATE TABLE autofill_profile_names ( " - "guid VARCHAR, " - "first_name VARCHAR, " - "middle_name VARCHAR, " - "last_name VARCHAR, " - "full_name VARCHAR, " - "honorific_prefix VARCHAR, " - "first_last_name VARCHAR, " - "conjunction_last_name VARCHAR, " - "second_last_name VARCHAR, " - "honorific_prefix_status INTEGER DEFAULT 0, " - "first_name_status INTEGER DEFAULT 0, " - "middle_name_status INTEGER DEFAULT 0, " - "last_name_status INTEGER DEFAULT 0, " - "first_last_name_status INTEGER DEFAULT 0, " - "conjunction_last_name_status INTEGER DEFAULT 0, " - "second_last_name_status INTEGER DEFAULT 0, " - "full_name_status INTEGER DEFAULT 0)")) { + if (!db_->Execute( + "CREATE TABLE autofill_profile_names ( " + "guid VARCHAR, " + "first_name VARCHAR, " + "middle_name VARCHAR, " + "last_name VARCHAR, " + "full_name VARCHAR, " + "honorific_prefix VARCHAR, " + "first_last_name VARCHAR, " + "conjunction_last_name VARCHAR, " + "second_last_name VARCHAR, " + "honorific_prefix_status INTEGER DEFAULT 0, " + "first_name_status INTEGER DEFAULT 0, " + "middle_name_status INTEGER DEFAULT 0, " + "last_name_status INTEGER DEFAULT 0, " + "first_last_name_status INTEGER DEFAULT 0, " + "conjunction_last_name_status INTEGER DEFAULT 0, " + "second_last_name_status INTEGER DEFAULT 0, " + "full_name_status INTEGER DEFAULT 0, " + "full_name_with_honorific_prefix VARCHAR, " + "full_name_with_honorific_prefix_status INTEGER DEFAULT 0)")) { NOTREACHED(); return false; } @@ -3687,7 +3767,11 @@ bool AutofillTable::InitProfileAddressesTable() { "state_status INTEGER DEFAULT 0, " "zip_code_status INTEGER DEFAULT 0, " "sorting_code_status INTEGER DEFAULT 0, " - "country_code_status INTEGER DEFAULT 0)")) { + "country_code_status INTEGER DEFAULT 0, " + "apartment_number VARCHAR, " + "floor VARCHAR, " + "apartment_number_status INTEGER DEFAULT 0, " + "floor_status INTEGER DEFAULT 0)")) { NOTREACHED(); return false; } diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.h b/chromium/components/autofill/core/browser/webdata/autofill_table.h index 8d3824c9653..91b13512f01 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h @@ -125,6 +125,8 @@ struct PaymentsCustomerData; // or organizations that might not be geographically // contiguous. // premise_name The name of the premise. +// apartment_number The number of the apartment. +// floor The floor in which the apartment is located. // street_address_status // street_name_status // dependent_street_name_status @@ -137,6 +139,8 @@ struct PaymentsCustomerData; // zip_code_status // country_code_status // sorting_code_status +// apartment_number_status +// floor_status // Each token of the address has an additional validation // status that indicates if Autofill parsed the value out // of an unstructured (last) name, or if autofill formatted @@ -165,6 +169,9 @@ struct PaymentsCustomerData; // consisting of a single part are stored in the second // part by default. // full_name The unstructured full name of a person. +// full_name_with_honorific_prefix +// The combination of the full name and the honorific +// prefix. // honorific_prefix_status // first_name_status // middle_name_status @@ -172,6 +179,7 @@ struct PaymentsCustomerData; // first_last_name_status // conjunction_last_name_status // second_last_name_status +// full_name_with_honorific_prefix_status // Each token of the names has an additional validation // status that indicates if Autofill parsed the value out // of an unstructured (last) name, or if autofill formatted @@ -674,6 +682,8 @@ class AutofillTable : public WebDatabaseTable, bool MigrateToVersion88AddNewNameColumns(); bool MigrateToVersion89AddInstrumentIdColumnToMaskedCreditCard(); bool MigrateToVersion90AddNewStructuredAddressColumns(); + bool MigrateToVersion91AddMoreStructuredAddressColumns(); + bool MigrateToVersion92AddNewPrefixedNameColumn(); // Max data length saved in the table, AKA the maximum length allowed for // form data. diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc index aa3324298db..92683da27b8 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc @@ -849,8 +849,9 @@ TEST_F(AutofillTableTest, RemoveExpiredFormElements_NotOldEnough) { TEST_F(AutofillTableTest, AutofillProfile_StructuredNames_BackAndForthMigration) { // Enable the structured names. - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillEnableSupportForMoreStructureInNames); + scoped_feature_list_.InitWithFeatures( + {features::kAutofillEnableSupportForMoreStructureInNames}, + {features::kAutofillEnableSupportForMoreStructureInAddresses}); AutofillProfile structured_name_profile; structured_name_profile.set_origin(std::string()); @@ -1046,7 +1047,7 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredAddresses) { profile.SetRawInfoWithVerificationStatus( ADDRESS_HOME_STREET_ADDRESS, - ASCIIToUTF16("Street Name House Number Premise Subpremise"), + ASCIIToUTF16("Street Name House Number Premise APT 10 Floor 2"), VerificationStatus::kUserVerified); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STREET_NAME, ASCIIToUTF16("Street Name"), @@ -1079,8 +1080,12 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredAddresses) { ASCIIToUTF16("House Number"), VerificationStatus::kUserVerified); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_SUBPREMISE, - ASCIIToUTF16("Subpremise"), + ASCIIToUTF16("APT 10 Floor 2"), VerificationStatus::kUserVerified); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_APT_NUM, ASCIIToUTF16("10"), VerificationStatus::kParsed); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_FLOOR, ASCIIToUTF16("2"), VerificationStatus::kParsed); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_PREMISE_NAME, ASCIIToUTF16("Premise"), VerificationStatus::kUserVerified); @@ -1108,6 +1113,12 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredAddresses) { EXPECT_EQ(db_profile->GetVerificationStatus(ADDRESS_HOME_SUBPREMISE), VerificationStatus::kUserVerified); + EXPECT_EQ(db_profile->GetVerificationStatus(ADDRESS_HOME_APT_NUM), + VerificationStatus::kParsed); + + EXPECT_EQ(db_profile->GetVerificationStatus(ADDRESS_HOME_FLOOR), + VerificationStatus::kParsed); + EXPECT_EQ(db_profile->GetVerificationStatus(ADDRESS_HOME_PREMISE_NAME), VerificationStatus::kUserVerified); @@ -1136,7 +1147,9 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredAddresses) { EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_HOUSE_NUMBER), ASCIIToUTF16("House Number")); EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_SUBPREMISE), - ASCIIToUTF16("Subpremise")); + ASCIIToUTF16("APT 10 Floor 2")); + EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_APT_NUM), ASCIIToUTF16("10")); + EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_FLOOR), ASCIIToUTF16("2")); EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_PREMISE_NAME), ASCIIToUTF16("Premise")); EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY), @@ -1287,8 +1300,9 @@ TEST_F(AutofillTableTest, // names. TEST_F(AutofillTableTest, AutofillProfile_StructuredNames) { // Enable the structured names. - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillEnableSupportForMoreStructureInNames); + scoped_feature_list_.InitWithFeatures( + {features::kAutofillEnableSupportForMoreStructureInNames}, + {features::kAutofillEnableSupportForMoreStructureInAddresses}); AutofillProfile home_profile; home_profile.set_origin(std::string()); @@ -1298,6 +1312,10 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredNames) { // NAME_HONORIFIC_PREFIX, ASCIIToUTF16("Dr."), // VerificationStatus::kObserved); + home_profile.SetRawInfoWithVerificationStatus(NAME_HONORIFIC_PREFIX, + ASCIIToUTF16("Dr."), + VerificationStatus::kObserved); + home_profile.SetRawInfoWithVerificationStatus( NAME_FIRST, ASCIIToUTF16("John"), VerificationStatus::kObserved); @@ -1320,6 +1338,11 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredNames) { NAME_FULL, ASCIIToUTF16("John Q. Agent 007 Smith"), VerificationStatus::kObserved); + home_profile.SetRawInfoWithVerificationStatus( + NAME_FULL_WITH_HONORIFIC_PREFIX, + ASCIIToUTF16("Dr. John Q. Agent 007 Smith"), + VerificationStatus::kObserved); + home_profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@smith.xyz")); home_profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google")); home_profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1234 Apple Way")); @@ -1462,8 +1485,9 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredNames) { TEST_F(AutofillTableTest, AutofillProfile) { // Disable the structured names since this test is only applicable if // structured names are not used. - scoped_feature_list_.InitAndDisableFeature( - features::kAutofillEnableSupportForMoreStructureInNames); + scoped_feature_list_.InitWithFeatures( + {}, {features::kAutofillEnableSupportForMoreStructureInAddresses, + features::kAutofillEnableSupportForMoreStructureInNames}); // Add a 'Home' profile with non-default data. The specific values are not // important. diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc index c89d012e159..746f529720b 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc @@ -23,10 +23,10 @@ #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_util.h" -#include "components/sync/model/entity_data.h" +#include "components/sync/engine/entity_data.h" +#include "components/sync/model/client_tag_based_model_type_processor.h" #include "components/sync/model/mutable_data_batch.h" -#include "components/sync/model_impl/client_tag_based_model_type_processor.h" -#include "components/sync/model_impl/sync_metadata_store_change_list.h" +#include "components/sync/model/sync_metadata_store_change_list.h" namespace autofill { @@ -330,7 +330,7 @@ AutofillWalletMetadataSyncBridge::AutofillWalletMetadataSyncBridge( : ModelTypeSyncBridge(std::move(change_processor)), web_data_backend_(web_data_backend) { DCHECK(web_data_backend_); - scoped_observer_.Add(web_data_backend_); + scoped_observation_.Observe(web_data_backend_); LoadDataCacheAndMetadata(); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h index 43d98a1eb62..e66390b84e1 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h @@ -10,7 +10,7 @@ #include <unordered_set> #include "base/macros.h" -#include "base/scoped_observer.h" +#include "base/scoped_observation.h" #include "base/sequence_checker.h" #include "base/supports_user_data.h" #include "components/autofill/core/browser/webdata/autofill_change.h" @@ -124,9 +124,9 @@ class AutofillWalletMetadataSyncBridge // SupportsUserData, so it's guaranteed to outlive |this|. AutofillWebDataBackend* const web_data_backend_; - ScopedObserver<AutofillWebDataBackend, - AutofillWebDataServiceObserverOnDBSequence> - scoped_observer_{this}; + base::ScopedObservation<AutofillWebDataBackend, + AutofillWebDataServiceObserverOnDBSequence> + scoped_observation_{this}; // Cache of the local data that allows figuring out the diff for local // changes; keyed by storage keys. diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc index f646c276c19..c0bc6688b43 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc @@ -30,9 +30,9 @@ #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/sync/base/client_tag_hash.h" +#include "components/sync/engine/entity_data.h" +#include "components/sync/model/client_tag_based_model_type_processor.h" #include "components/sync/model/data_batch.h" -#include "components/sync/model/entity_data.h" -#include "components/sync/model_impl/client_tag_based_model_type_processor.h" #include "components/sync/protocol/sync.pb.h" #include "components/sync/test/model/mock_model_type_change_processor.h" #include "components/webdata/common/web_database.h" diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc index bc4c93b79ba..36a70eb8898 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc @@ -15,9 +15,9 @@ #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/sync/base/hash_util.h" +#include "components/sync/model/client_tag_based_model_type_processor.h" #include "components/sync/model/mutable_data_batch.h" -#include "components/sync/model_impl/client_tag_based_model_type_processor.h" -#include "components/sync/model_impl/sync_metadata_store_change_list.h" +#include "components/sync/model/sync_metadata_store_change_list.h" namespace autofill { diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc index 5658421d159..c68ffc5acf1 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc @@ -25,10 +25,10 @@ #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/sync/base/hash_util.h" -#include "components/sync/model/entity_data.h" +#include "components/sync/engine/entity_data.h" +#include "components/sync/model/client_tag_based_model_type_processor.h" +#include "components/sync/model/in_memory_metadata_change_list.h" #include "components/sync/model/sync_data.h" -#include "components/sync/model_impl/client_tag_based_model_type_processor.h" -#include "components/sync/model_impl/in_memory_metadata_change_list.h" #include "components/sync/protocol/autofill_specifics.pb.h" #include "components/sync/protocol/sync.pb.h" #include "components/sync/test/model/mock_model_type_change_processor.h" diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc index 47f257894e1..04421a3e523 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc @@ -25,10 +25,10 @@ #include "components/sync/base/data_type_histogram.h" #include "components/sync/base/hash_util.h" #include "components/sync/driver/sync_driver_switches.h" -#include "components/sync/model/entity_data.h" +#include "components/sync/engine/entity_data.h" +#include "components/sync/model/client_tag_based_model_type_processor.h" #include "components/sync/model/mutable_data_batch.h" -#include "components/sync/model_impl/client_tag_based_model_type_processor.h" -#include "components/sync/model_impl/sync_metadata_store_change_list.h" +#include "components/sync/model/sync_metadata_store_change_list.h" using sync_pb::AutofillWalletSpecifics; using syncer::EntityData; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc index ce186b41372..aa7b8cc416e 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc @@ -34,14 +34,14 @@ #include "components/autofill/core/common/autofill_constants.h" #include "components/sync/base/client_tag_hash.h" #include "components/sync/driver/sync_driver_switches.h" -#include "components/sync/model/entity_data.h" +#include "components/sync/engine/entity_data.h" +#include "components/sync/model/client_tag_based_model_type_processor.h" +#include "components/sync/model/in_memory_metadata_change_list.h" #include "components/sync/model/sync_data.h" -#include "components/sync/model_impl/client_tag_based_model_type_processor.h" -#include "components/sync/model_impl/in_memory_metadata_change_list.h" #include "components/sync/protocol/autofill_specifics.pb.h" #include "components/sync/protocol/sync.pb.h" #include "components/sync/test/model/mock_model_type_change_processor.h" -#include "components/sync/test/test_matchers.h" +#include "components/sync/test/model/test_matchers.h" #include "components/webdata/common/web_database.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/components/autofill/core/common/BUILD.gn b/chromium/components/autofill/core/common/BUILD.gn index 948b48e1485..319e579b989 100644 --- a/chromium/components/autofill/core/common/BUILD.gn +++ b/chromium/components/autofill/core/common/BUILD.gn @@ -41,6 +41,7 @@ static_library("common") { "form_field_data_predictions.h", "gaia_id_hash.cc", "gaia_id_hash.h", + "language_code.h", "logging/log_buffer.cc", "logging/log_buffer.h", "password_form_fill_data.cc", @@ -58,6 +59,7 @@ static_library("common") { deps = [ "//base", "//base:i18n", + "//build:chromeos_buildflags", # Note: Can't use mojom:mojo_types here, as that already depends on :common. "//components/autofill/core/common/mojom:mojo_types_shared", diff --git a/chromium/components/autofill/core/common/autofill_constants.h b/chromium/components/autofill/core/common/autofill_constants.h index 07428810cc3..6e1efcc22c7 100644 --- a/chromium/components/autofill/core/common/autofill_constants.h +++ b/chromium/components/autofill/core/common/autofill_constants.h @@ -68,12 +68,14 @@ const int64_t kAutocompleteRetentionPolicyPeriodInDays = 14 * 31; // Limits the number of times the value of a specific type can be filled into a // form. -constexpr int kTypeValueFormFillingLimit = 9; - // Credit card numbers are sometimes distributed between up to 19 individual -// fields. Therefore, credit cards need a higher limit compared to -// |kTypeValueFormFillingLimit|. -constexpr int kCreditCardTypeValueFormFillingLimit = 19; +// fields. Therefore, credit cards need a higher limit. +// State fields are effecectively unlimited because there are sometimes hidden +// fields select boxes, each with a list of states for one specific countries, +// which are displayed only upon country selection. +constexpr size_t kTypeValueFormFillingLimit = 9; +constexpr size_t kCreditCardTypeValueFormFillingLimit = 19; +constexpr size_t kStateTypeValueFormFillingLimit = 1000; } // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_data_validation.cc b/chromium/components/autofill/core/common/autofill_data_validation.cc index 67010537037..9fb6165979d 100644 --- a/chromium/components/autofill/core/common/autofill_data_validation.cc +++ b/chromium/components/autofill/core/common/autofill_data_validation.cc @@ -4,6 +4,7 @@ #include "components/autofill/core/common/autofill_data_validation.h" +#include "base/ranges/algorithm.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/password_form_fill_data.h" @@ -38,60 +39,29 @@ bool IsValidFormFieldData(const FormFieldData& field) { } bool IsValidFormData(const FormData& form) { - if (!IsValidString16(form.name) || !IsValidGURL(form.url) || - !IsValidGURL(form.action)) - return false; - - if (form.fields.size() > kMaxListSize) - return false; - - for (const FormFieldData& field : form.fields) { - if (!IsValidFormFieldData(field)) - return false; - } - - return true; + return IsValidString16(form.name) && IsValidGURL(form.url) && + IsValidGURL(form.action) && form.fields.size() <= kMaxListSize && + base::ranges::all_of(form.fields, &IsValidFormFieldData); } bool IsValidPasswordFormFillData(const PasswordFormFillData& form) { - if (!IsValidString16(form.name) || !IsValidGURL(form.url) || - !IsValidGURL(form.action) || !IsValidFormFieldData(form.username_field) || - !IsValidFormFieldData(form.password_field) || - !IsValidString(form.preferred_realm)) { - return false; - } - - for (const auto& it : form.additional_logins) { - if (!IsValidString16(it.username) || !IsValidString16(it.password) || - !IsValidString(it.realm)) - return false; - } - - return true; + return IsValidString16(form.name) && IsValidGURL(form.url) && + IsValidGURL(form.action) && + IsValidFormFieldData(form.username_field) && + IsValidFormFieldData(form.password_field) && + IsValidString(form.preferred_realm) && + base::ranges::all_of(form.additional_logins, [](const auto& login) { + return IsValidString16(login.username) && + IsValidString16(login.password) && IsValidString(login.realm); + }); } bool IsValidString16Vector(const std::vector<base::string16>& v) { - if (v.size() > kMaxListSize) - return false; - - for (const base::string16& str : v) { - if (!IsValidString16(str)) - return false; - } - - return true; + return v.size() <= kMaxListSize && base::ranges::all_of(v, &IsValidString16); } bool IsValidFormDataVector(const std::vector<FormData>& v) { - if (v.size() > kMaxListSize) - return false; - - for (const FormData& form : v) { - if (!IsValidFormData(form)) - return false; - } - - return true; + return v.size() <= kMaxListSize && base::ranges::all_of(v, &IsValidFormData); } } // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_features.cc b/chromium/components/autofill/core/common/autofill_features.cc index 349361c1c7b..866f6a9274a 100644 --- a/chromium/components/autofill/core/common/autofill_features.cc +++ b/chromium/components/autofill/core/common/autofill_features.cc @@ -12,6 +12,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "build/chromeos_buildflags.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/prefs/pref_service.h" @@ -20,6 +21,12 @@ namespace autofill { namespace features { +// Controls if Autocomplete suggestions are only shown/stored for meaningful +// field names. +// TODO(crbug.com/1181759): Remove once launched. +const base::Feature kAutocompleteFilterForMeaningfulNames{ + "AutocompleteFilterForMeaningfulNames", base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls if Autofill sends votes for the new address types. const base::Feature kAutofillAddressEnhancementVotes{ "kAutofillAddressEnhancementVotes", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -45,16 +52,6 @@ const base::Feature kAutofillAllowDuplicateFormSubmissions{ const base::Feature kAutofillAllowNonHttpActivation{ "AutofillAllowNonHttpActivation", base::FEATURE_DISABLED_BY_DEFAULT}; -const base::Feature kAutofillAlwaysFillAddresses{ - "AlwaysFillAddresses", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Controls whether negative patterns are used to parse the field type. -// TODO(crbug.com/1132831): Remove once launched. -const base::Feature - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics{ - "AutofillApplyNegativePatternsForFieldTypeDetectionHeuristics", - base::FEATURE_DISABLED_BY_DEFAULT}; - // Controls the use of GET (instead of POST) to fetch cacheable autofill query // responses. const base::Feature kAutofillCacheQueryResponses{ @@ -63,12 +60,32 @@ const base::Feature kAutofillCacheQueryResponses{ const base::Feature kAutofillCreateDataForTest{ "AutofillCreateDataForTest", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls if the heuristic field parsing utilizes shared labels. +// TODO(crbug/1165780): Remove once shared labels are launched. +const base::Feature kAutofillEnableSupportForParsingWithSharedLabels{ + "AutofillEnableSupportForParsingWithSharedLabels", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// Kill switch for Autofill filling. +const base::Feature kAutofillDisableFilling{"AutofillDisableFilling", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// Kill switch for Autofill address import. +const base::Feature kAutofillDisableAddressImport{ + "AutofillDisableAddressImport", base::FEATURE_DISABLED_BY_DEFAULT}; + +// Controls if Chrome support filling and importing apartment numbers. +// TODO(crbug.com/1153715): Remove once launched. +const base::Feature kAutofillEnableSupportForApartmentNumbers{ + "AutofillEnableSupportForApartmentNumbers", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether we download server credit cards to the ephemeral // account-based storage when sync the transport is enabled. const base::Feature kAutofillEnableAccountWalletStorage { "AutofillEnableAccountWalletStorage", -#if defined(OS_CHROMEOS) || defined(OS_ANDROID) || defined(OS_IOS) - // Wallet transport is only currently available on Win/Mac/Linux. +#if BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_IOS) + // Wallet transport is only currently available on Win/Mac/Linux/Android. // (Somehow, swapping this check makes iOS unhappy?) base::FEATURE_DISABLED_BY_DEFAULT #else @@ -78,14 +95,28 @@ const base::Feature kAutofillEnableAccountWalletStorage { // Controls whether to detect and fill the augmented phone country code field // when enabled. +// TODO(crbug.com/1150890) Remove once launched const base::Feature kAutofillEnableAugmentedPhoneCountryCode{ "AutofillEnableAugmentedPhoneCountryCode", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls if Autofill parses ADDRESS_HOME_DEPENDENT_LOCALITY. +// TODO(crbug.com/1157405): Remove once launched. +const base::Feature kAutofillEnableDependentLocalityParsing{ + "AutofillEnableDependentLocalityParsing", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether we show "Hide suggestions" item in the suggestions menu. const base::Feature kAutofillEnableHideSuggestionsUI{ "AutofillEnableHideSuggestionsUI", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls whether to save the first number in a form with multiple phone +// numbers instead of aborting the import. +// TODO(crbug.com/1167484) Remove once launched +const base::Feature kAutofillEnableImportWhenMultiplePhoneNumbers{ + "AutofillEnableImportWhenMultiplePhoneNumbers", + base::FEATURE_DISABLED_BY_DEFAULT}; + // When enabled and user has single account, a footer indicating user's e-mail // address and profile picture will appear at the bottom of InfoBars which has // corresponding account indication footer flags on. @@ -101,6 +132,14 @@ const base::Feature kAutofillEnableInfoBarAccountIndicationFooterForSyncUsers{ "AutofillEnableInfoBarAccountIndicationFooterForSyncUsers", base::FEATURE_DISABLED_BY_DEFAULT}; +// When enabled, the precedence is given to the field label over the name when +// they match different types. Applied only for parsing of address forms in +// Turkish. +// TODO(crbug.com/1156315): Remove once launched. +const base::Feature kAutofillEnableLabelPrecedenceForTurkishAddresses{ + "AutofillEnableLabelPrecedenceForTurkishAddresses", + base::FEATURE_DISABLED_BY_DEFAULT}; + // When enabled and user is signed in, a footer indicating user's e-mail address // and profile picture will appear at the bottom of corresponding password // InfoBars. @@ -108,6 +147,11 @@ const base::Feature kAutofillEnablePasswordInfoBarAccountIndicationFooter{ "AutofillEnablePasswordInfoBarAccountIndicationFooter", base::FEATURE_DISABLED_BY_DEFAULT}; +// When enabled, the address profile deduplication logic runs after the browser +// startup, once per chrome version. +const base::Feature kAutofillEnableProfileDeduplication{ + "AutofillEnableProfileDeduplication", base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls if Autofill supports new structure in names. // TODO(crbug.com/1098943): Remove once launched. const base::Feature kAutofillEnableSupportForMoreStructureInNames{ @@ -129,9 +173,9 @@ const base::Feature kAutofillEnableSupportForMergingSubsetNames{ // Controls whether honorific prefix is shown and editable in Autofill Settings // on Android, iOS and Desktop. // TODO(crbug.com/1141460): Remove once launched. -const base::Feature kAutofillEnableUIForHonorificPrefixesInSettings{ - "AutofillEnableUIForHonorificPrefixesInSettings", - base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kAutofillEnableSupportForHonorificPrefixes{ + "AutofillEnableSupportForHonorificPrefixes", + base::FEATURE_DISABLED_BY_DEFAULT}; // Controls whether or not all datalist shall be extracted into FormFieldData. // This feature is enabled in both WebView and WebLayer where all datalists @@ -148,6 +192,12 @@ const base::Feature kAutofillExtractAllDatalists{ const base::Feature kAutofillFixFillableFieldTypes{ "AutofillFixFillableFieldTypes", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls if a server prediction with a prediction source |OVERRIDE| is +// granted precedence over html type attributes. +// TODO(crbug.com/1170384) Remove once launched +const base::Feature kAutofillServerTypeTakesPrecedence{ + "AutofillServerTypeTakesPrecedence", base::FEATURE_DISABLED_BY_DEFAULT}; + // When enabled, Autofill will use FormRendererIds instead of // GetIdentifierForRefill() to identify forms during refills. // TODO(crbug/896689): Remove once experiment is finished. @@ -160,12 +210,12 @@ const base::Feature kAutofillRefillWithRendererIds{ const base::Feature kAutofillNameSectionsWithRendererIds{ "AutofillNameSectionsWithRendererIds", base::FEATURE_DISABLED_BY_DEFAULT}; -// When enabled, autofill suggestions are displayed in the keyboard accessory +// When enabled, Autofill suggestions are displayed in the keyboard accessory // instead of the regular popup. const base::Feature kAutofillKeyboardAccessory{ "AutofillKeyboardAccessory", base::FEATURE_DISABLED_BY_DEFAULT}; -// When enabled, autofill will use new logic to strip both prefixes +// When enabled, Autofill will use new logic to strip both prefixes // and suffixes when setting FormStructure::parseable_name_ extern const base::Feature kAutofillLabelAffixRemoval{ "AutofillLabelAffixRemoval", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -176,13 +226,33 @@ const base::Feature kAutofillPruneSuggestions{ const base::Feature kAutofillMetadataUploads{"AutofillMetadataUploads", base::FEATURE_DISABLED_BY_DEFAULT}; -const base::Feature kAutofillOffNoServerData{"AutofillOffNoServerData", - base::FEATURE_DISABLED_BY_DEFAULT}; +// When enabled, Autofill will load remote patterns via the component updater. +// TODO(crbug/1121990): Remove once launched. +extern const base::Feature kAutofillParsingPatternsFromRemote{ + "AutofillParsingPatternsFromRemote", base::FEATURE_DISABLED_BY_DEFAULT}; + +// Enables detection of language from Translate. +// TODO(crbug/1150895): Cleanup when launched. +const base::Feature kAutofillParsingPatternsLanguageDetection{ + "AutofillParsingPatternsLanguageDetection", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// Controls whether negative patterns are used to parse the field type. +// TODO(crbug.com/1132831): Remove once launched. +const base::Feature kAutofillParsingPatternsNegativeMatching{ + "AutofillParsingPatternsNegativeMatching", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// Controls whether page language is used to match patterns. +// TODO(crbug.com/1134496): Remove once launched. +const base::Feature kAutofillParsingPatternsLanguageDependent{ + "AutofillParsingPatternsLanguageDependent", + base::FEATURE_DISABLED_BY_DEFAULT}; -// If feature is enabled, autofill will be disabled for mixed forms (forms on +// If feature is enabled, Autofill will be disabled for mixed forms (forms on // HTTPS sites that submit over HTTP). const base::Feature kAutofillPreventMixedFormsFilling{ - "AutofillPreventMixedFormsFilling", base::FEATURE_DISABLED_BY_DEFAULT}; + "AutofillPreventMixedFormsFilling", base::FEATURE_ENABLED_BY_DEFAULT}; // If the feature is enabled, FormTracker's probable-form-submission detection // is disabled and replaced with browser-side detection. @@ -194,6 +264,11 @@ const base::Feature kAutofillProbableFormSubmissionInBrowser{ const base::Feature kAutofillProfileClientValidation{ "AutofillProfileClientValidation", base::FEATURE_DISABLED_BY_DEFAULT}; +// TODO(crbug.com/1101280): Remove once feature is tested. +const base::Feature kAutofillProfileImportFromUnfocusableFields{ + "AutofillProfileImportFromUnfocusableFields", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether Autofill uses server-side validation to ensure that fields // with invalid data are not suggested. const base::Feature kAutofillProfileServerValidation{ @@ -206,6 +281,11 @@ const base::Feature kAutofillRestrictUnownedFieldsToFormlessCheckout{ "AutofillRestrictUnownedFieldsToFormlessCheckout", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls whether or not overall prediction are retrieved from the cache. +const base::Feature kAutofillRetrieveOverallPredictionsFromCache{ + "AutofillRetrieveOverallPredictionsFromCache", + base::FEATURE_DISABLED_BY_DEFAULT}; + // On Canary and Dev channels only, this feature flag instructs chrome to send // rich form/field metadata with queries. This will trigger the use of richer // field-type predictions model on the server, for testing/evaluation of those @@ -241,10 +321,11 @@ const base::Feature kAutofillShowTypePredictions{ const base::Feature kAutofillSkipComparingInferredLabels{ "AutofillSkipComparingInferredLabels", base::FEATURE_DISABLED_BY_DEFAULT}; -// Controls whether to skip fields whose last seen value differs from the -// initially value. -const base::Feature kAutofillSkipFillingFieldsWithChangedValues{ - "AutofillSkipFillingFieldsWithChangedValues", +// Controls whether we require an expiration date or verification field when a +// name field is detected for a credit card, but we aren't confident it's not +// a non-credit card specific name field. +const base::Feature kAutofillStrictContextualCardNameConditions{ + "AutofillStrictContextualCardNameConditions", base::FEATURE_DISABLED_BY_DEFAULT}; // Controls whether Autofill should search prefixes of all words/tokens when @@ -274,12 +355,6 @@ const base::Feature kAutofillUseImprovedLabelDisambiguation{ const base::Feature kAutofillUseNewSectioningMethod{ "AutofillUseNewSectioningMethod", base::FEATURE_DISABLED_BY_DEFAULT}; -// Controls whether page language is used to match patterns. -// TODO(crbug.com/1134496): Remove once launched. -const base::Feature kAutofillUsePageLanguageToSelectFieldParsingPatterns{ - "AutofillUsePageLanguageToSelectFieldParsingPatterns", - base::FEATURE_DISABLED_BY_DEFAULT}; - #if defined(OS_ANDROID) // Controls whether the Autofill manual fallback for Addresses and Payments is // present on Android. @@ -306,9 +381,22 @@ const char kAutofillUseMobileLabelDisambiguationParameterShowOne[] = "show-one"; // TODO(crbug/1131038): Remove once it's launched. const base::Feature kAutofillUseUniqueRendererIDsOnIOS{ "AutofillUseUniqueRendererIDsOnIOS", base::FEATURE_DISABLED_BY_DEFAULT}; + +// Controls whether the creation of new address profiles is enabled in settings +// on IOS. +// TODO(crbug/1167105): Remove once it's launched. +const base::Feature kAutofillEnableNewAddressProfileCreationInSettingsOnIOS{ + "AutofillEnableNewAddressProfileCreationInSettingsOnIOS", + base::FEATURE_DISABLED_BY_DEFAULT}; #endif #if defined(OS_ANDROID) +// Controls whether Android autofill (WebView and WebLayer) should query the +// Autofill server for the server field type predictions and send them to +// Android autofill service. +const base::Feature kAndroidAutofillQueryServerFieldTypes{ + "AndroidAutofillQueryServerFieldTypes", base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether the Wallet (GPay) integration requires first-sync-setup to // be complete. // TODO(crbug.com/1134564): Clean up after launch. diff --git a/chromium/components/autofill/core/common/autofill_features.h b/chromium/components/autofill/core/common/autofill_features.h index 8209085714b..934b73830d6 100644 --- a/chromium/components/autofill/core/common/autofill_features.h +++ b/chromium/components/autofill/core/common/autofill_features.h @@ -21,55 +21,66 @@ namespace autofill { namespace features { // All features in alphabetical order. +extern const base::Feature kAutocompleteFilterForMeaningfulNames; extern const base::Feature kAutofillAddressEnhancementVotes; extern const base::Feature kAutofillAddressProfileSavePrompt; extern const base::Feature kAutofillAllowDuplicateFormSubmissions; extern const base::Feature kAutofillAllowNonHttpActivation; -extern const base::Feature kAutofillAlwaysFillAddresses; -extern const base::Feature - kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics; extern const base::Feature kAutofillCacheQueryResponses; extern const base::Feature kAutofillCreateDataForTest; +extern const base::Feature kAutofillDisableFilling; +extern const base::Feature kAutofillDisableAddressImport; extern const base::Feature kAutofillEnableAccountWalletStorage; extern const base::Feature kAutofillEnableAugmentedPhoneCountryCode; +extern const base::Feature kAutofillEnableDependentLocalityParsing; extern const base::Feature kAutofillEnableHideSuggestionsUI; +extern const base::Feature kAutofillEnableImportWhenMultiplePhoneNumbers; extern const base::Feature kAutofillEnableInfoBarAccountIndicationFooterForSingleAccountUsers; extern const base::Feature kAutofillEnableInfoBarAccountIndicationFooterForSyncUsers; extern const base::Feature kAutofillEnablePasswordInfoBarAccountIndicationFooter; +extern const base::Feature kAutofillEnableSupportForApartmentNumbers; +extern const base::Feature kAutofillEnableLabelPrecedenceForTurkishAddresses; +extern const base::Feature kAutofillEnableProfileDeduplication; +extern const base::Feature kAutofillEnableSupportForParsingWithSharedLabels; extern const base::Feature kAutofillEnableSupportForMoreStructureInNames; extern const base::Feature kAutofillEnableSupportForMoreStructureInAddresses; extern const base::Feature kAutofillEnableSupportForMergingSubsetNames; -extern const base::Feature kAutofillEnableUIForHonorificPrefixesInSettings; +extern const base::Feature kAutofillEnableSupportForHonorificPrefixes; extern const base::Feature kAutofillExtractAllDatalists; extern const base::Feature kAutofillFixFillableFieldTypes; +extern const base::Feature kAutofillServerTypeTakesPrecedence; extern const base::Feature kAutofillRefillWithRendererIds; extern const base::Feature kAutofillNameSectionsWithRendererIds; extern const base::Feature kAutofillKeyboardAccessory; extern const base::Feature kAutofillLabelAffixRemoval; extern const base::Feature kAutofillPruneSuggestions; extern const base::Feature kAutofillMetadataUploads; -extern const base::Feature kAutofillOffNoServerData; +extern const base::Feature kAutofillParsingPatternsFromRemote; +extern const base::Feature kAutofillParsingPatternsLanguageDetection; +extern const base::Feature kAutofillParsingPatternsNegativeMatching; +extern const base::Feature kAutofillParsingPatternsLanguageDependent; extern const base::Feature kAutofillPreventMixedFormsFilling; extern const base::Feature kAutofillProbableFormSubmissionInBrowser; extern const base::Feature kAutofillProfileClientValidation; +extern const base::Feature kAutofillProfileImportFromUnfocusableFields; extern const base::Feature kAutofillProfileServerValidation; extern const base::Feature kAutofillRestrictUnownedFieldsToFormlessCheckout; +extern const base::Feature kAutofillRetrieveOverallPredictionsFromCache; extern const base::Feature kAutofillRichMetadataQueries; extern const base::Feature kAutofillSaveAndFillVPA; extern const base::Feature kAutofillSectionUponRedundantNameInfo; extern const base::Feature kAutofillServerCommunication; extern const base::Feature kAutofillShowTypePredictions; extern const base::Feature kAutofillSkipComparingInferredLabels; -extern const base::Feature kAutofillSkipFillingFieldsWithChangedValues; +extern const base::Feature kAutofillStrictContextualCardNameConditions; extern const base::Feature kAutofillTokenPrefixMatching; extern const base::Feature kAutofillUploadThrottling; extern const base::Feature kAutofillUseAlternativeStateNameMap; extern const base::Feature kAutofillUseImprovedLabelDisambiguation; extern const base::Feature kAutofillUseNewSectioningMethod; -extern const base::Feature kAutofillUsePageLanguageToSelectFieldParsingPatterns; #if defined(OS_ANDROID) extern const base::Feature kAutofillManualFallbackAndroid; @@ -91,9 +102,12 @@ bool IsMacViewsAutofillPopupExperimentEnabled(); #if defined(OS_IOS) extern const base::Feature kAutofillUseUniqueRendererIDsOnIOS; +extern const base::Feature + kAutofillEnableNewAddressProfileCreationInSettingsOnIOS; #endif // OS_IOS #if defined(OS_ANDROID) +extern const base::Feature kAndroidAutofillQueryServerFieldTypes; extern const base::Feature kWalletRequiresFirstSyncSetupComplete; #endif diff --git a/chromium/components/autofill/core/common/autofill_payments_features.cc b/chromium/components/autofill/core/common/autofill_payments_features.cc index 8d74f541c6c..1bcfa67eb48 100644 --- a/chromium/components/autofill/core/common/autofill_payments_features.cc +++ b/chromium/components/autofill/core/common/autofill_payments_features.cc @@ -12,6 +12,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "build/chromeos_buildflags.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/prefs/pref_service.h" @@ -28,11 +29,6 @@ const base::Feature kAutofillAlwaysReturnCloudTokenizedCard{ "AutofillAlwaysReturnCloudTokenizedCard", base::FEATURE_DISABLED_BY_DEFAULT}; -// If enabled, when a server card is unmasked, its info will be cached until -// page navigation to simplify consecutive fills on the same page. -const base::Feature kAutofillCacheServerCardInfo{ - "AutofillCacheServerCardInfo", base::FEATURE_ENABLED_BY_DEFAULT}; - const base::Feature kAutofillCreditCardAblationExperiment{ "AutofillCreditCardAblationExperiment", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -40,8 +36,8 @@ const base::Feature kAutofillCreditCardAblationExperiment{ // credit cards from Google payments. const base::Feature kAutofillCreditCardAuthentication{ "AutofillCreditCardAuthentication", -#if defined(OS_WIN) || defined(OS_MAC) - // Better Auth project is fully launched on Win/Mac. +#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_ANDROID) + // Better Auth project is fully launched on Win/Mac/Clank. base::FEATURE_ENABLED_BY_DEFAULT #else base::FEATURE_DISABLED_BY_DEFAULT @@ -54,31 +50,26 @@ const base::Feature kAutofillCreditCardAuthentication{ const base::Feature kAutofillCreditCardUploadFeedback{ "AutofillCreditCardUploadFeedback", base::FEATURE_DISABLED_BY_DEFAULT}; -// When enabled, the credit card nicknames will be manageable. They can be -// modified locally. -const base::Feature kAutofillEnableCardNicknameManagement{ - "AutofillEnableCardNicknameManagement", base::FEATURE_DISABLED_BY_DEFAULT}; - // When enabled, shows the Google Pay logo on CVC prompt on Android. const base::Feature kAutofillDownstreamCvcPromptUseGooglePayLogo{ "AutofillDownstreamCvcPromptUseGooglePayLogo", base::FEATURE_DISABLED_BY_DEFAULT}; -// When enabled, the credit card nicknames will be manageable. They can be -// uploaded to Payments. -const base::Feature kAutofillEnableCardNicknameUpstream{ - "AutofillEnableCardNicknameUpstream", base::FEATURE_DISABLED_BY_DEFAULT}; - -// When enabled, autofill payments bubbles' result will be recorded as either -// 'accepted', 'cancelled', 'closed', 'not interacted' or 'lost focus'. -const base::Feature kAutofillEnableFixedPaymentsBubbleLogging{ - "AutofillEnableFixedPaymentsBubbleLogging", - base::FEATURE_DISABLED_BY_DEFAULT}; - // Controls whether we show a Google-issued card in the suggestions list. const base::Feature kAutofillEnableGoogleIssuedCard{ "AutofillEnableGoogleIssuedCard", base::FEATURE_DISABLED_BY_DEFAULT}; +// When enabled, a notification will be displayed on page navigation if the +// domain has an eligible credit card linked offer or reward. +const base::Feature kAutofillEnableOfferNotification{ + "AutofillEnableOfferNotification", base::FEATURE_DISABLED_BY_DEFAULT}; + +// When enabled, offers will be displayed in the Clank keyboard accessory during +// downstream. +const base::Feature kAutofillEnableOffersInClankKeyboardAccessory{ + "AutofillEnableOffersInClankKeyboardAccessory", + base::FEATURE_DISABLED_BY_DEFAULT}; + // When enabled, offer data will be retrieved during downstream and shown in // the dropdown list. const base::Feature kAutofillEnableOffersInDownstream{ @@ -90,11 +81,6 @@ const base::Feature kAutofillEnableSaveCardInfoBarAccountIndicationFooter{ "AutofillEnableSaveCardInfoBarAccountIndicationFooter", base::FEATURE_DISABLED_BY_DEFAULT}; -// When enabled, all payments related bubbles will not be dismissed upon page -// navigation. -const base::Feature kAutofillEnableStickyPaymentsBubble{ - "AutofillEnableStickyPaymentsBubble", base::FEATURE_DISABLED_BY_DEFAULT}; - // When enabled, Autofill data related icons will be shown in the status // chip in toolbar along with the avatar toolbar button. const base::Feature kAutofillEnableToolbarStatusChip{ @@ -114,6 +100,11 @@ const base::Feature kAutofillSaveCardDismissOnNavigation{ const base::Feature kAutofillSaveCardInfobarEditSupport{ "AutofillSaveCardInfobarEditSupport", base::FEATURE_ENABLED_BY_DEFAULT}; +// When enabled, suggestions with offers will be shown at the top. +const base::Feature kAutofillSortSuggestionsBasedOnOfferPresence{ + "AutofillSortSuggestionsBasedOnOfferPresence", + base::FEATURE_ENABLED_BY_DEFAULT}; + // Controls offering credit card upload to Google Payments. Cannot ever be // ENABLED_BY_DEFAULT because the feature state depends on the user's country. // There are countries we simply can't turn this on for, and they change over @@ -128,8 +119,10 @@ const base::Feature kAutofillUpstreamAllowAllEmailDomains{ "AutofillUpstreamAllowAllEmailDomains", base::FEATURE_DISABLED_BY_DEFAULT}; bool ShouldShowImprovedUserConsentForCreditCardSave() { +// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch +// of lacros-chrome is complete. #if defined(OS_WIN) || defined(OS_APPLE) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) + (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) // The new user consent UI is fully launched on MacOS, Windows and Linux. return true; #else diff --git a/chromium/components/autofill/core/common/autofill_payments_features.h b/chromium/components/autofill/core/common/autofill_payments_features.h index 4cadf5421ad..82c39b64954 100644 --- a/chromium/components/autofill/core/common/autofill_payments_features.h +++ b/chromium/components/autofill/core/common/autofill_payments_features.h @@ -20,23 +20,21 @@ namespace features { // All features in alphabetical order. extern const base::Feature kAutofillAlwaysReturnCloudTokenizedCard; -extern const base::Feature kAutofillCacheServerCardInfo; extern const base::Feature kAutofillCreditCardAblationExperiment; extern const base::Feature kAutofillCreditCardAuthentication; extern const base::Feature kAutofillCreditCardUploadFeedback; extern const base::Feature kAutofillDownstreamCvcPromptUseGooglePayLogo; -extern const base::Feature kAutofillEnableCardNicknameManagement; -extern const base::Feature kAutofillEnableCardNicknameUpstream; -extern const base::Feature kAutofillEnableFixedPaymentsBubbleLogging; extern const base::Feature kAutofillEnableGoogleIssuedCard; +extern const base::Feature kAutofillEnableOfferNotification; +extern const base::Feature kAutofillEnableOffersInClankKeyboardAccessory; extern const base::Feature kAutofillEnableOffersInDownstream; extern const base::Feature kAutofillEnableSaveCardInfoBarAccountIndicationFooter; -extern const base::Feature kAutofillEnableStickyPaymentsBubble; extern const base::Feature kAutofillEnableToolbarStatusChip; extern const base::Feature kAutofillEnableVirtualCard; extern const base::Feature kAutofillSaveCardDismissOnNavigation; extern const base::Feature kAutofillSaveCardInfobarEditSupport; +extern const base::Feature kAutofillSortSuggestionsBasedOnOfferPresence; extern const base::Feature kAutofillUpstream; extern const base::Feature kAutofillUpstreamAllowAllEmailDomains; diff --git a/chromium/components/autofill/core/common/autofill_util.cc b/chromium/components/autofill/core/common/autofill_util.cc index d9a17f403a6..807dd4458cb 100644 --- a/chromium/components/autofill/core/common/autofill_util.cc +++ b/chromium/components/autofill/core/common/autofill_util.cc @@ -126,10 +126,6 @@ bool IsDesktopPlatform() { #endif } -bool ShouldSkipField(const FormFieldData& field) { - return IsCheckable(field.check_status); -} - bool IsCheckable(const FormFieldData::CheckStatus& check_status) { return check_status != FormFieldData::CheckStatus::kNotCheckable; } diff --git a/chromium/components/autofill/core/common/autofill_util.h b/chromium/components/autofill/core/common/autofill_util.h index 240c7a31090..f2e0f271c0b 100644 --- a/chromium/components/autofill/core/common/autofill_util.h +++ b/chromium/components/autofill/core/common/autofill_util.h @@ -67,8 +67,6 @@ size_t GetTextSelectionStart(const base::string16& suggestion, // Android or iOS is considered desktop. bool IsDesktopPlatform(); -bool ShouldSkipField(const FormFieldData& field); - bool IsCheckable(const FormFieldData::CheckStatus& check_status); bool IsChecked(const FormFieldData::CheckStatus& check_status); void SetCheckStatus(FormFieldData* form_field_data, diff --git a/chromium/components/autofill/core/common/dense_set.h b/chromium/components/autofill/core/common/dense_set.h index cd52b04a025..3f4897d74dc 100644 --- a/chromium/components/autofill/core/common/dense_set.h +++ b/chromium/components/autofill/core/common/dense_set.h @@ -23,18 +23,18 @@ namespace autofill { // representation. // // The lower and upper bounds of elements storable in a container are -// [T(0), kEnd). +// [T(0), kMaxValue]. By default, kMaxValue is T::kMaxValue. // // Internally, the set is represented as a std::bitset. // // Time and space complexity depend on std::bitset: // - insert(), erase(), contains() should run in time O(1) -// - empty(), size(), iteration should run in time O(kEnd) -// - sizeof(DenseSet) should be ceil(kEnd / 8) bytes. +// - empty(), size(), iteration should run in time O(kMaxValue) +// - sizeof(DenseSet) should be ceil(kMaxValue / 8) bytes. // // Iterators are invalidated when the owning container is destructed or moved, // or when the element the iterator points to is erased from the container. -template <typename T, T kEnd> +template <typename T, T kMaxValue = T::kMaxValue> class DenseSet { private: using Index = std::make_unsigned_t<T>; @@ -194,6 +194,9 @@ class DenseSet { return {find(x), !contained}; } + // Inserts all values of |xs| into the present set. + void insert_all(const DenseSet& xs) { bitset_ |= xs.bitset_; } + // Erases the element whose index matches the index of |x| and returns the // number of erased elements (0 or 1). size_t erase(T x) { @@ -220,6 +223,9 @@ class DenseSet { return last; } + // Erases all values of |xs| into the present set. + void erase_all(const DenseSet& xs) { bitset_ &= ~xs.bitset_; } + // Lookup. // Returns 1 if |x| is an element, otherwise 0. @@ -233,6 +239,21 @@ class DenseSet { // Returns true if |x| is an element, else |false|. bool contains(T x) const { return bitset_.test(value_to_index(x)); } + // Returns true if some element of |xs| is an element, else |false|. + bool contains_none(const DenseSet& xs) const { + return (bitset_ & xs.bitset_).none(); + } + + // Returns true if some element of |xs| is an element, else |false|. + bool contains_any(const DenseSet& xs) const { + return (bitset_ & xs.bitset_).any(); + } + + // Returns true if every elements of |xs| is an element, else |false|. + bool contains_all(const DenseSet& xs) const { + return (bitset_ & xs.bitset_) == xs.bitset_; + } + // Returns an iterator to the first element not less than the |x|, or end(). const_iterator lower_bound(T x) const { const_iterator it(this, value_to_index(x)); @@ -255,12 +276,12 @@ class DenseSet { }; static constexpr Index value_to_index(T x) { - DCHECK(index_to_value(0) <= x && x < kEnd); + DCHECK(index_to_value(0) <= x && x <= kMaxValue); return base::checked_cast<Index>(x); } static constexpr T index_to_value(Index i) { - DCHECK_LT(i, base::checked_cast<Index>(kEnd)); + DCHECK_LE(i, base::checked_cast<Index>(kMaxValue)); using UnderlyingType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>, Wrapper>::type; @@ -268,9 +289,9 @@ class DenseSet { } static_assert(std::is_integral<T>::value || std::is_enum<T>::value, ""); - static_assert(index_to_value(0) <= kEnd, ""); + static_assert(0 <= base::checked_cast<Index>(kMaxValue) + 1, ""); - std::bitset<base::checked_cast<Index>(kEnd)> bitset_{}; + std::bitset<base::checked_cast<Index>(kMaxValue) + 1> bitset_{}; }; } // namespace autofill diff --git a/chromium/components/autofill/core/common/dense_set_unittest.cc b/chromium/components/autofill/core/common/dense_set_unittest.cc index c6df3972962..d99bc000495 100644 --- a/chromium/components/autofill/core/common/dense_set_unittest.cc +++ b/chromium/components/autofill/core/common/dense_set_unittest.cc @@ -19,9 +19,9 @@ TEST(DenseSet, initialization) { Three = 3, Four = 4, Five = 5, - kEnd = 6 + kMaxValue = Five, }; - using DS = DenseSet<T, T::kEnd>; + using DS = DenseSet<T>; DS s; EXPECT_TRUE(s.empty()); @@ -45,9 +45,9 @@ TEST(DenseSet, iterators_begin_end) { Three = 3, Four = 4, Five = 5, - kEnd = 6 + kMaxValue = Five, }; - using DS = DenseSet<T, T::kEnd>; + using DS = DenseSet<T, T::kMaxValue>; DS s; s.insert(T::Two); @@ -88,9 +88,9 @@ TEST(DenseSet, iterators_begin_end_reverse) { Three = 3, Four = 4, Five = 5, - kEnd = 6 + kMaxValue = Five, }; - using DS = DenseSet<T, T::kEnd>; + using DS = DenseSet<T>; DS s; s.insert(T::Two); @@ -123,8 +123,15 @@ TEST(DenseSet, iterators_begin_end_reverse) { } TEST(DenseSet, iterators_rbegin_rend) { - enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5, kEnd = 6 }; - using DS = DenseSet<T, T::kEnd>; + enum class T { + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, + kMaxValue = Five + }; + using DS = DenseSet<T, T::kMaxValue>; DS s; s.insert(T::Two); @@ -160,8 +167,15 @@ TEST(DenseSet, iterators_rbegin_rend) { } TEST(DenseSet, lookup) { - enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5, kEnd = 6 }; - using DS = DenseSet<T, T::kEnd>; + enum class T { + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, + kMaxValue = Five + }; + using DS = DenseSet<T, T::kMaxValue>; DS s; s.insert(T::Two); @@ -199,11 +213,37 @@ TEST(DenseSet, lookup) { EXPECT_NE(s.find(T::Three), s.lower_bound(T::Three)); EXPECT_EQ(s.find(T::Four), s.lower_bound(T::Four)); EXPECT_EQ(s.find(T::Five), s.lower_bound(T::Five)); + + DS t; + EXPECT_TRUE(t.empty()); + EXPECT_TRUE(t.contains_none({})); + EXPECT_FALSE(t.contains_any({})); + EXPECT_TRUE(t.contains_all({})); + + t.insert_all(s); + EXPECT_EQ(s, t); + EXPECT_FALSE(s.contains_none(t)); + EXPECT_TRUE(s.contains_any(t)); + EXPECT_TRUE(s.contains_all(t)); + EXPECT_TRUE(s.contains_none({})); + EXPECT_FALSE(s.contains_any({})); + EXPECT_TRUE(s.contains_all({})); + + t.erase(t.begin()); + EXPECT_FALSE(s.contains_none(t)); + EXPECT_TRUE(s.contains_any(t)); + EXPECT_TRUE(s.contains_all(t)); + EXPECT_FALSE(t.contains_none(s)); + EXPECT_FALSE(t.contains_all(s)); + EXPECT_TRUE(t.contains_any(s)); + EXPECT_TRUE(s.contains_none({})); + EXPECT_FALSE(s.contains_any({})); + EXPECT_TRUE(s.contains_all({})); } TEST(DenseSet, iterators_lower_upper_bound) { - enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5, kEnd = 6 }; - using DS = DenseSet<T, T::kEnd>; + enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5 }; + using DS = DenseSet<T, T::Five>; DS s; s.insert(T::Two); @@ -276,8 +316,8 @@ TEST(DenseSet, max_size) { // const int Three = 3; const int Four = 4; // const int Five = 5; - const int kEnd = 6; - using DS = DenseSet<int, kEnd>; + const int kMaxValue = 5; + using DS = DenseSet<int, kMaxValue>; DS s; EXPECT_TRUE(s.empty()); @@ -301,8 +341,8 @@ TEST(DenseSet, modifiers) { const size_t Three = 3; const size_t Four = 4; // const size_t Five = 5; - const size_t kEnd = 6; - using DS = DenseSet<size_t, kEnd>; + const size_t kMaxValue = 5; + using DS = DenseSet<size_t, kMaxValue>; DS s; s.insert(Two); @@ -375,7 +415,35 @@ TEST(DenseSet, modifiers) { s.clear(); EXPECT_EQ(s, DS()); - EXPECT_EQ(s.size(), 0u); + EXPECT_TRUE(s.empty()); + + s.insert(Three); + s.insert_all(t); + EXPECT_EQ(s.size(), 4u); + EXPECT_EQ(t.size(), 3u); + EXPECT_NE(s, t); + EXPECT_FALSE(s.contains_none(t)); + EXPECT_TRUE(s.contains_any(t)); + EXPECT_TRUE(s.contains_all(t)); + EXPECT_TRUE(s.contains(Three)); + EXPECT_FALSE(t.contains_none(s)); + EXPECT_TRUE(t.contains_any(s)); + EXPECT_FALSE(t.contains_all(s)); + + s.erase_all(t); + EXPECT_EQ(s.size(), 1u); + EXPECT_TRUE(s.contains(Three)); + EXPECT_TRUE(s.contains_none(t)); + EXPECT_FALSE(s.contains_any(t)); + EXPECT_FALSE(s.contains_all(t)); + + s.insert_all(t); + s.erase(Three); + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(s, t); + + s.erase_all(t); + EXPECT_TRUE(s.empty()); EXPECT_INSERTION(s, *t.begin(), true); EXPECT_TRUE(s.contains(One)); @@ -389,8 +457,8 @@ TEST(DenseSet, modifiers) { } TEST(DenseSet, std_set) { - constexpr size_t kEnd = 50; - DenseSet<size_t, kEnd> dense_set; + constexpr size_t kMaxValue = 50; + DenseSet<size_t, kMaxValue> dense_set; std::set<size_t> std_set; auto expect_equivalence = [&] { @@ -401,7 +469,7 @@ TEST(DenseSet, std_set) { auto random_insert = [&] { expect_equivalence(); - size_t value = base::RandUint64() % kEnd; + size_t value = base::RandUint64() % kMaxValue; auto p = dense_set.insert(value); auto q = std_set.insert(value); EXPECT_EQ(p.second, q.second); @@ -412,13 +480,13 @@ TEST(DenseSet, std_set) { auto random_erase = [&] { expect_equivalence(); - size_t value = base::RandUint64() % kEnd; + size_t value = base::RandUint64() % kMaxValue; EXPECT_EQ(dense_set.erase(value), std_set.erase(value)); }; auto random_erase_iterator = [&] { expect_equivalence(); - size_t value = base::RandUint64() % kEnd; + size_t value = base::RandUint64() % kMaxValue; auto it = dense_set.find(value); auto jt = std_set.find(value); EXPECT_EQ(it == dense_set.end(), jt == std_set.end()); @@ -434,8 +502,8 @@ TEST(DenseSet, std_set) { auto random_erase_range = [&] { expect_equivalence(); - size_t min_value = base::RandUint64() % kEnd; - size_t max_value = base::RandUint64() % kEnd; + size_t min_value = base::RandUint64() % kMaxValue; + size_t max_value = base::RandUint64() % kMaxValue; min_value = std::min(min_value, max_value); max_value = std::max(min_value, max_value); dense_set.erase(dense_set.lower_bound(min_value), @@ -444,11 +512,11 @@ TEST(DenseSet, std_set) { std_set.upper_bound(max_value)); }; - for (size_t i = 0; i < kEnd; ++i) { + for (size_t i = 0; i < kMaxValue; ++i) { random_insert(); } - for (size_t i = 0; i < kEnd / 2; ++i) { + for (size_t i = 0; i < kMaxValue / 2; ++i) { random_erase(); } @@ -457,11 +525,11 @@ TEST(DenseSet, std_set) { std_set.clear(); expect_equivalence(); - for (size_t i = 0; i < kEnd; ++i) { + for (size_t i = 0; i < kMaxValue; ++i) { random_insert(); } - for (size_t i = 0; i < kEnd; ++i) { + for (size_t i = 0; i < kMaxValue; ++i) { random_erase_iterator(); } @@ -470,11 +538,11 @@ TEST(DenseSet, std_set) { std_set.clear(); expect_equivalence(); - for (size_t i = 0; i < kEnd; ++i) { + for (size_t i = 0; i < kMaxValue; ++i) { random_insert(); } - for (size_t i = 0; i < kEnd; ++i) { + for (size_t i = 0; i < kMaxValue; ++i) { random_erase_range(); } diff --git a/chromium/components/autofill/core/common/field_data_manager.cc b/chromium/components/autofill/core/common/field_data_manager.cc index 90712097017..860c8f63c79 100644 --- a/chromium/components/autofill/core/common/field_data_manager.cc +++ b/chromium/components/autofill/core/common/field_data_manager.cc @@ -13,7 +13,6 @@ FieldDataManager::FieldDataManager() = default; void FieldDataManager::ClearData() { field_value_and_properties_map_.clear(); - autofilled_values_map_.clear(); } bool FieldDataManager::HasFieldData(FieldRendererId id) const { @@ -21,7 +20,7 @@ bool FieldDataManager::HasFieldData(FieldRendererId id) const { field_value_and_properties_map_.end(); } -base::string16 FieldDataManager::GetUserTypedValue(FieldRendererId id) const { +base::string16 FieldDataManager::GetUserInput(FieldRendererId id) const { DCHECK(HasFieldData(id)); return field_value_and_properties_map_.at(id).first.value_or( base::string16()); @@ -86,30 +85,6 @@ bool FieldDataManager::WasAutofilledOnUserTrigger(FieldRendererId id) const { FieldPropertiesFlags::kAutofilledOnUserTrigger); } -bool FieldDataManager::WasAutofilledOnPageLoad(FieldRendererId id) const { - return HasFieldData(id) && (GetFieldPropertiesMask(id) & - FieldPropertiesFlags::kAutofilledOnPageLoad); -} - -void FieldDataManager::UpdateFieldDataWithAutofilledValue( - FieldRendererId id, - const base::string16& value, - FieldPropertiesMask mask) { - // Typed value has no interest once it is rewritten with an autofilled value. - if (HasFieldData(id)) - field_value_and_properties_map_.at(id).first.reset(); - UpdateFieldDataMapWithNullValue(id, mask); - autofilled_values_map_[id] = value; -} - -base::Optional<base::string16> FieldDataManager::GetAutofilledValue( - FieldRendererId id) const { - if (autofilled_values_map_.count(id)) - return base::Optional<base::string16>(autofilled_values_map_.at(id)); - else - return base::nullopt; -} - FieldDataManager::~FieldDataManager() = default; } // namespace autofill diff --git a/chromium/components/autofill/core/common/field_data_manager.h b/chromium/components/autofill/core/common/field_data_manager.h index 501eabc9f4b..5ce658e6099 100644 --- a/chromium/components/autofill/core/common/field_data_manager.h +++ b/chromium/components/autofill/core/common/field_data_manager.h @@ -38,7 +38,9 @@ class FieldDataManager : public base::RefCounted<FieldDataManager> { void UpdateFieldDataMapWithNullValue(FieldRendererId id, FieldPropertiesMask mask); - base::string16 GetUserTypedValue(FieldRendererId id) const; + // Returns value that was either typed or manually autofilled into the field. + base::string16 GetUserInput(FieldRendererId id) const; + FieldPropertiesMask GetFieldPropertiesMask(FieldRendererId id) const; // Check if the string |value| is saved in |field_value_and_properties_map_|. @@ -52,24 +54,12 @@ class FieldDataManager : public base::RefCounted<FieldDataManager> { return field_value_and_properties_map_; } - bool WasAutofilledOnPageLoad(FieldRendererId id) const; - - // Update data with autofilled value. - void UpdateFieldDataWithAutofilledValue(FieldRendererId id, - const base::string16& value, - FieldPropertiesMask mask); - - base::Optional<base::string16> GetAutofilledValue(FieldRendererId id) const; - private: friend class base::RefCounted<FieldDataManager>; ~FieldDataManager(); FieldDataMap field_value_and_properties_map_; - - // Stores values autofilled either on page load or on user trigger. - std::map<FieldRendererId, base::string16> autofilled_values_map_; }; } // namespace autofill diff --git a/chromium/components/autofill/core/common/field_data_manager_unittest.cc b/chromium/components/autofill/core/common/field_data_manager_unittest.cc index 8c56453876e..ca525b3a14a 100644 --- a/chromium/components/autofill/core/common/field_data_manager_unittest.cc +++ b/chromium/components/autofill/core/common/field_data_manager_unittest.cc @@ -44,14 +44,14 @@ TEST_F(FieldDataManagerTest, UpdateFieldDataMap) { FieldPropertiesFlags::kUserTyped); const FieldRendererId id(control_elements_[0].unique_renderer_id); EXPECT_TRUE(field_data_manager->HasFieldData(id)); - EXPECT_EQ(UTF8ToUTF16("first"), field_data_manager->GetUserTypedValue(id)); + EXPECT_EQ(UTF8ToUTF16("first"), field_data_manager->GetUserInput(id)); EXPECT_EQ(FieldPropertiesFlags::kUserTyped, field_data_manager->GetFieldPropertiesMask(id)); field_data_manager->UpdateFieldDataMap( control_elements_[0].unique_renderer_id, UTF8ToUTF16("newvalue"), FieldPropertiesFlags::kAutofilled); - EXPECT_EQ(UTF8ToUTF16("newvalue"), field_data_manager->GetUserTypedValue(id)); + EXPECT_EQ(UTF8ToUTF16("newvalue"), field_data_manager->GetUserInput(id)); FieldPropertiesMask mask = FieldPropertiesFlags::kUserTyped | FieldPropertiesFlags::kAutofilled; EXPECT_EQ(mask, field_data_manager->GetFieldPropertiesMask(id)); @@ -75,14 +75,14 @@ TEST_F(FieldDataManagerTest, UpdateFieldDataMapWithNullValue) { FieldPropertiesFlags::kUserTyped); const FieldRendererId id(control_elements_[0].unique_renderer_id); EXPECT_TRUE(field_data_manager->HasFieldData(id)); - EXPECT_EQ(base::string16(), field_data_manager->GetUserTypedValue(id)); + EXPECT_EQ(base::string16(), field_data_manager->GetUserInput(id)); EXPECT_EQ(FieldPropertiesFlags::kUserTyped, field_data_manager->GetFieldPropertiesMask(id)); field_data_manager->UpdateFieldDataMapWithNullValue( control_elements_[0].unique_renderer_id, FieldPropertiesFlags::kAutofilled); - EXPECT_EQ(base::string16(), field_data_manager->GetUserTypedValue(id)); + EXPECT_EQ(base::string16(), field_data_manager->GetUserInput(id)); FieldPropertiesMask mask = FieldPropertiesFlags::kUserTyped | FieldPropertiesFlags::kAutofilled; EXPECT_EQ(mask, field_data_manager->GetFieldPropertiesMask(id)); @@ -90,7 +90,7 @@ TEST_F(FieldDataManagerTest, UpdateFieldDataMapWithNullValue) { field_data_manager->UpdateFieldDataMap( control_elements_[0].unique_renderer_id, control_elements_[0].value, FieldPropertiesFlags::kAutofilled); - EXPECT_EQ(UTF8ToUTF16("first"), field_data_manager->GetUserTypedValue(id)); + EXPECT_EQ(UTF8ToUTF16("first"), field_data_manager->GetUserInput(id)); } TEST_F(FieldDataManagerTest, FindMachedValue) { @@ -105,23 +105,4 @@ TEST_F(FieldDataManagerTest, FindMachedValue) { field_data_manager->FindMachedValue(UTF8ToUTF16("second_element"))); } -TEST_F(FieldDataManagerTest, UpdateFieldDataMapWithAutofilledValue) { - const scoped_refptr<FieldDataManager> field_data_manager = - base::MakeRefCounted<FieldDataManager>(); - const FieldRendererId id(control_elements_[0].unique_renderer_id); - // Add a typed value to make sure it will be cleared. - field_data_manager->UpdateFieldDataMap(id, ASCIIToUTF16("typedvalue"), 0); - - field_data_manager->UpdateFieldDataWithAutofilledValue( - id, ASCIIToUTF16("autofilled"), - FieldPropertiesFlags::kAutofilledOnPageLoad); - - EXPECT_TRUE(field_data_manager->HasFieldData(id)); - EXPECT_EQ(base::string16(), field_data_manager->GetUserTypedValue(id)); - EXPECT_EQ(UTF8ToUTF16("autofilled"), - field_data_manager->GetAutofilledValue(id)); - EXPECT_EQ(FieldPropertiesFlags::kAutofilledOnPageLoad, - field_data_manager->GetFieldPropertiesMask(id)); -} - } // namespace autofill diff --git a/chromium/components/autofill/core/common/form_data.cc b/chromium/components/autofill/core/common/form_data.cc index 0861134e659..dd5ccbc4791 100644 --- a/chromium/components/autofill/core/common/form_data.cc +++ b/chromium/components/autofill/core/common/form_data.cc @@ -145,7 +145,7 @@ bool FormData::IdentityComparator::operator()(const FormData& a, bool FormHasNonEmptyPasswordField(const FormData& form) { for (const auto& field : form.fields) { if (field.IsPasswordInputElement()) { - if (!field.value.empty() || !field.typed_value.empty()) + if (!field.value.empty() || !field.user_input.empty()) return true; } } diff --git a/chromium/components/autofill/core/common/form_field_data.cc b/chromium/components/autofill/core/common/form_field_data.cc index fc998f879ef..26585e31b65 100644 --- a/chromium/components/autofill/core/common/form_field_data.cc +++ b/chromium/components/autofill/core/common/form_field_data.cc @@ -417,7 +417,7 @@ std::ostream& operator<<(std::ostream& os, const FormFieldData& field) { << "text_direction=" << field.text_direction << " " << "is_enabled=" << field.is_enabled << " " << "is_readonly=" << field.is_readonly << " " - << "typed_value=" << field.typed_value << " " + << "user_input=" << field.user_input << " " << "properties_mask=" << field.properties_mask << " " << "label_source=" << field.label_source << " " << "bounds=" << field.bounds.ToString(); diff --git a/chromium/components/autofill/core/common/form_field_data.h b/chromium/components/autofill/core/common/form_field_data.h index 84becbef96c..d9778424a43 100644 --- a/chromium/components/autofill/core/common/form_field_data.h +++ b/chromium/components/autofill/core/common/form_field_data.h @@ -173,7 +173,9 @@ struct FormFieldData { // serialised for storage. bool is_enabled = false; bool is_readonly = false; - base::string16 typed_value; + // Contains value that was either manually typed or autofilled on user + // trigger. + base::string16 user_input; // For the HTML snippet |<option value="US">United States</option>|, the // value is "US" and the contents are "United States". diff --git a/chromium/components/autofill/core/common/language_code.h b/chromium/components/autofill/core/common/language_code.h new file mode 100644 index 00000000000..ae6570fea99 --- /dev/null +++ b/chromium/components/autofill/core/common/language_code.h @@ -0,0 +1,45 @@ +// Copyright 2020 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_LANGUAGE_CODE_H_ +#define COMPONENTS_AUTOFILL_CORE_COMMON_LANGUAGE_CODE_H_ + +#include <cctype> +#include <string> +#include <utility> + +#include "base/check.h" +#include "base/ranges/algorithm.h" +#include "base/types/strong_alias.h" + +namespace autofill { + +// Following the implicit conventions in //components/translate, a LanguageCode +// is a lowercase alphabetic string of length up to 3, or "zh-CN", or "zh-TW". A +// non-exhaustive list of common values is +// translate::kDefaultSupportedLanguages. +// C++ small string optimization keeps these objects lightweight so that copying +// should not be a worry. +class LanguageCode + : public base::StrongAlias<class LanguageCodeTag, std::string> { + private: + using BaseClass = base::StrongAlias<LanguageCodeTag, std::string>; + + public: + LanguageCode() = default; + explicit LanguageCode(const char* s) : BaseClass(s) { Check(); } + explicit LanguageCode(std::string&& s) : BaseClass(std::move(s)) { Check(); } + explicit LanguageCode(const std::string& s) : BaseClass(s) { Check(); } + + private: + void Check() { + DCHECK(((*this)->size() <= 3 && base::ranges::all_of(value(), &islower)) || + value() == "zh-CN" || value() == "zh-TW") + << "Unexpected language code '" << value() << "'"; + } +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_COMMON_LANGUAGE_CODE_H_ diff --git a/chromium/components/autofill/core/common/mojom/autofill_types.mojom b/chromium/components/autofill/core/common/mojom/autofill_types.mojom index 4c35cd75787..f0a1654c991 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types.mojom +++ b/chromium/components/autofill/core/common/mojom/autofill_types.mojom @@ -132,7 +132,7 @@ struct FormFieldData { mojo_base.mojom.TextDirection text_direction; bool is_enabled; bool is_readonly; - mojo_base.mojom.String16 typed_value; + mojo_base.mojom.String16 user_input; array<mojo_base.mojom.String16> option_values; array<mojo_base.mojom.String16> option_contents; diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc index 7f8ccc299c0..7f9fb938595 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc +++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc @@ -90,7 +90,7 @@ bool StructTraits< out->is_enabled = data.is_enabled(); out->is_readonly = data.is_readonly(); - if (!data.ReadTypedValue(&out->typed_value)) + if (!data.ReadUserInput(&out->user_input)) return false; if (!data.ReadOptionValues(&out->option_values)) diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h index 8b910689db4..4863cad0561 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h +++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h @@ -153,8 +153,8 @@ struct StructTraits<autofill::mojom::FormFieldDataDataView, return r.is_readonly; } - static const base::string16& typed_value(const autofill::FormFieldData& r) { - return r.typed_value; + static const base::string16& user_input(const autofill::FormFieldData& r) { + return r.user_input; } static const std::vector<base::string16>& option_values( diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc index 5b9b8780e67..6e9744467b3 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc +++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc @@ -187,7 +187,7 @@ void ExpectFormFieldData(const FormFieldData& expected, const FormFieldData& passed) { EXPECT_TRUE(EquivalentData(expected, passed)); EXPECT_EQ(expected.value, passed.value); - EXPECT_EQ(expected.typed_value, passed.typed_value); + EXPECT_EQ(expected.user_input, passed.user_input); std::move(closure).Run(); } @@ -256,7 +256,7 @@ TEST_F(AutofillTypeTraitsTestImpl, PassFormFieldData) { input.role = FormFieldData::RoleAttribute::kPresentation; input.text_direction = base::i18n::RIGHT_TO_LEFT; input.properties_mask = FieldPropertiesFlags::kHadFocus; - input.typed_value = base::ASCIIToUTF16("TestTypedValue"); + input.user_input = base::ASCIIToUTF16("TestTypedValue"); input.bounds = gfx::RectF(1, 2, 10, 100); base::RunLoop loop; @@ -286,7 +286,7 @@ TEST_F(AutofillTypeTraitsTestImpl, PassDataListFormFieldData) { input.role = FormFieldData::RoleAttribute::kPresentation; input.text_direction = base::i18n::RIGHT_TO_LEFT; input.properties_mask = FieldPropertiesFlags::kHadFocus; - input.typed_value = base::ASCIIToUTF16("TestTypedValue"); + input.user_input = base::ASCIIToUTF16("TestTypedValue"); input.bounds = gfx::RectF(1, 2, 10, 100); base::RunLoop loop; diff --git a/chromium/components/autofill/core/common/signatures.cc b/chromium/components/autofill/core/common/signatures.cc index ea5b9129dfc..b6078f82dc2 100644 --- a/chromium/components/autofill/core/common/signatures.cc +++ b/chromium/components/autofill/core/common/signatures.cc @@ -74,6 +74,10 @@ FormSignature CalculateFormSignature(const FormData& form_data) { std::string form_signature_field_names; + auto ShouldSkipField = [](const FormFieldData& field) { + return IsCheckable(field.check_status); + }; + for (const FormFieldData& field : form_data.fields) { if (!ShouldSkipField(field)) { // Add all supported form fields (including with empty names) to the diff --git a/chromium/components/autofill/ios/browser/BUILD.gn b/chromium/components/autofill/ios/browser/BUILD.gn index edbcb3f6c66..5d55496daa7 100644 --- a/chromium/components/autofill/ios/browser/BUILD.gn +++ b/chromium/components/autofill/ios/browser/BUILD.gn @@ -41,12 +41,15 @@ source_set("browser") { "//components/autofill/ios/form_util", "//components/prefs:prefs", "//components/prefs/ios", + "//components/ukm/ios:ukm_url_recorder", "//google_apis", "//ios/web/common", "//ios/web/public", "//ios/web/public/deprecated", "//ios/web/public/js_messaging", "//ios/web/public/security", + "//services/metrics/public/cpp:metrics_cpp", + "//services/metrics/public/cpp:ukm_builders", "//services/network/public/cpp", "//ui/accessibility", "//ui/gfx/geometry", @@ -93,10 +96,10 @@ source_set("unit_tests") { "//components/leveldb_proto:leveldb_proto", "//components/prefs", "//ios/web/public", - "//ios/web/public/deprecated", "//ios/web/public/js_messaging", "//ios/web/public/test", "//ios/web/public/test/fakes", + "//services/metrics/public/cpp:ukm_builders", "//testing/gmock", "//testing/gtest", "//third_party/ocmock", diff --git a/chromium/components/autofill/ios/browser/DEPS b/chromium/components/autofill/ios/browser/DEPS index cfaa9b06ddf..c11a8a4cbb6 100644 --- a/chromium/components/autofill/ios/browser/DEPS +++ b/chromium/components/autofill/ios/browser/DEPS @@ -1,6 +1,8 @@ include_rules = [ + "+components/ukm/ios", "+ios/web/common", "+ios/web/public", + "+services/metrics/public", "+services/network/public/cpp", "+third_party/ocmock", ] diff --git a/chromium/components/autofill/ios/browser/autofill_agent.mm b/chromium/components/autofill/ios/browser/autofill_agent.mm index c33daac5902..422adbc7dc9 100644 --- a/chromium/components/autofill/ios/browser/autofill_agent.mm +++ b/chromium/components/autofill/ios/browser/autofill_agent.mm @@ -4,6 +4,8 @@ #import "components/autofill/ios/browser/autofill_agent.h" +#import <UIKit/UIKit.h> + #include <memory> #include <string> #include <utility> @@ -48,8 +50,8 @@ #import "components/prefs/ios/pref_observer_bridge.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h" +#include "components/ukm/ios/ukm_url_recorder.h" #include "ios/web/common/url_scheme_util.h" -#import "ios/web/public/deprecated/crw_js_injection_receiver.h" #include "ios/web/public/deprecated/url_verification_constants.h" #include "ios/web/public/js_messaging/web_frame.h" #include "ios/web/public/js_messaging/web_frame_util.h" @@ -57,6 +59,7 @@ #import "ios/web/public/navigation/navigation_context.h" #import "ios/web/public/web_state.h" #import "ios/web/public/web_state_observer_bridge.h" +#include "services/metrics/public/cpp/ukm_builders.h" #include "ui/gfx/geometry/rect.h" #include "url/gurl.h" @@ -106,35 +109,6 @@ void GetFormField(autofill::FormFieldData* field, } } -void UpdateFieldManagerWithFillingResults( - scoped_refptr<FieldDataManager> fieldDataManager, - NSString* jsonString, - size_t numFieldsInFormData) { - std::map<uint32_t, base::string16> fillingResults; - if (autofill::ExtractFillingResults(jsonString, &fillingResults)) { - for (auto& fillData : fillingResults) { - fieldDataManager->UpdateFieldDataWithAutofilledValue( - FieldRendererId(fillData.first), fillData.second, - kAutofilledOnUserTrigger); - } - } - // TODO(crbug/1131038): Remove once the experiment is over. - UMA_HISTOGRAM_BOOLEAN("Autofill.FormFillSuccessIOS", !fillingResults.empty()); -} - -void UpdateFieldManagerForClearedIDs( - scoped_refptr<FieldDataManager> fieldDataManager, - NSString* jsonString) { - std::vector<uint32_t> clearingResults; - if (autofill::ExtractIDs(jsonString, &clearingResults)) { - for (auto uniqueID : clearingResults) { - fieldDataManager->UpdateFieldDataWithAutofilledValue( - FieldRendererId(uniqueID), base::string16(), - kAutofilledOnUserTrigger); - } - } -} - } // namespace @interface AutofillAgent () <CRWWebStateObserver, @@ -270,7 +244,7 @@ autofillManagerFromWebState:(web::WebState*)webState ofFormsSeen:(const FormDataVector&)forms { DCHECK(autofillManager); DCHECK(!forms.empty()); - autofillManager->OnFormsSeen(forms, base::TimeTicks::Now()); + autofillManager->OnFormsSeen(forms); } // Notifies the autofill manager when forms are submitted. @@ -425,6 +399,17 @@ autofillManagerFromWebState:(web::WebState*)webState completion(_mostRecentSuggestions, self); } +- (void)updateFieldManagerForClearedIDs:(NSString*)jsonString { + std::vector<uint32_t> clearingResults; + if (autofill::ExtractIDs(jsonString, &clearingResults)) { + for (auto uniqueID : clearingResults) { + _fieldDataManager->UpdateFieldDataMap(FieldRendererId(uniqueID), + base::string16(), + kAutofilledOnUserTrigger); + } + } +} + - (void)didSelectSuggestion:(FormSuggestion*)suggestion form:(NSString*)formName uniqueFormID:(FormRendererId)uniqueFormID @@ -471,8 +456,8 @@ autofillManagerFromWebState:(web::WebState*)webState AutofillAgent* strongSelf = weakSelf; if (!strongSelf) return; - UpdateFieldManagerForClearedIDs( - strongSelf->_fieldDataManager, jsonString); + [strongSelf + updateFieldManagerForClearedIDs:jsonString]; suggestionHandledCompletionCopy(); }]; @@ -933,13 +918,31 @@ autofillManagerFromWebState:(web::WebState*)webState if (!strongSelf) return; if (success) { - strongSelf->_fieldDataManager->UpdateFieldDataWithAutofilledValue( + strongSelf->_fieldDataManager->UpdateFieldDataMap( uniqueFieldID, value, kAutofilledOnUserTrigger); } suggestionHandledCompletionCopy(); }]; } +- (void)updateFieldManagerWithFillingResults:(NSString*)jsonString { + std::map<uint32_t, base::string16> fillingResults; + if (autofill::ExtractFillingResults(jsonString, &fillingResults)) { + for (auto& fillData : fillingResults) { + _fieldDataManager->UpdateFieldDataMap(FieldRendererId(fillData.first), + fillData.second, + kAutofilledOnUserTrigger); + } + } + // TODO(crbug/1131038): Remove once the experiment is over. + UMA_HISTOGRAM_BOOLEAN("Autofill.FormFillSuccessIOS", !fillingResults.empty()); + + ukm::SourceId source_id = ukm::GetSourceIdForWebStateDocument(_webState); + ukm::builders::Autofill_FormFillSuccessIOS(source_id) + .SetFormFillSuccess(!fillingResults.empty()) + .Record(ukm::UkmRecorder::Get()); +} + // Sends the the |data| to |frame| to actually fill the data. - (void)sendData:(std::unique_ptr<base::Value>)data toFrame:(web::WebFrame*)frame { @@ -948,7 +951,6 @@ autofillManagerFromWebState:(web::WebState*)webState SuggestionHandledCompletion suggestionHandledCompletionCopy = [_suggestionHandledCompletion copy]; _suggestionHandledCompletion = nil; - size_t numFieldsInFormData = data->FindPath("fields")->DictSize(); [_jsAutofillManager fillForm:std::move(data) forceFillFieldIdentifier:SysUTF16ToNSString(_pendingAutocompleteField) forceFillFieldUniqueID:_pendingAutocompleteFieldID @@ -957,9 +959,7 @@ autofillManagerFromWebState:(web::WebState*)webState AutofillAgent* strongSelf = weakSelf; if (!strongSelf) return; - UpdateFieldManagerWithFillingResults( - strongSelf->_fieldDataManager, jsonString, - numFieldsInFormData); + [strongSelf updateFieldManagerWithFillingResults:jsonString]; // It is possible that the fill was not initiated by selecting // a suggestion in this case the callback is nil. if (suggestionHandledCompletionCopy) diff --git a/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm b/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm index 1dac7ebe0d1..2e1a8d31875 100644 --- a/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm +++ b/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm @@ -4,6 +4,7 @@ #import "components/autofill/ios/browser/autofill_agent.h" +#include "base/mac/bundle_locations.h" #include "base/strings/utf_string_conversions.h" #import "base/test/ios/wait_util.h" #include "base/test/scoped_feature_list.h" @@ -18,12 +19,15 @@ #import "components/autofill/ios/browser/js_autofill_manager.h" #include "components/autofill/ios/form_util/unique_id_data_tab_helper.h" #include "components/prefs/pref_service.h" -#import "ios/web/public/deprecated/crw_js_injection_receiver.h" +#include "ios/web/public/js_messaging/web_frame_util.h" +#include "ios/web/public/test/fakes/fake_browser_state.h" +#include "ios/web/public/test/fakes/fake_web_client.h" #include "ios/web/public/test/fakes/fake_web_frame.h" #import "ios/web/public/test/fakes/fake_web_frames_manager.h" -#include "ios/web/public/test/fakes/test_browser_state.h" -#import "ios/web/public/test/fakes/test_web_state.h" +#import "ios/web/public/test/fakes/fake_web_state.h" #include "ios/web/public/test/web_task_environment.h" +#import "ios/web/public/test/web_test_with_web_state.h" +#include "services/metrics/public/cpp/ukm_builders.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #import "testing/gtest_mac.h" @@ -39,9 +43,14 @@ using autofill::POPUP_ITEM_ID_CLEAR_FORM; using autofill::POPUP_ITEM_ID_SHOW_ACCOUNT_CARDS; using autofill::FormRendererId; +using autofill::FieldDataManager; using autofill::FieldRendererId; using base::test::ios::WaitUntilCondition; +@interface AutofillAgent (Testing) +- (void)updateFieldManagerWithFillingResults:(NSString*)jsonString; +@end + // Subclass of web::FakeWebFrame that allow to set a callback before any // JavaScript call. This callback can be used to check the state of the page. class FakeWebFrameCallback : public web::FakeWebFrame { @@ -72,30 +81,26 @@ class AutofillAgentTests : public PlatformTest { void AddWebFrame(std::unique_ptr<web::WebFrame> frame) { web::WebFrame* frame_ptr = frame.get(); fake_web_frames_manager_->AddWebFrame(std::move(frame)); - test_web_state_.OnWebFrameDidBecomeAvailable(frame_ptr); + fake_web_state_.OnWebFrameDidBecomeAvailable(frame_ptr); } void RemoveWebFrame(const std::string& frame_id) { web::WebFrame* frame_ptr = fake_web_frames_manager_->GetFrameWithId(frame_id); - test_web_state_.OnWebFrameWillBecomeUnavailable(frame_ptr); + fake_web_state_.OnWebFrameWillBecomeUnavailable(frame_ptr); fake_web_frames_manager_->RemoveWebFrame(frame_id); } void SetUp() override { PlatformTest::SetUp(); - // Mock CRWJSInjectionReceiver for verifying interactions. - mock_js_injection_receiver_ = - [OCMockObject mockForClass:[CRWJSInjectionReceiver class]]; - test_web_state_.SetBrowserState(&test_browser_state_); - test_web_state_.SetJSInjectionReceiver(mock_js_injection_receiver_); - test_web_state_.SetContentIsHTML(true); + fake_web_state_.SetBrowserState(&fake_browser_state_); + fake_web_state_.SetContentIsHTML(true); auto frames_manager = std::make_unique<web::FakeWebFramesManager>(); fake_web_frames_manager_ = frames_manager.get(); - test_web_state_.SetWebFramesManager(std::move(frames_manager)); + fake_web_state_.SetWebFramesManager(std::move(frames_manager)); GURL url("https://example.com"); - test_web_state_.SetCurrentURL(url); + fake_web_state_.SetCurrentURL(url); auto main_frame = std::make_unique<web::FakeWebFrame>("frameID", true, url); fake_main_frame_ = main_frame.get(); AddWebFrame(std::move(main_frame)); @@ -103,21 +108,20 @@ class AutofillAgentTests : public PlatformTest { prefs_ = autofill::test::PrefServiceForTesting(); autofill::prefs::SetAutofillProfileEnabled(prefs_.get(), true); autofill::prefs::SetAutofillCreditCardEnabled(prefs_.get(), true); - UniqueIDDataTabHelper::CreateForWebState(&test_web_state_); + UniqueIDDataTabHelper::CreateForWebState(&fake_web_state_); autofill_agent_ = [[AutofillAgent alloc] initWithPrefService:prefs_.get() - webState:&test_web_state_]; + webState:&fake_web_state_]; } web::WebTaskEnvironment task_environment_; - web::TestBrowserState test_browser_state_; - web::TestWebState test_web_state_; + web::FakeBrowserState fake_browser_state_; + web::FakeWebState fake_web_state_; web::FakeWebFrame* fake_main_frame_ = nullptr; web::FakeWebFramesManager* fake_web_frames_manager_ = nullptr; autofill::TestAutofillClient client_; std::unique_ptr<PrefService> prefs_; AutofillAgent* autofill_agent_; - id mock_js_injection_receiver_; DISALLOW_COPY_AND_ASSIGN(AutofillAgentTests); }; @@ -135,7 +139,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) { std::string locale("en"); autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate( - &test_web_state_, &client_, nil, locale, + &fake_web_state_, &client_, nil, locale, autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER); autofill::FormData form; @@ -184,8 +188,8 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) { form.fields.push_back(field); [autofill_agent_ fillFormData:form - inFrame:test_web_state_.GetWebFramesManager()->GetMainWebFrame()]; - test_web_state_.WasShown(); + inFrame:fake_web_state_.GetWebFramesManager()->GetMainWebFrame()]; + fake_web_state_.WasShown(); EXPECT_EQ( "__gCrWeb.autofill.fillForm({\"fields\":{\"name\":{\"section\":\"\"," "\"value\":\"name_value\"}," @@ -207,7 +211,7 @@ TEST_F(AutofillAgentTests, std::string locale("en"); autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate( - &test_web_state_, &client_, nil, locale, + &fake_web_state_, &client_, nil, locale, autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER); autofill::FormData form; @@ -256,8 +260,8 @@ TEST_F(AutofillAgentTests, form.fields.push_back(field); [autofill_agent_ fillFormData:form - inFrame:test_web_state_.GetWebFramesManager()->GetMainWebFrame()]; - test_web_state_.WasShown(); + inFrame:fake_web_state_.GetWebFramesManager()->GetMainWebFrame()]; + fake_web_state_.WasShown(); EXPECT_EQ("__gCrWeb.autofill.fillForm({\"fields\":{\"1\":{\"section\":\"\"," "\"value\":\"number_value\"}," "\"2\":{\"section\":\"\",\"value\":\"name_value\"}}," @@ -278,7 +282,7 @@ TEST_F(AutofillAgentTests, std::string locale("en"); autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate( - &test_web_state_, &client_, nil, locale, + &fake_web_state_, &client_, nil, locale, autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER); autofill::FormData form; @@ -318,8 +322,8 @@ TEST_F(AutofillAgentTests, // Fields are in alphabetical order. [autofill_agent_ fillFormData:form - inFrame:test_web_state_.GetWebFramesManager()->GetMainWebFrame()]; - test_web_state_.WasShown(); + inFrame:fake_web_state_.GetWebFramesManager()->GetMainWebFrame()]; + fake_web_state_.WasShown(); EXPECT_EQ("__gCrWeb.autofill.fillForm({\"fields\":{\"field1\":{\"section\":" "\"\",\"value\":\"value " "2\"},\"region\":{\"section\":\"\",\"value\":\"California\"}}," @@ -350,9 +354,9 @@ TEST_F(AutofillAgentTests, [autofill_agent_ checkIfSuggestionsAvailableForForm:form_query isMainFrame:YES hasUserGesture:YES - webState:&test_web_state_ + webState:&fake_web_state_ completionHandler:nil]; - test_web_state_.WasShown(); + fake_web_state_.WasShown(); EXPECT_EQ("__gCrWeb.autofill.extractForms(1, true);", fake_main_frame_->GetLastJavaScriptCall()); } @@ -377,12 +381,12 @@ TEST_F(AutofillAgentTests, [autofill_agent_ checkIfSuggestionsAvailableForForm:form_query isMainFrame:YES hasUserGesture:NO - webState:&test_web_state_ + webState:&fake_web_state_ completionHandler:^(BOOL success) { completion_handler_success = success; completion_handler_called = YES; }]; - test_web_state_.WasShown(); + fake_web_state_.WasShown(); // Wait until the expected handler is called. WaitUntilCondition(^bool() { @@ -420,9 +424,9 @@ TEST_F(AutofillAgentTests, onSuggestionsReady_ShowAccountCards) { typedValue:@"" frameID:@"frameID"]; [autofill_agent_ retrieveSuggestionsForForm:form_query - webState:&test_web_state_ + webState:&fake_web_state_ completionHandler:completionHandler]; - test_web_state_.WasShown(); + fake_web_state_.WasShown(); // Wait until the expected handler is called. WaitUntilCondition(^bool() { @@ -468,9 +472,9 @@ TEST_F(AutofillAgentTests, onSuggestionsReady_ClearForm) { typedValue:@"" frameID:@"frameID"]; [autofill_agent_ retrieveSuggestionsForForm:form_query - webState:&test_web_state_ + webState:&fake_web_state_ completionHandler:completionHandler]; - test_web_state_.WasShown(); + fake_web_state_.WasShown(); // Wait until the expected handler is called. WaitUntilCondition(^bool() { @@ -518,9 +522,9 @@ TEST_F(AutofillAgentTests, onSuggestionsReady_ClearFormWithGPay) { typedValue:@"" frameID:@"frameID"]; [autofill_agent_ retrieveSuggestionsForForm:form_query - webState:&test_web_state_ + webState:&fake_web_state_ completionHandler:completionHandler]; - test_web_state_.WasShown(); + fake_web_state_.WasShown(); // Wait until the expected handler is called. WaitUntilCondition(^bool() { @@ -539,20 +543,20 @@ TEST_F(AutofillAgentTests, onSuggestionsReady_ClearFormWithGPay) { TEST_F(AutofillAgentTests, FrameInitializationOrderFrames) { std::string locale("en"); autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate( - &test_web_state_, &client_, nil, locale, + &fake_web_state_, &client_, nil, locale, autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER); // Remove the current main frame. RemoveWebFrame(fake_main_frame_->GetFrameId()); // Both frames available, then page loaded. - test_web_state_.SetLoading(true); + fake_web_state_.SetLoading(true); auto main_frame_unique = std::make_unique<web::FakeWebFrame>("main", true, GURL()); web::FakeWebFrame* main_frame = main_frame_unique.get(); AddWebFrame(std::move(main_frame_unique)); autofill::AutofillDriverIOS* main_frame_driver = - autofill::AutofillDriverIOS::FromWebStateAndWebFrame(&test_web_state_, + autofill::AutofillDriverIOS::FromWebStateAndWebFrame(&fake_web_state_, main_frame); EXPECT_TRUE(main_frame_driver->IsInMainFrame()); auto iframe_unique = std::make_unique<FakeWebFrameCallback>( @@ -562,13 +566,13 @@ TEST_F(AutofillAgentTests, FrameInitializationOrderFrames) { FakeWebFrameCallback* iframe = iframe_unique.get(); AddWebFrame(std::move(iframe_unique)); autofill::AutofillDriverIOS* iframe_driver = - autofill::AutofillDriverIOS::FromWebStateAndWebFrame(&test_web_state_, + autofill::AutofillDriverIOS::FromWebStateAndWebFrame(&fake_web_state_, iframe); EXPECT_FALSE(iframe_driver->IsInMainFrame()); EXPECT_FALSE(main_frame_driver->is_processed()); EXPECT_FALSE(iframe_driver->is_processed()); - test_web_state_.SetLoading(false); - test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS); + fake_web_state_.SetLoading(false); + fake_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS); EXPECT_TRUE(main_frame_driver->is_processed()); EXPECT_TRUE(iframe_driver->is_processed()); RemoveWebFrame(main_frame->GetFrameId()); @@ -578,20 +582,20 @@ TEST_F(AutofillAgentTests, FrameInitializationOrderFrames) { main_frame_unique = std::make_unique<web::FakeWebFrame>("main", true, GURL()); main_frame = main_frame_unique.get(); main_frame_driver = autofill::AutofillDriverIOS::FromWebStateAndWebFrame( - &test_web_state_, main_frame); + &fake_web_state_, main_frame); iframe_unique = std::make_unique<FakeWebFrameCallback>( "iframe", false, GURL(), [main_frame_driver]() { EXPECT_TRUE(main_frame_driver->is_processed()); }); iframe = iframe_unique.get(); iframe_driver = autofill::AutofillDriverIOS::FromWebStateAndWebFrame( - &test_web_state_, iframe); - test_web_state_.SetLoading(true); + &fake_web_state_, iframe); + fake_web_state_.SetLoading(true); AddWebFrame(std::move(main_frame_unique)); EXPECT_FALSE(main_frame_driver->is_processed()); EXPECT_FALSE(iframe_driver->is_processed()); - test_web_state_.SetLoading(false); - test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS); + fake_web_state_.SetLoading(false); + fake_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS); EXPECT_TRUE(main_frame_driver->is_processed()); EXPECT_FALSE(iframe_driver->is_processed()); AddWebFrame(std::move(iframe_unique)); @@ -604,17 +608,17 @@ TEST_F(AutofillAgentTests, FrameInitializationOrderFrames) { main_frame_unique = std::make_unique<web::FakeWebFrame>("main", true, GURL()); main_frame = main_frame_unique.get(); main_frame_driver = autofill::AutofillDriverIOS::FromWebStateAndWebFrame( - &test_web_state_, main_frame); + &fake_web_state_, main_frame); iframe_unique = std::make_unique<FakeWebFrameCallback>( "iframe", false, GURL(), [main_frame_driver]() { EXPECT_TRUE(main_frame_driver->is_processed()); }); iframe = iframe_unique.get(); iframe_driver = autofill::AutofillDriverIOS::FromWebStateAndWebFrame( - &test_web_state_, iframe); - test_web_state_.SetLoading(true); - test_web_state_.SetLoading(false); - test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS); + &fake_web_state_, iframe); + fake_web_state_.SetLoading(true); + fake_web_state_.SetLoading(false); + fake_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS); EXPECT_FALSE(main_frame_driver->is_processed()); EXPECT_FALSE(iframe_driver->is_processed()); AddWebFrame(std::move(main_frame_unique)); @@ -630,17 +634,17 @@ TEST_F(AutofillAgentTests, FrameInitializationOrderFrames) { main_frame_unique = std::make_unique<web::FakeWebFrame>("main", true, GURL()); main_frame = main_frame_unique.get(); main_frame_driver = autofill::AutofillDriverIOS::FromWebStateAndWebFrame( - &test_web_state_, main_frame); + &fake_web_state_, main_frame); iframe_unique = std::make_unique<FakeWebFrameCallback>( "iframe", false, GURL(), [main_frame_driver]() { EXPECT_TRUE(main_frame_driver->is_processed()); }); iframe = iframe_unique.get(); iframe_driver = autofill::AutofillDriverIOS::FromWebStateAndWebFrame( - &test_web_state_, iframe); - test_web_state_.SetLoading(true); - test_web_state_.SetLoading(false); - test_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS); + &fake_web_state_, iframe); + fake_web_state_.SetLoading(true); + fake_web_state_.SetLoading(false); + fake_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS); EXPECT_FALSE(main_frame_driver->is_processed()); EXPECT_FALSE(iframe_driver->is_processed()); AddWebFrame(std::move(iframe_unique)); @@ -652,3 +656,23 @@ TEST_F(AutofillAgentTests, FrameInitializationOrderFrames) { RemoveWebFrame(main_frame->GetFrameId()); RemoveWebFrame(iframe->GetFrameId()); } + +TEST_F(AutofillAgentTests, UpdateFieldManagerWithFillingResults) { + auto test_recorder = std::make_unique<ukm::TestAutoSetUkmRecorder>(); + + [autofill_agent_ updateFieldManagerWithFillingResults:@"{\"1\":\"Val1\"}"]; + + // Check recorded FieldDataManager data. + UniqueIDDataTabHelper* uniqueIDDataTabHelper = + UniqueIDDataTabHelper::FromWebState(&fake_web_state_); + scoped_refptr<FieldDataManager> fieldDataManager = + uniqueIDDataTabHelper->GetFieldDataManager(); + EXPECT_TRUE(fieldDataManager->WasAutofilledOnUserTrigger(FieldRendererId(1))); + + // Check recorded UKM. + auto entries = test_recorder->GetEntriesByName( + ukm::builders::Autofill_FormFillSuccessIOS::kEntryName); + // Expect one recorded metric. + ASSERT_EQ(1u, entries.size()); + test_recorder->ExpectEntryMetric(entries[0], "FormFillSuccess", true); +} diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.h b/chromium/components/autofill/ios/browser/autofill_driver_ios.h index c04142313b1..ad73457b698 100644 --- a/chromium/components/autofill/ios/browser/autofill_driver_ios.h +++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.h @@ -99,8 +99,6 @@ class AutofillDriverIOS : public AutofillDriver { // AutofillManager instance via which this object drives the shared Autofill // code. AutofillManager autofill_manager_; - // AutofillExternalDelegate instance that is passed to the AutofillManager. - AutofillExternalDelegate autofill_external_delegate_; }; } // namespace autofill diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm index f72958bede1..0ad76b76877 100644 --- a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm +++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm @@ -54,10 +54,8 @@ AutofillDriverIOS::AutofillDriverIOS( AutofillManager::AutofillDownloadManagerState enable_download_manager) : web_state_(web_state), bridge_(bridge), - autofill_manager_(this, client, app_locale, enable_download_manager), - autofill_external_delegate_(&autofill_manager_, this) { + autofill_manager_(this, client, app_locale, enable_download_manager) { web_frame_id_ = web::GetWebFrameId(web_frame); - autofill_manager_.SetExternalDelegate(&autofill_external_delegate_); } AutofillDriverIOS::~AutofillDriverIOS() {} diff --git a/chromium/components/autofill/ios/browser/autofill_util.h b/chromium/components/autofill/ios/browser/autofill_util.h index a533b81674c..8d6e48d9235 100644 --- a/chromium/components/autofill/ios/browser/autofill_util.h +++ b/chromium/components/autofill/ios/browser/autofill_util.h @@ -9,7 +9,6 @@ #import "ios/web/public/js_messaging/web_frame.h" -@class CRWJSInjectionReceiver; class GURL; namespace base { diff --git a/chromium/components/autofill/ios/browser/js_suggestion_manager.h b/chromium/components/autofill/ios/browser/js_suggestion_manager.h index 953742feb73..425e4ae9cb8 100644 --- a/chromium/components/autofill/ios/browser/js_suggestion_manager.h +++ b/chromium/components/autofill/ios/browser/js_suggestion_manager.h @@ -5,70 +5,90 @@ #ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_JS_SUGGESTION_MANAGER_H_ #define COMPONENTS_AUTOFILL_IOS_BROWSER_JS_SUGGESTION_MANAGER_H_ -#import "ios/web/public/deprecated/crw_js_injection_receiver.h" +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#import "ios/web/public/web_state_user_data.h" + +namespace base { +class Value; +} // namespace base namespace web { -class WebFramesManager; -} // namespace - -// Loads the JavaScript file, suggestion_manager.js, which contains form parsing -// and autofill functions. -@interface JsSuggestionManager : NSObject - -// Designated initializer. |receiver| should not be nil. -- (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver - NS_DESIGNATED_INITIALIZER; - -- (instancetype)init NS_UNAVAILABLE; - -// Sets the WebFrames manager associated with the receiver. -- (void)setWebFramesManager:(web::WebFramesManager*)framesManager; - -// Focuses the next focusable element in tab order inside the web frame with -// frame id |frameID|. No action if there is no such element. -- (void)selectNextElementInFrameWithID:(NSString*)frameID; - -// Focuses the next focusable element in tab order after the element specified -// by |formName| and |fieldName| in tab order inside the web frame with frame id -// |frameID|. No action if there is no such element. -- (void)selectNextElementInFrameWithID:(NSString*)frameID - afterForm:(NSString*)formName - field:(NSString*)fieldName; - -// Focuses the previous focusable element in tab order inside the web frame with -// frame id |frameID|. No action if there is no such element. -- (void)selectPreviousElementInFrameWithID:(NSString*)frameID; - -// Focuses the previous focusable element in tab order from the element -// specified by |formName| and |fieldName| in tab order inside the web frame -// with frame id |frameID|. No action if there is no such element. -- (void)selectPreviousElementInFrameWithID:(NSString*)frameID - beforeForm:(NSString*)formName - field:(NSString*)fieldName; - -// Checks if the frame with frame id |frameID| contains a next and previous -// element. |completionHandler| is called with 2 BOOLs, the first indicating if -// a previous element was found, and the second indicating if a next element was -// found. |completionHandler| cannot be nil. -- (void)fetchPreviousAndNextElementsPresenceInFrameWithID:(NSString*)frameID - completionHandler:(void (^)(BOOL, BOOL)) - completionHandler; - -// Checks if the frame with frame id |frameID| contains a next and previous -// element starting from the field specified by |formName| and |fieldName|. -// |completionHandler| is called with 2 BOOLs, the first indicating if a -// previous element was found, and the second indicating if a next element was -// found. |completionHandler| cannot be nil. -- (void)fetchPreviousAndNextElementsPresenceInFrameWithID:(NSString*)frameID - forForm:(NSString*)formName - field:(NSString*)fieldName - completionHandler:(void (^)(BOOL, BOOL)) - completionHandler; - -// Closes the keyboard and defocuses the active input element in the frame with -// frame id |frameID|. -- (void)closeKeyboardForFrameWithID:(NSString*)frameID; - -@end +class WebFrame; +class WebState; +} // namespace web + +namespace autofill { + +class JsSuggestionManager : public web::WebStateUserData<JsSuggestionManager> { + public: + ~JsSuggestionManager() override; + + static JsSuggestionManager* GetOrCreateForWebState(web::WebState* web_state); + + // Focuses the next focusable element in tab order inside the web frame with + // frame id |frame_ID|. No action if there is no such element. + void SelectNextElementInFrameWithID(const std::string& frame_ID); + + // Focuses the next focusable element in tab order after the element specified + // by |form_name| and |field_name| in tab order inside the web frame with + // frame id |frame_ID|. No action if there is no such element. + void SelectNextElementInFrameWithID(const std::string& frame_ID, + const std::string& form_name, + const std::string& field_name); + + // Focuses the previous focusable element in tab order inside the web frame + // with frame id |frame_ID|. No action if there is no such element. + void SelectPreviousElementInFrameWithID(const std::string& frame_ID); + + // Focuses the previous focusable element in tab order from the element + // specified by |form_name| and |field_name| in tab order inside the web frame + // with frame id |frame_ID|. No action if there is no such element. + void SelectPreviousElementInFrameWithID(const std::string& frame_ID, + const std::string& form_name, + const std::string& field_name); + + // Checks if the frame with frame id |frame_ID| contains a next and previous + // element. |completionHandler| is called with 2 bools, the first indicating + // if a previous element was found, and the second indicating if a next + // element was found. |completionHcompletion_handlerandler| cannot be nil. + void FetchPreviousAndNextElementsPresenceInFrameWithID( + const std::string& frame_ID, + base::OnceCallback<void(bool, bool)> completion_handler); + + // Checks if the frame with frame id |frame_ID| contains a next and previous + // element starting from the field specified by |form_name| and |field_name|. + // |completionHandler| is called with 2 BOOLs, the first indicating if a + // previous element was found, and the second indicating if a next element was + // found. |completion_handler| cannot be nil. + void FetchPreviousAndNextElementsPresenceInFrameWithID( + const std::string& frame_ID, + const std::string& form_name, + const std::string& field_name, + base::OnceCallback<void(bool, bool)> completion_handler); + + // Closes the keyboard and defocuses the active input element in the frame + // with frame id |frame_ID|. + void CloseKeyboardForFrameWithID(const std::string& frame_ID); + + private: + explicit JsSuggestionManager(web::WebState* web_state); + + void PreviousAndNextElementsPresenceResult( + base::OnceCallback<void(bool, bool)> completion_handler, + const base::Value* res); + + web::WebFrame* GetFrameWithFrameID(const std::string& frame_ID); + + web::WebState* web_state_; + + base::WeakPtrFactory<JsSuggestionManager> weak_ptr_factory_; + + friend class web::WebStateUserData<JsSuggestionManager>; + + WEB_STATE_USER_DATA_KEY_DECL(); +}; + +} // namespace autofill #endif // COMPONENTS_AUTOFILL_IOS_BROWSER_JS_SUGGESTION_MANAGER_H_ diff --git a/chromium/components/autofill/ios/browser/js_suggestion_manager.mm b/chromium/components/autofill/ios/browser/js_suggestion_manager.mm index 7826e68fbae..2b207468e32 100644 --- a/chromium/components/autofill/ios/browser/js_suggestion_manager.mm +++ b/chromium/components/autofill/ios/browser/js_suggestion_manager.mm @@ -4,6 +4,7 @@ #import "components/autofill/ios/browser/js_suggestion_manager.h" +#import <Foundation/Foundation.h> #include <vector> #include "base/bind.h" @@ -14,7 +15,6 @@ #include "base/strings/sys_string_conversions.h" #include "base/values.h" #import "components/autofill/ios/browser/autofill_util.h" -#import "ios/web/public/deprecated/crw_js_injection_receiver.h" #include "ios/web/public/js_messaging/web_frame.h" #include "ios/web/public/js_messaging/web_frames_manager.h" @@ -22,115 +22,125 @@ #error "This file requires ARC support." #endif -@implementation JsSuggestionManager { - // The injection receiver used to evaluate JavaScript. - __weak CRWJSInjectionReceiver* _receiver; - web::WebFramesManager* _webFramesManager; -} +namespace autofill { -- (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver { - DCHECK(receiver); - self = [super init]; - if (self) { - _receiver = receiver; - } - return self; -} +JsSuggestionManager::JsSuggestionManager(web::WebState* web_state) + : web_state_(web_state), weak_ptr_factory_(this) {} -- (void)setWebFramesManager:(web::WebFramesManager*)framesManager { - _webFramesManager = framesManager; -} +JsSuggestionManager::~JsSuggestionManager() = default; -#pragma mark - -#pragma mark ProtectedMethods +// static +JsSuggestionManager* JsSuggestionManager::GetOrCreateForWebState( + web::WebState* web_state) { + JsSuggestionManager* helper = FromWebState(web_state); + if (!helper) { + CreateForWebState(web_state); + helper = FromWebState(web_state); + DCHECK(helper); + } + return helper; +} -- (void)selectNextElementInFrameWithID:(NSString*)frameID { - [self selectNextElementInFrameWithID:frameID afterForm:@"" field:@""]; +void JsSuggestionManager::SelectNextElementInFrameWithID( + const std::string& frame_ID) { + SelectNextElementInFrameWithID(frame_ID, "", ""); } -- (void)selectNextElementInFrameWithID:(NSString*)frameID - afterForm:(NSString*)formName - field:(NSString*)fieldName { +void JsSuggestionManager::SelectNextElementInFrameWithID( + const std::string& frame_ID, + const std::string& form_name, + const std::string& field_name) { std::vector<base::Value> parameters; - parameters.push_back(base::Value(base::SysNSStringToUTF8(formName))); - parameters.push_back(base::Value(base::SysNSStringToUTF8(fieldName))); - autofill::ExecuteJavaScriptFunction( - "suggestion.selectNextElement", parameters, - [self frameWithFrameID:frameID], autofill::JavaScriptResultCallback()); + parameters.push_back(base::Value(form_name)); + parameters.push_back(base::Value(field_name)); + autofill::ExecuteJavaScriptFunction("suggestion.selectNextElement", + parameters, GetFrameWithFrameID(frame_ID), + autofill::JavaScriptResultCallback()); } -- (void)selectPreviousElementInFrameWithID:(NSString*)frameID { - [self selectPreviousElementInFrameWithID:frameID beforeForm:@"" field:@""]; +void JsSuggestionManager::SelectPreviousElementInFrameWithID( + const std::string& frame_ID) { + SelectPreviousElementInFrameWithID(frame_ID, "", ""); } -- (void)selectPreviousElementInFrameWithID:(NSString*)frameID - beforeForm:(NSString*)formName - field:(NSString*)fieldName { +void JsSuggestionManager::SelectPreviousElementInFrameWithID( + const std::string& frame_ID, + const std::string& form_name, + const std::string& field_name) { std::vector<base::Value> parameters; - parameters.push_back(base::Value(base::SysNSStringToUTF8(formName))); - parameters.push_back(base::Value(base::SysNSStringToUTF8(fieldName))); - autofill::ExecuteJavaScriptFunction( - "suggestion.selectPreviousElement", parameters, - [self frameWithFrameID:frameID], autofill::JavaScriptResultCallback()); + parameters.push_back(base::Value(form_name)); + parameters.push_back(base::Value(field_name)); + autofill::ExecuteJavaScriptFunction("suggestion.selectPreviousElement", + parameters, GetFrameWithFrameID(frame_ID), + autofill::JavaScriptResultCallback()); } -- (void)fetchPreviousAndNextElementsPresenceInFrameWithID:(NSString*)frameID - completionHandler: - (void (^)(BOOL, - BOOL))completionHandler { - [self fetchPreviousAndNextElementsPresenceInFrameWithID:frameID - forForm:@"" - field:@"" - completionHandler:completionHandler]; +void JsSuggestionManager::FetchPreviousAndNextElementsPresenceInFrameWithID( + const std::string& frame_ID, + base::OnceCallback<void(bool, bool)> completion_handler) { + FetchPreviousAndNextElementsPresenceInFrameWithID( + frame_ID, "", "", std::move(completion_handler)); } -- (void)fetchPreviousAndNextElementsPresenceInFrameWithID:(NSString*)frameID - forForm:(NSString*)formName - field:(NSString*)fieldName - completionHandler: - (void (^)(BOOL, - BOOL))completionHandler { - DCHECK(completionHandler); +void JsSuggestionManager::FetchPreviousAndNextElementsPresenceInFrameWithID( + const std::string& frame_ID, + const std::string& form_name, + const std::string& field_name, + base::OnceCallback<void(bool, bool)> completion_handler) { + DCHECK(completion_handler); std::vector<base::Value> parameters; - parameters.push_back(base::Value(base::SysNSStringToUTF8(formName))); - parameters.push_back(base::Value(base::SysNSStringToUTF8(fieldName))); + parameters.push_back(base::Value(form_name)); + parameters.push_back(base::Value(field_name)); autofill::ExecuteJavaScriptFunction( "suggestion.hasPreviousNextElements", parameters, - [self frameWithFrameID:frameID], - autofill::CreateStringCallback(^(NSString* result) { - // The result maybe an empty string here due to 2 reasons: - // 1) When there is an exception running the JS - // 2) There is a race when the page is changing due to which - // JSSuggestionManager has not yet injected __gCrWeb.suggestion - // object Handle this case gracefully. If a page has overridden - // Array.toString, the string returned may not contain a ",", - // hence this is a defensive measure to early return. - NSArray* components = [result componentsSeparatedByString:@","]; - if (components.count != 2) { - completionHandler(NO, NO); - return; - } - - DCHECK([components[0] isEqualToString:@"true"] || - [components[0] isEqualToString:@"false"]); - BOOL hasPreviousElement = [components[0] isEqualToString:@"true"]; - DCHECK([components[1] isEqualToString:@"true"] || - [components[1] isEqualToString:@"false"]); - BOOL hasNextElement = [components[1] isEqualToString:@"true"]; - completionHandler(hasPreviousElement, hasNextElement); - })); + GetFrameWithFrameID(frame_ID), + base::BindOnce( + &JsSuggestionManager::PreviousAndNextElementsPresenceResult, + weak_ptr_factory_.GetWeakPtr(), std::move(completion_handler))); } -- (void)closeKeyboardForFrameWithID:(NSString*)frameID { +void JsSuggestionManager::PreviousAndNextElementsPresenceResult( + base::OnceCallback<void(bool, bool)> completion_handler, + const base::Value* res) { + NSString* result = nil; + if (res && res->is_string()) { + result = base::SysUTF8ToNSString(res->GetString()); + } + // The result maybe an empty string here due to 2 reasons: + // 1) When there is an exception running the JS + // 2) There is a race when the page is changing due to which + // JSSuggestionManager has not yet injected __gCrWeb.suggestion + // object Handle this case gracefully. If a page has overridden + // Array.toString, the string returned may not contain a ",", + // hence this is a defensive measure to early return. + NSArray* components = [result componentsSeparatedByString:@","]; + if (components.count != 2) { + std::move(completion_handler).Run(false, false); + return; + } + + DCHECK([components[0] isEqualToString:@"true"] || + [components[0] isEqualToString:@"false"]); + bool has_previous_element = [components[0] isEqualToString:@"true"]; + DCHECK([components[1] isEqualToString:@"true"] || + [components[1] isEqualToString:@"false"]); + bool has_next_element = [components[1] isEqualToString:@"true"]; + std::move(completion_handler).Run(has_previous_element, has_next_element); +} + +void JsSuggestionManager::CloseKeyboardForFrameWithID( + const std::string& frame_ID) { std::vector<base::Value> parameters; - autofill::ExecuteJavaScriptFunction( - "suggestion.blurActiveElement", parameters, - [self frameWithFrameID:frameID], autofill::JavaScriptResultCallback()); + autofill::ExecuteJavaScriptFunction("suggestion.blurActiveElement", + parameters, GetFrameWithFrameID(frame_ID), + autofill::JavaScriptResultCallback()); } -- (web::WebFrame*)frameWithFrameID:(NSString*)frameID { - DCHECK(_webFramesManager); - return _webFramesManager->GetFrameWithId(base::SysNSStringToUTF8(frameID)); +web::WebFrame* JsSuggestionManager::GetFrameWithFrameID( + const std::string& frame_ID) { + return web_state_->GetWebFramesManager()->GetFrameWithId(frame_ID); } -@end +WEB_STATE_USER_DATA_KEY_IMPL(JsSuggestionManager) + +} // namspace autofill diff --git a/chromium/components/autofill/ios/form_util/form_activity_observer_bridge_unittest.mm b/chromium/components/autofill/ios/form_util/form_activity_observer_bridge_unittest.mm index 091d9d7c5df..07cd5182b2c 100644 --- a/chromium/components/autofill/ios/form_util/form_activity_observer_bridge_unittest.mm +++ b/chromium/components/autofill/ios/form_util/form_activity_observer_bridge_unittest.mm @@ -6,7 +6,7 @@ #include "components/autofill/ios/form_util/test_form_activity_observer.h" #import "ios/web/public/test/fakes/fake_web_frame.h" -#import "ios/web/public/test/fakes/test_web_state.h" +#import "ios/web/public/test/fakes/fake_web_state.h" #include "testing/platform_test.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -72,10 +72,10 @@ class FormActivityObserverBridgeTest : public PlatformTest { public: FormActivityObserverBridgeTest() : observer_([[FakeFormActivityObserver alloc] init]), - observer_bridge_(&test_web_state_, observer_) {} + observer_bridge_(&fake_web_state_, observer_) {} protected: - web::TestWebState test_web_state_; + web::FakeWebState fake_web_state_; FakeFormActivityObserver* observer_; autofill::FormActivityObserverBridge observer_bridge_; }; @@ -88,11 +88,11 @@ TEST_F(FormActivityObserverBridgeTest, DocumentSubmitted) { bool has_user_gesture = true; bool form_in_main_frame = true; web::FakeWebFrame sender_frame("sender_frame", true, GURL::EmptyGURL()); - observer_bridge_.DocumentSubmitted(&test_web_state_, &sender_frame, + observer_bridge_.DocumentSubmitted(&fake_web_state_, &sender_frame, kTestFormName, kTestFormData, has_user_gesture, form_in_main_frame); ASSERT_TRUE([observer_ submitDocumentInfo]); - EXPECT_EQ(&test_web_state_, [observer_ submitDocumentInfo]->web_state); + EXPECT_EQ(&fake_web_state_, [observer_ submitDocumentInfo]->web_state); EXPECT_EQ(&sender_frame, [observer_ submitDocumentInfo]->sender_frame); EXPECT_EQ(kTestFormName, [observer_ submitDocumentInfo]->form_name); EXPECT_EQ(kTestFormData, [observer_ submitDocumentInfo]->form_data); @@ -112,10 +112,10 @@ TEST_F(FormActivityObserverBridgeTest, FormActivityRegistered) { params.type = "type"; params.value = "value"; params.input_missing = true; - observer_bridge_.FormActivityRegistered(&test_web_state_, &sender_frame, + observer_bridge_.FormActivityRegistered(&fake_web_state_, &sender_frame, params); ASSERT_TRUE([observer_ formActivityInfo]); - EXPECT_EQ(&test_web_state_, [observer_ formActivityInfo]->web_state); + EXPECT_EQ(&fake_web_state_, [observer_ formActivityInfo]->web_state); EXPECT_EQ(&sender_frame, [observer_ formActivityInfo]->sender_frame); EXPECT_EQ(params.form_name, [observer_ formActivityInfo]->form_activity.form_name); diff --git a/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h b/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h index 976a4634857..72461af86fd 100644 --- a/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h +++ b/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h @@ -69,7 +69,7 @@ class FormActivityTabHelper base::ObserverList<FormActivityObserver>::Unchecked observers_; // Subscription for JS message. - std::unique_ptr<web::WebState::ScriptCommandSubscription> subscription_; + base::CallbackListSubscription subscription_; WEB_STATE_USER_DATA_KEY_DECL(); diff --git a/chromium/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm b/chromium/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm index 9acd740d56f..99f01745ae9 100644 --- a/chromium/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm +++ b/chromium/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm @@ -9,8 +9,8 @@ #import "components/autofill/ios/form_util/test_form_activity_observer.h" #include "ios/web/public/js_messaging/web_frame.h" #import "ios/web/public/js_messaging/web_frames_manager.h" -#import "ios/web/public/test/fakes/test_web_client.h" -#import "ios/web/public/test/fakes/test_web_state_observer_util.h" +#import "ios/web/public/test/fakes/fake_web_client.h" +#import "ios/web/public/test/fakes/fake_web_state_observer_util.h" #import "ios/web/public/test/js_test_util.h" #import "ios/web/public/test/web_js_test.h" #import "ios/web/public/test/web_test_with_web_state.h" @@ -18,7 +18,7 @@ using web::WebFrame; -class FormTestClient : public web::TestWebClient { +class FormTestClient : public web::FakeWebClient { public: NSString* GetDocumentStartScriptForAllFrames( web::BrowserState* browser_state) const override { diff --git a/chromium/components/autofill/ios/form_util/form_unittest.mm b/chromium/components/autofill/ios/form_util/form_unittest.mm index 3f505ad8cd7..dd6971a6738 100644 --- a/chromium/components/autofill/ios/form_util/form_unittest.mm +++ b/chromium/components/autofill/ios/form_util/form_unittest.mm @@ -6,8 +6,7 @@ #include "components/autofill/ios/form_util/form_activity_tab_helper.h" #include "components/autofill/ios/form_util/test_form_activity_observer.h" #import "ios/web/public/browser_state.h" -#import "ios/web/public/test/fakes/test_web_client.h" -#include "ios/web/public/test/fakes/test_web_state_observer.h" +#import "ios/web/public/test/fakes/fake_web_client.h" #import "ios/web/public/test/js_test_util.h" #import "ios/web/public/test/web_js_test.h" #import "ios/web/public/test/web_test_with_web_state.h" @@ -20,7 +19,7 @@ using base::test::ios::WaitUntilConditionOrTimeout; using base::test::ios::kWaitForJSCompletionTimeout; -class FormTestClient : public web::TestWebClient { +class FormTestClient : public web::FakeWebClient { public: NSString* GetDocumentStartScriptForAllFrames( web::BrowserState* browser_state) const override { diff --git a/chromium/components/autofill/ios/form_util/resources/fill.js b/chromium/components/autofill/ios/form_util/resources/fill.js index 6117e8b8a26..4526098a344 100644 --- a/chromium/components/autofill/ios/form_util/resources/fill.js +++ b/chromium/components/autofill/ios/form_util/resources/fill.js @@ -145,14 +145,11 @@ __gCrWeb.fill.ROLE_ATTRIBUTE_PRESENTATION = 0; __gCrWeb.fill.RENDERER_ID_NOT_SET = '-1'; /** - * The name of the JS Symbol used to set stable unique form and field IDs. + * The JS Symbol object used to set stable unique form and field IDs. * - * This variable is |kNotSetRendererID| from - * chromium/src/components/autofill/ios/browser/autofill_util.h - * - * @const {string} + * @const {symbol} */ -__gCrWeb.fill.UNIQUE_ID_SYMBOL_NAME = '__gChrome~uniqueID'; +__gCrWeb.fill.ID_SYMBOL = window.Symbol.for('__gChrome~uniqueID'); /** * Returns true if an element can be autocompleted. @@ -2310,8 +2307,7 @@ __gCrWeb.fill.extractAutofillableElementsFromSet = function(controlElements) { * @param {int} nextAvailableID Next available integer. */ __gCrWeb.fill['setUpForUniqueIDs'] = function(nextAvailableID) { - const uniqueID = Symbol.for(__gCrWeb.fill.UNIQUE_ID_SYMBOL_NAME); - document[uniqueID] = nextAvailableID; + document[__gCrWeb.fill.ID_SYMBOL] = nextAvailableID; }; /** @@ -2319,7 +2315,7 @@ __gCrWeb.fill['setUpForUniqueIDs'] = function(nextAvailableID) { */ __gCrWeb.fill.setUniqueIDIfNeeded = function(element) { try { - const uniqueID = Symbol.for(__gCrWeb.fill.UNIQUE_ID_SYMBOL_NAME); + const uniqueID = __gCrWeb.fill.ID_SYMBOL; // Do not assign element id value if the base value for the document // is not set. if (typeof document[uniqueID] !== 'undefined' && @@ -2336,7 +2332,7 @@ __gCrWeb.fill.setUniqueIDIfNeeded = function(element) { */ __gCrWeb.fill.getUniqueID = function(element) { try { - const uniqueID = Symbol.for(__gCrWeb.fill.UNIQUE_ID_SYMBOL_NAME); + const uniqueID = __gCrWeb.fill.ID_SYMBOL; if (typeof element[uniqueID] !== 'undefined' && !isNaN(element[uniqueID])) { return element[uniqueID].toString(); } else { diff --git a/chromium/components/autofill/ios/form_util/resources/form.js b/chromium/components/autofill/ios/form_util/resources/form.js index e857c9e88b5..135f3dbfd2f 100644 --- a/chromium/components/autofill/ios/form_util/resources/form.js +++ b/chromium/components/autofill/ios/form_util/resources/form.js @@ -280,8 +280,7 @@ __gCrWeb.form.getFormElementFromUniqueFormId = function(identifier) { const forms = document.forms; for (let i = 0; i < forms.length; i++) { const form = forms[i]; - const uniqueID = Symbol.for(__gCrWeb.fill.UNIQUE_ID_SYMBOL_NAME); - if (identifier === form[uniqueID]) { + if (identifier.toString() === __gCrWeb.fill.getUniqueID(form)) { return form; } } diff --git a/chromium/components/autofill/ios/form_util/resources/form_handlers.js b/chromium/components/autofill/ios/form_util/resources/form_handlers.js index 69f0d8d2c9c..81344804475 100644 --- a/chromium/components/autofill/ios/form_util/resources/form_handlers.js +++ b/chromium/components/autofill/ios/form_util/resources/form_handlers.js @@ -28,6 +28,11 @@ __gCrWeb.formHandlers = {}; let formMutationObserver = null; /** + * The MutationObserver tracking the latest password field that had user input. + */ +let passwordFieldsObserver = null; + +/** * The form mutation message scheduled to be sent to browser. */ let formMutationMessageToSend = null; @@ -77,8 +82,87 @@ function getFullyQualifiedUrl_(originalURL) { } /** - * Focus, input, change, keyup and blur events for form elements (form and input - * elements) are messaged to the main application for broadcast to + * @param {Element} A form element to check. + * @return {boolean} Whether the element is an input of type password. + */ +function isPasswordField_(element) { + return element.tagName === 'INPUT' && element.type === 'password'; +} + +/** + * Installs a MutationObserver to track the last password field that had + * user input. + * @param {Element} A password field that should be observed. + * @suppress {checkTypes} Required for for...of loop on mutations. + */ +function trackPasswordField_(field) { + if (passwordFieldsObserver) { + passwordFieldsObserver.disconnect(); + } + + passwordFieldsObserver = new MutationObserver(function(mutations) { + for (let i = 0; i < mutations.length; i++) { + const mutation = mutations[i]; + if (mutation.attributeName !== 'value') { + return; + } + const target = mutation.target; + const form = target.form; + let shouldNotifyPasswordManager = true; + if (form) { + // Verify that all password fields are cleared. + for (let i = 0; i < form.elements.length; i++) { + if (isPasswordField_(form.elements[i]) && + form.elements[i].value !== '') { + shouldNotifyPasswordManager = false; + } + } + } + if (!shouldNotifyPasswordManager) { + return; + } + const formData = form ? + __gCrWeb.passwords.getPasswordFormData(form, window) : + __gCrWeb.passwords.getPasswordFormDataFromUnownedElements(window); + if (target.value === '') { + const msg = { + 'command': 'form.activity', + 'formName': '', + 'uniqueFormID': '', + 'fieldIdentifier': '', + 'uniqueFieldID': '', + 'fieldType': '', + 'type': 'password_form_cleared', + 'value': __gCrWeb.stringify(formData), + 'hasUserGesture': false + }; + sendMessageOnNextLoop_(msg); + } + } + }); + passwordFieldsObserver.observe(field, {attributes: true}); +} + + +/** + * @param {Element} A form that was reset. + * @return {boolean} Whether the form contains password fields that had user + * typed or manually filled input. + */ +function shouldNotifyAboutFormReset_(form) { + for (let i = 0; i < form.elements.length; i++) { + const element = form.elements[i]; + if (isPasswordField_(element) && + __gCrWeb.form.wasEditedByUser.get(element)) { + return true; + } + } + return false; +} + +/** + * Focus, input, change, keyup, blur and reset events for form elements (form + * and input elements) are messaged to the main application for broadcast to * WebStateObservers. * Events will be included in a message to be sent in a future runloop (without * delay). If an event is already scheduled to be sent, it is replaced by |evt|. @@ -87,6 +171,8 @@ function getFullyQualifiedUrl_(originalURL) { * replace it. * Only the events targeting the active element (or the previously active in * case of 'blur') are sent to the main application. + * 'reset' events are sent to the main application only if they are targeting + * a password form that has user input in it. * This is done with a single event handler for each type being added to the * main document element which checks the source element of the event; this * is much easier to manage than adding handlers to individual elements. @@ -98,8 +184,6 @@ function formActivity_(evt) { target.tagName)) { return; } - const value = target.value || ''; - const fieldType = target.type || ''; if (evt.type !== 'blur') { lastFocusedElement = document.activeElement; } @@ -107,21 +191,43 @@ function formActivity_(evt) { __gCrWeb.form.wasEditedByUser !== null) { __gCrWeb.form.wasEditedByUser.set(target, evt.isTrusted); } - if (target !== lastFocusedElement) { + + // Notify FormActivityTabHelper about form reset if the form contains + // password fields that had user typed or manually filled input. + const isPasswordFormReset = target.tagName === 'FORM' && + evt.type === 'reset' && shouldNotifyAboutFormReset_(target); + + if (target !== lastFocusedElement && !isPasswordFormReset) { return; } - __gCrWeb.fill.setUniqueIDIfNeeded(target.form); - __gCrWeb.fill.setUniqueIDIfNeeded(target); - const formUniqueId = __gCrWeb.fill.getUniqueID(target.form); - const fieldUniqueId = __gCrWeb.fill.getUniqueID(target); + const form = target.tagName === 'FORM' ? target : target.form; + const field = target.tagName === 'FORM' ? null : target; + + __gCrWeb.fill.setUniqueIDIfNeeded(form); + const formUniqueId = __gCrWeb.fill.getUniqueID(form); + __gCrWeb.fill.setUniqueIDIfNeeded(field); + const fieldUniqueId = __gCrWeb.fill.getUniqueID(field); + + const fieldType = target.type || ''; + const fieldValue = target.value || ''; + const value = isPasswordFormReset ? + __gCrWeb.stringify(__gCrWeb.passwords.getPasswordFormData(form, window)) : + fieldValue; + const type = isPasswordFormReset ? 'password_form_cleared' : evt.type; + + if ((evt.type === 'change' || evt.type === 'input') && + isPasswordField_(target)) { + trackPasswordField_(evt.target); + } + const msg = { 'command': 'form.activity', - 'formName': __gCrWeb.form.getFormIdentifier(target.form), + 'formName': __gCrWeb.form.getFormIdentifier(form), 'uniqueFormID': formUniqueId, - 'fieldIdentifier': __gCrWeb.form.getFieldIdentifier(target), + 'fieldIdentifier': __gCrWeb.form.getFieldIdentifier(field), 'uniqueFieldID': fieldUniqueId, 'fieldType': fieldType, - 'type': evt.type, + 'type': type, 'value': value, 'hasUserGesture': evt.isTrusted }; @@ -178,6 +284,7 @@ function attachListeners_() { document.addEventListener('blur', formActivity_, true); document.addEventListener('change', formActivity_, true); document.addEventListener('input', formActivity_, true); + document.addEventListener('reset', formActivity_, true); /** * Other events are watched at the bubbling phase as this seems adequate in @@ -276,8 +383,7 @@ __gCrWeb.formHandlers['trackFormMutations'] = function(delay) { const formGone = removedElements.find(function(element) { if (element.tagName.match(/(FORM)/)) { for (let k = 0; k < element.elements.length; k++) { - if (element.elements[k].tagName.match(/(INPUT)/) && - element.elements[k].type === 'password') { + if (isPasswordField_(element.elements[k])) { return true; } } diff --git a/chromium/components/autofill/ios/form_util/unique_id_data_tab_helper_unittest.mm b/chromium/components/autofill/ios/form_util/unique_id_data_tab_helper_unittest.mm index 426c93a7d3b..a44851cecaf 100644 --- a/chromium/components/autofill/ios/form_util/unique_id_data_tab_helper_unittest.mm +++ b/chromium/components/autofill/ios/form_util/unique_id_data_tab_helper_unittest.mm @@ -4,7 +4,7 @@ #include "components/autofill/ios/form_util/unique_id_data_tab_helper.h" -#import "ios/web/public/test/fakes/test_web_state.h" +#import "ios/web/public/test/fakes/fake_web_state.h" #include "testing/gtest/include/gtest/gtest.h" #import "testing/gtest_mac.h" #include "testing/platform_test.h" @@ -16,8 +16,8 @@ // Test fixture for TabIdTabHelper class. class UniqueIDDataTabHelperTest : public PlatformTest { protected: - web::TestWebState first_web_state_; - web::TestWebState second_web_state_; + web::FakeWebState first_web_state_; + web::FakeWebState second_web_state_; }; // Tests that a tab ID is returned for a WebState, and tab ID's are different |