diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-03-12 09:13:00 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-03-16 09:58:26 +0000 |
commit | 03561cae90f1d99b5c54b1ef3be69f10e882b25e (patch) | |
tree | cc5f0958e823c044e7ae51cc0117fe51432abe5e /chromium/components/autofill | |
parent | fa98118a45f7e169f8846086dc2c22c49a8ba310 (diff) | |
download | qtwebengine-chromium-03561cae90f1d99b5c54b1ef3be69f10e882b25e.tar.gz |
BASELINE: Update Chromium to 88.0.4324.208
Change-Id: I3ae87d23e4eff4b4a469685658740a213600c667
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/autofill')
208 files changed, 9495 insertions, 4487 deletions
diff --git a/chromium/components/autofill/android/BUILD.gn b/chromium/components/autofill/android/BUILD.gn index 150a6db3bd8..9f15164b1a8 100644 --- a/chromium/components/autofill/android/BUILD.gn +++ b/chromium/components/autofill/android/BUILD.gn @@ -4,6 +4,7 @@ import("//build/config/android/rules.gni") import("//build/config/locales.gni") +import("//tools/grit/grit_rule.gni") java_strings_grd("autofill_strings_grd") { grd_file = "java/strings/autofill_strings.grd" @@ -20,6 +21,7 @@ android_resources("autofill_java_resources") { "java/res/layout/autofill_dropdown_item_refresh.xml", "java/res/values/colors.xml", "java/res/values/dimens.xml", + "java/res/values/styles.xml", ] deps = [ ":autofill_strings_grd", diff --git a/chromium/components/autofill/android/provider/autofill_provider_android.cc b/chromium/components/autofill/android/provider/autofill_provider_android.cc index a9a7099e5e0..157142cde47 100644 --- a/chromium/components/autofill/android/provider/autofill_provider_android.cc +++ b/chromium/components/autofill/android/provider/autofill_provider_android.cc @@ -86,8 +86,7 @@ void AutofillProviderAndroid::OnQueryFormFieldAutofill( // Focus or field value change will also trigger the query, so it should be // ignored if the form is same. - if (ShouldStartNewSession(handler, form)) - StartNewSession(handler, form, field, bounding_box); + MaybeStartNewSession(handler, form, field, bounding_box); JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); @@ -105,18 +104,24 @@ void AutofillProviderAndroid::OnQueryFormFieldAutofill( } } -bool AutofillProviderAndroid::ShouldStartNewSession( +void AutofillProviderAndroid::MaybeStartNewSession( AutofillHandlerProxy* handler, - const FormData& form) { - // Only start a new session when form or handler is changed, the change of - // handler indicates query from other frame and a new session is needed. - return !IsCurrentlyLinkedForm(form) || !IsCurrentlyLinkedHandler(handler); -} + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box) { + // Don't start a new session when the new form is similar to the old form, the + // new handler is the same as the current handler, and the coordinates of the + // relevant form field haven't changed. + if (form_ && form_->SimilarFormAs(form) && + IsCurrentlyLinkedHandler(handler)) { + size_t index; + if (form_->GetFieldIndex(field, &index) && + handler->driver()->TransformBoundingBoxToViewportCoordinates( + form.fields[index].bounds) == form_->form().fields[index].bounds) { + return; + } + } -void AutofillProviderAndroid::StartNewSession(AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); if (obj.is_null()) @@ -221,8 +226,7 @@ void AutofillProviderAndroid::OnSelectControlDidChange( const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box) { - if (ShouldStartNewSession(handler, form)) - StartNewSession(handler, form, field, bounding_box); + MaybeStartNewSession(handler, form, field, bounding_box); FireFormFieldDidChanged(handler, form, field, bounding_box); } @@ -255,7 +259,8 @@ void AutofillProviderAndroid::OnFormSubmitted(AutofillHandlerProxy* handler, } void AutofillProviderAndroid::OnFocusNoLongerOnForm( - AutofillHandlerProxy* handler) { + AutofillHandlerProxy* handler, + bool had_interacted_form) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!IsCurrentlyLinkedHandler(handler)) return; diff --git a/chromium/components/autofill/android/provider/autofill_provider_android.h b/chromium/components/autofill/android/provider/autofill_provider_android.h index 5ed22d4e6ae..618517ca585 100644 --- a/chromium/components/autofill/android/provider/autofill_provider_android.h +++ b/chromium/components/autofill/android/provider/autofill_provider_android.h @@ -56,7 +56,8 @@ class AutofillProviderAndroid : public AutofillProvider { const FormData& form, bool known_success, mojom::SubmissionSource source) override; - void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler) override; + void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler, + bool had_interacted_form) override; void OnFocusOnFormField(AutofillHandlerProxy* handler, const FormData& form, const FormFieldData& field, @@ -99,13 +100,12 @@ class AutofillProviderAndroid : public AutofillProvider { gfx::RectF ToClientAreaBound(const gfx::RectF& bounding_box); - bool ShouldStartNewSession(AutofillHandlerProxy* handler, - const FormData& form); - - void StartNewSession(AutofillHandlerProxy* handler, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box); + // Starts a new session, but only if |form| or |handler| doesn't match the + // current session. + void MaybeStartNewSession(AutofillHandlerProxy* handler, + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box); void Reset(); diff --git a/chromium/components/autofill/android/provider/form_data_android.h b/chromium/components/autofill/android/provider/form_data_android.h index 8882a788673..96002f62c94 100644 --- a/chromium/components/autofill/android/provider/form_data_android.h +++ b/chromium/components/autofill/android/provider/form_data_android.h @@ -54,7 +54,7 @@ class FormDataAndroid { void ApplyHeuristicFieldType(const FormStructure& form); - const FormData& form_for_testing() { return form_; } + const FormData& form() { return form_; } private: // Same as the form passed in from constructor, but FormFieldData's bounds is 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 b9b4da26a6b..e92fdb19c61 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 @@ -14,11 +14,11 @@ import android.view.autofill.AutofillValue; import androidx.annotation.VisibleForTesting; +import org.chromium.base.CollectionUtil; import org.chromium.base.Log; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Iterator; /** * The class to call Android's AutofillManager. @@ -158,25 +158,9 @@ public class AutofillManagerWrapper { mInputUIObservers.add(new WeakReference<InputUIObserver>(observer)); } - public void removeInputUIObserver(InputUIObserver observer) { - if (observer == null) return; - for (Iterator<WeakReference<InputUIObserver>> i = mInputUIObservers.listIterator(); - i.hasNext();) { - WeakReference<InputUIObserver> o = i.next(); - if (o.get() == null || o.get() == observer) i.remove(); - } - } - @VisibleForTesting public void notifyInputUIChange() { - for (Iterator<WeakReference<InputUIObserver>> i = mInputUIObservers.listIterator(); - i.hasNext();) { - WeakReference<InputUIObserver> o = i.next(); - InputUIObserver observer = o.get(); - if (observer == null) { - i.remove(); - continue; - } + for (InputUIObserver observer : CollectionUtil.strengthen(mInputUIObservers)) { observer.onInputUIShown(); } } 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 1546b3dca28..226f0dc55a7 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 @@ -29,6 +29,7 @@ import org.chromium.base.annotations.NativeMethods; import org.chromium.base.annotations.VerifiesOnO; import org.chromium.base.metrics.ScopedSysTraceEvent; import org.chromium.components.version_info.VersionConstants; +import org.chromium.content_public.browser.RenderCoordinates; import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContentsAccessibility; import org.chromium.ui.DropdownItem; @@ -715,8 +716,16 @@ public class AutofillProvider { return mDatalistPopup; } + private Rect transformToWindowBounds(RectF rect) { + // Refer to crbug.com/1085294 for the reason of offset. + // The current version of Mockito didn't support mock static method, adding extra method so + // the transform can be tested. + return transformToWindowBoundsWithOffsetY( + rect, RenderCoordinates.fromWebContents(mWebContents).getContentOffsetYPixInt()); + } + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - public Rect transformToWindowBounds(RectF rect) { + public Rect transformToWindowBoundsWithOffsetY(RectF rect, int offsetY) { // Convert bounds to device pixel. WindowAndroid windowAndroid = mWebContents.getTopLevelNativeWindow(); DisplayAndroid displayAndroid = windowAndroid.getDisplay(); @@ -726,6 +735,7 @@ public class AutofillProvider { matrix.setScale(dipScale, dipScale); int[] location = new int[2]; mContainerView.getLocationOnScreen(location); + location[1] += offsetY; matrix.postTranslate(location[0], location[1]); matrix.mapRect(bounds); return new Rect( 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 7b017f0c419..5d63ae0630b 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 @@ -106,10 +106,11 @@ public class AutofillProviderTest { @Test public void testTransformToWindowBounds() { RectF source = new RectF(10, 20, 300, 400); - Rect result = mAutofillProvider.transformToWindowBounds(source); + final int offsetY = 10; + Rect result = mAutofillProvider.transformToWindowBoundsWithOffsetY(source, offsetY); assertEquals(10 * EXPECTED_DIP_SCALE + LOCATION_X, result.left, 0); - assertEquals(20 * EXPECTED_DIP_SCALE + LOCATION_Y, result.top, 0); + assertEquals(20 * EXPECTED_DIP_SCALE + LOCATION_Y + offsetY, result.top, 0); assertEquals(300 * EXPECTED_DIP_SCALE + LOCATION_X, result.right, 0); - assertEquals(400 * EXPECTED_DIP_SCALE + LOCATION_Y, result.bottom, 0); + assertEquals(400 * EXPECTED_DIP_SCALE + LOCATION_Y + offsetY, result.bottom, 0); } } diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc index c657114d28a..3c37af33dac 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc @@ -287,8 +287,8 @@ void ContentAutofillDriver::HidePopup() { autofill_handler_->OnHidePopup(); } -void ContentAutofillDriver::FocusNoLongerOnForm() { - autofill_handler_->OnFocusNoLongerOnForm(); +void ContentAutofillDriver::FocusNoLongerOnForm(bool had_interacted_form) { + autofill_handler_->OnFocusNoLongerOnForm(had_interacted_form); } void ContentAutofillDriver::FocusOnFormField(const FormData& form, diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h index 1a8df440236..efa287202f4 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver.h @@ -128,7 +128,7 @@ class ContentAutofillDriver : public AutofillDriver, const gfx::RectF& bounding_box, bool autoselect_first_suggestion) override; void HidePopup() override; - void FocusNoLongerOnForm() override; + void FocusNoLongerOnForm(bool had_interacted_form) override; void FocusOnFormField(const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box) override; 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 1906bc92438..3862c76d670 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc @@ -25,7 +25,6 @@ #include "content/public/browser/ssl_status.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" -#include "content/public/common/frame_navigate_params.h" #include "content/public/test/mock_navigation_handle.h" #include "content/public/test/test_renderer_host.h" #include "mojo/public/cpp/bindings/associated_receiver_set.h" diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.cc b/chromium/components/autofill/content/browser/risk/fingerprint.cc index e23505a78d0..994561d81ae 100644 --- a/chromium/components/autofill/content/browser/risk/fingerprint.cc +++ b/chromium/components/autofill/content/browser/risk/fingerprint.cc @@ -20,7 +20,7 @@ #include "base/cpu.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "base/scoped_observer.h" +#include "base/scoped_observation.h" #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "base/system/sys_info.h" @@ -221,8 +221,9 @@ class FingerprintDataLoader : public content::GpuDataManagerObserver { // Ensures that any observer registrations for the GPU data are cleaned up by // the time this object is destroyed. - ScopedObserver<content::GpuDataManager, content::GpuDataManagerObserver> - gpu_observer_{this}; + base::ScopedObservation<content::GpuDataManager, + content::GpuDataManagerObserver> + gpu_observation_{this}; // Data that will be passed on to the next loading phase. See the comment for // GetFingerprint() for a description of these variables. @@ -295,7 +296,7 @@ FingerprintDataLoader::FingerprintDataLoader( // Load GPU data if needed. if (gpu_data_manager_->GpuAccessAllowed(nullptr) && !gpu_data_manager_->IsEssentialGpuInfoAvailable()) { - gpu_observer_.Add(gpu_data_manager_); + gpu_observation_.Observe(gpu_data_manager_); OnGpuInfoUpdate(); } @@ -326,7 +327,8 @@ void FingerprintDataLoader::OnGpuInfoUpdate() { if (!gpu_data_manager_->IsEssentialGpuInfoAvailable()) return; - gpu_observer_.Remove(gpu_data_manager_); + DCHECK(gpu_observation_.IsObservingSource(gpu_data_manager_)); + gpu_observation_.RemoveObservation(); MaybeFillFingerprint(); } diff --git a/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc b/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc index 663ebeaab86..885c1d4206d 100644 --- a/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc +++ b/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc @@ -75,16 +75,17 @@ class AutofillRiskFingerprintTest : public content::ContentBrowserTest { unavailable_screen_bounds_(0, 0, 101, 11) {} void SetUpOnMainThread() override { - device::mojom::Geoposition position; - position.latitude = kLatitude; - position.longitude = kLongitude; - position.altitude = kAltitude; - position.accuracy = kAccuracy; - position.timestamp = base::Time::UnixEpoch() + - base::TimeDelta::FromMilliseconds(kGeolocationTime); + auto position = device::mojom::Geoposition::New(); + position->latitude = kLatitude; + position->longitude = kLongitude; + position->altitude = kAltitude; + position->accuracy = kAccuracy; + position->timestamp = base::Time::UnixEpoch() + + base::TimeDelta::FromMilliseconds(kGeolocationTime); geolocation_overrider_ = - std::make_unique<device::ScopedGeolocationOverrider>(position); + std::make_unique<device::ScopedGeolocationOverrider>( + std::move(position)); } void GetFingerprintTestCallback(base::OnceClosure continuation_callback, diff --git a/chromium/components/autofill/content/common/mojom/autofill_agent.mojom b/chromium/components/autofill/content/common/mojom/autofill_agent.mojom index 28b90f991f2..28e6bd73674 100644 --- a/chromium/components/autofill/content/common/mojom/autofill_agent.mojom +++ b/chromium/components/autofill/content/common/mojom/autofill_agent.mojom @@ -118,7 +118,7 @@ interface PasswordGenerationAgent { // Tells the renderer to find a focused element, and if it is a password field // eligible for generation then to trigger generation by returning // non-empty PasswordGenerationUIData. - UserTriggeredGeneratePassword() => (PasswordGenerationUIData? data); + TriggeredGeneratePassword() => (PasswordGenerationUIData? data); // Tells the renderer that a password can be generated on the fields // identified by |form|. diff --git a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom index 6807ea2b937..106f0d69d37 100644 --- a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom +++ b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom @@ -61,8 +61,12 @@ interface AutofillDriver { // Instructs the browser to hide the Autofill popup if it is open. HidePopup(); - // Sent when the current form is no longer focused. - FocusNoLongerOnForm(); + // Sent when either (a) focus moves off of any form element, or (b) focus + // moves off of the form that the user had previously interacted with to a + // different form. |had_interacted_form| indicates whether there was such a + // previously-interacted form. + // TODO(crbug.com/1140473): Remove need to pass |had_interacted_form|. + FocusNoLongerOnForm(bool had_interacted_form); // Notification that a form field is focused. FocusOnFormField(FormData form, @@ -108,6 +112,10 @@ interface PasswordManagerDriver { // FRAME_DETACHED. SameDocumentNavigation(SubmissionIndicatorEvent submission_indication_event); + // Notification that password form was cleared. This is used as a signal of + // a successful submission for change password forms. + PasswordFormCleared(FormData form_data); + // Sends |log| to browser for displaying to the user. Only strings passed as // an argument to methods overriding SavePasswordProgressLogger::SendLog may // become |log|, because those are guaranteed to be sanitized. diff --git a/chromium/components/autofill/content/renderer/OWNERS b/chromium/components/autofill/content/renderer/OWNERS index 15f8279c885..39dfee96ce7 100644 --- a/chromium/components/autofill/content/renderer/OWNERS +++ b/chromium/components/autofill/content/renderer/OWNERS @@ -1,7 +1,2 @@ -per-file *password*=dvadym@chromium.org -per-file *password*=kolos@chromium.org -per-file *password*=vasilii@chromium.org - -per-file *username*=dvadym@chromium.org -per-file *username*=kolos@chromium.org -per-file *username*=vasilii@chromium.org +per-file *password*=file://components/password_manager/OWNERS +per-file *username*=file://components/password_manager/OWNERS diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc index c7e7044d8b9..95ff56c0a45 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/autofill_agent.cc @@ -40,7 +40,6 @@ #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" -#include "components/autofill/core/common/password_form.h" #include "components/autofill/core/common/password_form_fill_data.h" #include "components/autofill/core/common/save_password_progress_logger.h" #include "content/public/common/content_switches.h" @@ -217,11 +216,9 @@ void AutofillAgent::FocusedElementChanged(const WebElement& element) { HidePopup(); if (element.IsNull()) { - if (!last_interacted_form_.IsNull()) { - // Focus moved away from the last interacted form to somewhere else on - // the page. - GetAutofillDriver()->FocusNoLongerOnForm(); - } + // Focus moved away from the last interacted form (if any) to somewhere else + // on the page. + GetAutofillDriver()->FocusNoLongerOnForm(!last_interacted_form_.IsNull()); return; } @@ -232,7 +229,7 @@ void AutofillAgent::FocusedElementChanged(const WebElement& element) { (!input || last_interacted_form_ != input->Form())) { // The focused element is not part of the last interacted form (could be // in a different form). - GetAutofillDriver()->FocusNoLongerOnForm(); + GetAutofillDriver()->FocusNoLongerOnForm(/*had_interacted_form=*/true); focus_moved_to_new_form = true; } @@ -930,6 +927,14 @@ bool AutofillAgent::ShouldSuppressKeyboard( autofill_assistant_agent_->ShouldSuppressKeyboard()); } +void AutofillAgent::FormElementReset(const WebFormElement& form) { + password_autofill_agent_->InformAboutFormClearing(form); +} + +void AutofillAgent::PasswordFieldReset(const WebInputElement& element) { + password_autofill_agent_->InformAboutFieldClearing(element); +} + void AutofillAgent::SelectWasUpdated( const blink::WebFormControlElement& element) { // Look for the form and field associated with the select element. If they are @@ -1106,6 +1111,11 @@ void AutofillAgent::RemoveFormObserver(Observer* observer) { form_tracker_.RemoveObserver(observer); } +void AutofillAgent::TrackAutofilledElement( + const blink::WebFormControlElement& element) { + form_tracker_.TrackAutofilledElement(element); +} + base::Optional<FormData> AutofillAgent::GetSubmittedForm() const { if (!last_interacted_form_.IsNull()) { FormData form; diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h index b88c2b490e7..a6d4cbeb9ec 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.h +++ b/chromium/components/autofill/content/renderer/autofill_agent.h @@ -34,6 +34,7 @@ namespace blink { class WebNode; class WebView; class WebFormControlElement; +class WebFormElement; template <typename T> class WebVector; } // namespace blink @@ -118,6 +119,9 @@ class AutofillAgent : public content::RenderFrameObserver, void AddFormObserver(Observer* observer); void RemoveFormObserver(Observer* observer); + // Instructs `form_tracker_` to track the autofilled `element`. + void TrackAutofilledElement(const blink::WebFormControlElement& element); + FormTracker* form_tracker_for_testing() { return &form_tracker_; } void SelectWasUpdated(const blink::WebFormControlElement& element); @@ -191,6 +195,8 @@ class AutofillAgent : public content::RenderFrameObserver, const blink::WebFormControlElement& element) override; bool ShouldSuppressKeyboard( const blink::WebFormControlElement& element) override; + void FormElementReset(const blink::WebFormElement& form) override; + void PasswordFieldReset(const blink::WebInputElement& element) override; void HandleFocusChangeComplete(); diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc index 2ba2414738c..f42cf808f28 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc @@ -28,7 +28,6 @@ #include "build/build_config.h" #include "components/autofill/core/common/autofill_data_validation.h" #include "components/autofill/core/common/autofill_features.h" -#include "components/autofill/core/common/autofill_regexes.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/field_data_manager.h" diff --git a/chromium/components/autofill/content/renderer/form_cache.cc b/chromium/components/autofill/content/renderer/form_cache.cc index a64c24aa9ae..a4aca1d571a 100644 --- a/chromium/components/autofill/content/renderer/form_cache.cc +++ b/chromium/components/autofill/content/renderer/form_cache.cc @@ -105,9 +105,9 @@ bool IsFormInteresting(const FormData& form, size_t num_editable_elements) { // If there are no autocomplete attributes, the form needs to have at least // the required number of editable fields for the prediction routines to be a // candidate for autofill. - return num_editable_elements >= MinRequiredFieldsForHeuristics() || - num_editable_elements >= MinRequiredFieldsForQuery() || - num_editable_elements >= MinRequiredFieldsForUpload() || + return num_editable_elements >= kMinRequiredFieldsForHeuristics || + num_editable_elements >= kMinRequiredFieldsForQuery || + num_editable_elements >= kMinRequiredFieldsForUpload || (all_fields_are_passwords && num_editable_elements >= kRequiredFieldsForFormsWithOnlyPasswordFields); diff --git a/chromium/components/autofill/content/renderer/form_tracker.cc b/chromium/components/autofill/content/renderer/form_tracker.cc index f4f99702c69..0afae7d2a75 100644 --- a/chromium/components/autofill/content/renderer/form_tracker.cc +++ b/chromium/components/autofill/content/renderer/form_tracker.cc @@ -106,6 +106,21 @@ void FormTracker::SelectControlDidChange(const WebFormControlElement& element) { Observer::ElementChangeSource::SELECT_CHANGED)); } +void FormTracker::TrackAutofilledElement(const WebFormControlElement& element) { + DCHECK_CALLED_ON_VALID_SEQUENCE(form_tracker_sequence_checker_); + DCHECK(element.IsAutofilled()); + + if (ignore_control_changes_) + return; + + ResetLastInteractedElements(); + if (element.Form().IsNull()) + last_interacted_formless_element_ = element; + else + last_interacted_form_ = element.Form(); + TrackElement(); +} + void FormTracker::FireProbablyFormSubmittedForTesting() { FireProbablyFormSubmitted(); } diff --git a/chromium/components/autofill/content/renderer/form_tracker.h b/chromium/components/autofill/content/renderer/form_tracker.h index 7498871c373..ea0e7ca31aa 100644 --- a/chromium/components/autofill/content/renderer/form_tracker.h +++ b/chromium/components/autofill/content/renderer/form_tracker.h @@ -73,6 +73,11 @@ class FormTracker : public content::RenderFrameObserver { void TextFieldDidChange(const blink::WebFormControlElement& element); void SelectControlDidChange(const blink::WebFormControlElement& element); + // Tells the tracker to track the autofilled `element`. Since autofilling a + // form or field won't trigger the regular *DidChange events, the tracker + // won't be notified of this `element` otherwise. + void TrackAutofilledElement(const blink::WebFormControlElement& element); + void set_ignore_control_changes(bool ignore_control_changes) { ignore_control_changes_ = ignore_control_changes; } diff --git a/chromium/components/autofill/content/renderer/html_based_username_detector.h b/chromium/components/autofill/content/renderer/html_based_username_detector.h index 5a32cb4a353..ef1c22a4b90 100644 --- a/chromium/components/autofill/content/renderer/html_based_username_detector.h +++ b/chromium/components/autofill/content/renderer/html_based_username_detector.h @@ -8,7 +8,7 @@ #include <map> #include <vector> -#include "components/autofill/core/common/password_form.h" +#include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/renderer_id.h" #include "third_party/blink/public/web/web_form_control_element.h" #include "third_party/blink/public/web/web_input_element.h" diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc index 7e4b3154b54..83a970ccad2 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc @@ -570,6 +570,11 @@ void PasswordAutofillAgent::UpdateStateForTextChange( } } +void PasswordAutofillAgent::TrackAutofilledElement( + const blink::WebFormControlElement& element) { + autofill_agent_->TrackAutofilledElement(element); +} + bool PasswordAutofillAgent::FillSuggestion( const WebFormControlElement& control_element, const base::string16& username, @@ -640,6 +645,7 @@ void PasswordAutofillAgent::FillField(WebInputElement* input, const FieldRendererId input_id(input->UniqueRendererFormControlId()); field_data_manager_->UpdateFieldDataMap( input_id, credential, FieldPropertiesFlags::kAutofilledOnUserTrigger); + TrackAutofilledElement(*input); } void PasswordAutofillAgent::FillPasswordFieldAndSave( @@ -797,19 +803,6 @@ bool PasswordAutofillAgent::ShouldSuppressKeyboard() { bool PasswordAutofillAgent::TryToShowTouchToFill( const WebFormControlElement& control_element) { - // Don't show Touch To Fill if it should only be enabled for insecure origins - // and we are currently on a potentially trustworthy origin. - if (base::GetFieldTrialParamByFeatureAsBool(features::kAutofillTouchToFill, - "insecure-origins-only", - /*default_value=*/false) && - render_frame() - ->GetWebFrame() - ->GetDocument() - .GetSecurityOrigin() - .IsPotentiallyTrustworthy()) { - return false; - } - if (touch_to_fill_state_ != TouchToFillState::kShouldShow) return false; @@ -1380,6 +1373,51 @@ PasswordAutofillAgent::GetFormDataFromUnownedInputElements() { &button_titles_cache_); } +void PasswordAutofillAgent::InformAboutFormClearing( + const WebFormElement& form) { + if (!FrameCanAccessPasswordManager()) + return; + for (const auto& element : form.GetFormControlElements()) { + FieldRendererId element_id(element.UniqueRendererFormControlId()); + // Notify PasswordManager if |form| has password fields that have user typed + // input or input autofilled on user trigger. + if (IsPasswordFieldFilledByUser(element)) { + NotifyPasswordManagerAboutClearedForm(form); + return; + } + } +} + +void PasswordAutofillAgent::InformAboutFieldClearing( + const WebInputElement& cleared_element) { + if (!FrameCanAccessPasswordManager()) + return; + DCHECK(cleared_element.Value().IsEmpty()); + FieldRendererId field_id(cleared_element.UniqueRendererFormControlId()); + // Ignore fields that had no user input or autofill on user trigger. + if (!field_data_manager_->DidUserType(field_id) && + !field_data_manager_->WasAutofilledOnUserTrigger(field_id)) { + return; + } + + WebFormElement form = cleared_element.Form(); + if (form.IsNull()) { + // Process password field clearing for fields outside the <form> tag. + if (auto unowned_form_data = GetFormDataFromUnownedInputElements()) + GetPasswordManagerDriver()->PasswordFormCleared(*unowned_form_data); + return; + } + // Process field clearing for a form under a <form> tag. + // Only notify PasswordManager in case all user filled password fields were + // cleared. + bool cleared_all_password_fields = base::ranges::all_of( + form.GetFormControlElements(), [this](const auto& el) { + return !IsPasswordFieldFilledByUser(el) || el.Value().IsEmpty(); + }); + if (cleared_all_password_fields) + NotifyPasswordManagerAboutClearedForm(form); +} + //////////////////////////////////////////////////////////////////////////////// // PasswordAutofillAgent, private: @@ -1868,4 +1906,24 @@ bool PasswordAutofillAgent::CanShowPopupWithoutPasswords( IsElementEditable(password_element); } +bool PasswordAutofillAgent::IsPasswordFieldFilledByUser( + const WebFormControlElement& element) const { + FieldRendererId element_id(element.UniqueRendererFormControlId()); + return element.FormControlTypeForAutofill() == "password" && + (field_data_manager_->DidUserType(element_id) || + field_data_manager_->WasAutofilledOnUserTrigger(element_id)); +} + +void PasswordAutofillAgent::NotifyPasswordManagerAboutClearedForm( + const WebFormElement& cleared_form) { + const auto extract_mask = static_cast<form_util::ExtractMask>( + form_util::EXTRACT_VALUE | form_util::EXTRACT_OPTIONS); + FormData form_data; + if (WebFormElementToFormData(cleared_form, WebFormControlElement(), + field_data_manager_.get(), extract_mask, + &form_data, nullptr)) { + GetPasswordManagerDriver()->PasswordFormCleared(form_data); + } +} + } // namespace autofill diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h index 3738a1fc7b2..040f7aecada 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.h +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h @@ -24,7 +24,6 @@ #include "components/autofill/content/renderer/html_based_username_detector.h" #include "components/autofill/core/common/field_data_manager.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" -#include "components/autofill/core/common/password_form.h" #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" @@ -163,6 +162,9 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, // shown. void UpdateStateForTextChange(const blink::WebInputElement& element); + // Instructs `autofill_agent_` to track the autofilled `element`. + void TrackAutofilledElement(const blink::WebFormControlElement& element); + // Fills the username and password fields of this form with the given values. // Returns true if the fields were filled, false otherwise. bool FillSuggestion(const blink::WebFormControlElement& control_element, @@ -221,6 +223,16 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, std::unique_ptr<FormData> GetFormDataFromUnownedInputElements(); + // Notification that form element was cleared by HTMLFormElement::reset() + // method. This can be used as a signal of a successful submission for change + // password forms. + void InformAboutFormClearing(const blink::WebFormElement& form); + + // Notification that input element was cleared by HTMLInputValue::SetValue() + // method by setting an empty value. This can be used as a signal of a + // successful submission for change password forms. + void InformAboutFieldClearing(const blink::WebInputElement& element); + bool logging_state_active() const { return logging_state_active_; } // Determine whether the current frame is allowed to access the password @@ -465,6 +477,15 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, bool CanShowPopupWithoutPasswords( const blink::WebInputElement& password_element) const; + // Returns true if the element is of type 'password' and has either user typed + // input or input autofilled on user trigger. + bool IsPasswordFieldFilledByUser( + const blink::WebFormControlElement& element) const; + + // Extracts and sends the form data of |cleared_form| to PasswordManager. + void NotifyPasswordManagerAboutClearedForm( + const blink::WebFormElement& cleared_form); + // The logins we have filled so far with their associated info. WebInputToPasswordInfoMap web_input_to_password_info_; // A (sort-of) reverse map to |web_input_to_password_info_|. diff --git a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc index 93824cae0bd..81cea7d92e2 100644 --- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc +++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc @@ -10,7 +10,6 @@ #include "base/strings/string_piece.h" #include "base/strings/string_split.h" #include "components/autofill/content/renderer/html_based_username_detector.h" -#include "components/autofill/core/common/password_form.h" #include "components/autofill/core/common/renderer_id.h" #include "google_apis/gaia/gaia_urls.h" #include "net/base/url_util.h" diff --git a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h index 07a2ad05095..1685530ea03 100644 --- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h +++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h @@ -14,7 +14,6 @@ #include "base/strings/string_piece.h" #include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/content/renderer/html_based_username_detector.h" -#include "components/autofill/core/common/password_form.h" #include "third_party/blink/public/platform/web_string.h" #include "url/gurl.h" diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc index a87d298ca38..dea5a37779b 100644 --- a/chromium/components/autofill/content/renderer/password_generation_agent.cc +++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc @@ -19,7 +19,6 @@ #include "components/autofill/content/renderer/password_form_conversion_utils.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/form_data.h" -#include "components/autofill/core/common/password_form.h" #include "components/autofill/core/common/password_form_generation_data.h" #include "components/autofill/core/common/password_generation_util.h" #include "components/autofill/core/common/signatures.h" @@ -232,6 +231,7 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted( if (!render_frame()) return; password_element.SetAutofillState(WebAutofillState::kAutofilled); + password_agent_->TrackAutofilledElement(password_element); // Advance focus to the next input field. We assume password fields in // an account creation form are always adjacent. render_frame()->GetRenderView()->GetWebView()->AdvanceFocus(false); @@ -290,10 +290,10 @@ void PasswordGenerationAgent::FoundFormEligibleForGeneration( } } -void PasswordGenerationAgent::UserTriggeredGeneratePassword( - UserTriggeredGeneratePasswordCallback callback) { - if (SetUpUserTriggeredGeneration()) { - LogMessage(Logger::STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP); +void PasswordGenerationAgent::TriggeredGeneratePassword( + TriggeredGeneratePasswordCallback callback) { + if (SetUpTriggeredGeneration()) { + LogMessage(Logger::STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP); // If the field is not |type=password|, the list of suggestions // should not be populated with passwords to avoid filling them in a // clear-text field. @@ -320,7 +320,7 @@ void PasswordGenerationAgent::UserTriggeredGeneratePassword( } } -bool PasswordGenerationAgent::SetUpUserTriggeredGeneration() { +bool PasswordGenerationAgent::SetUpTriggeredGeneration() { if (last_focused_password_element_.IsNull() || !render_frame()) return false; @@ -493,6 +493,11 @@ bool PasswordGenerationAgent::TextDidChangeInTextField( *presaved_form_data, generated_password); } } + + // Notify `password_agent_` of text changes to the other confirmation + // password fields. + for (const auto& element : current_generation_item_->password_elements_) + password_agent_->UpdateStateForTextChange(element); } return true; } diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.h b/chromium/components/autofill/content/renderer/password_generation_agent.h index 2eeb0e3044e..37fb76215b0 100644 --- a/chromium/components/autofill/content/renderer/password_generation_agent.h +++ b/chromium/components/autofill/content/renderer/password_generation_agent.h @@ -59,8 +59,8 @@ class PasswordGenerationAgent : public content::RenderFrameObserver, const PasswordFormGenerationData& form) override; // Sets |generation_element_| to the focused password field and responds back // if the generation was triggered successfully. - void UserTriggeredGeneratePassword( - UserTriggeredGeneratePasswordCallback callback) override; + void TriggeredGeneratePassword( + TriggeredGeneratePasswordCallback callback) override; // Returns true if the field being changed is one where a generated password // is being offered. Updates the state of the popup if necessary. @@ -110,7 +110,7 @@ class PasswordGenerationAgent : public content::RenderFrameObserver, // Helper function which takes care of the form processing and collecting the // information which is required to show the generation popup. Returns true if // all required information is collected. - bool SetUpUserTriggeredGeneration(); + bool SetUpTriggeredGeneration(); // This is called whenever automatic generation could be offered. // If manual generation was already requested, automatic generation will diff --git a/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc b/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc index 6c76bdbd609..a747fd5924f 100644 --- a/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc +++ b/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc @@ -55,6 +55,8 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver { void SameDocumentNavigation(autofill::mojom::SubmissionIndicatorEvent submission_indication_event) override {} + void PasswordFormCleared(const autofill::FormData& form_data) override {} + void ShowPasswordSuggestions(base::i18n::TextDirection text_direction, const base::string16& typed_username, int options, diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn index 3447ad7a303..b0dcbce1521 100644 --- a/chromium/components/autofill/core/browser/BUILD.gn +++ b/chromium/components/autofill/core/browser/BUILD.gn @@ -25,6 +25,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_rewriter.cc", "address_rewriter.h", "autocomplete_history_manager.cc", @@ -71,6 +73,10 @@ static_library("browser") { "autofill_profile_validator.h", "autofill_provider.cc", "autofill_provider.h", + "autofill_regex_constants.cc", + "autofill_regex_constants.h", + "autofill_regexes.cc", + "autofill_regexes.h", "autofill_subject.cc", "autofill_subject.h", "autofill_type.cc", @@ -151,6 +157,10 @@ static_library("browser") { "form_types.h", "geo/address_i18n.cc", "geo/address_i18n.h", + "geo/alternative_state_name_map.cc", + "geo/alternative_state_name_map.h", + "geo/alternative_state_name_map_updater.cc", + "geo/alternative_state_name_map_updater.h", "geo/autofill_country.cc", "geo/autofill_country.h", "geo/country_data.cc", @@ -183,6 +193,8 @@ static_library("browser") { "metrics/form_event_logger_base.cc", "metrics/form_event_logger_base.h", "metrics/form_events.h", + "pattern_provider/pattern_configuration_parser.cc", + "pattern_provider/pattern_configuration_parser.h", "pattern_provider/pattern_provider.cc", "pattern_provider/pattern_provider.h", "payments/account_info_getter.h", @@ -390,6 +402,7 @@ static_library("browser") { "//crypto", "//google_apis", "//net", + "//services/data_decoder/public/cpp:cpp", "//services/metrics/public/cpp:metrics_cpp", "//services/metrics/public/cpp:ukm_builders", "//services/network/public/cpp", @@ -440,12 +453,16 @@ static_library("test_support") { "autofill_test_utils.h", "data_driven_test.cc", "data_driven_test.h", + "geo/alternative_state_name_map_test_utils.cc", + "geo/alternative_state_name_map_test_utils.h", "geo/test_region_data_loader.cc", "geo/test_region_data_loader.h", "logging/stub_log_manager.cc", "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", @@ -576,6 +593,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_rewriter_unittest.cc", "autocomplete_history_manager_unittest.cc", "autofill_address_policy_handler_unittest.cc", @@ -591,6 +609,7 @@ source_set("unit_tests") { "autofill_profile_sync_util_unittest.cc", "autofill_profile_validation_util_unittest.cc", "autofill_profile_validator_unittest.cc", + "autofill_regexes_unittest.cc", "autofill_subject_unittest.cc", "autofill_type_unittest.cc", "data_model/address_unittest.cc", @@ -620,6 +639,8 @@ source_set("unit_tests") { "form_parsing/search_field_unittest.cc", "form_structure_unittest.cc", "geo/address_i18n_unittest.cc", + "geo/alternative_state_name_map_unittest.cc", + "geo/alternative_state_name_map_updater_unittest.cc", "geo/autofill_country_unittest.cc", "geo/country_names_for_locale_unittest.cc", "geo/country_names_unittest.cc", @@ -628,6 +649,7 @@ source_set("unit_tests") { "logging/log_buffer_submitter_unittest.cc", "logging/log_manager_unittest.cc", "logging/log_router_unittest.cc", + "pattern_provider/pattern_configuration_parser_unittest.cc", "pattern_provider/pattern_provider_unittest.cc", "payments/autofill_offer_manager_unittest.cc", "payments/credit_card_access_manager_unittest.cc", @@ -727,6 +749,7 @@ source_set("unit_tests") { "//google_apis", "//google_apis:test_support", "//net:test_support", + "//services/data_decoder/public/cpp:test_support", "//services/metrics/public/cpp:ukm_builders", "//services/network:test_support", "//services/network/public/cpp", diff --git a/chromium/components/autofill/core/browser/OWNERS b/chromium/components/autofill/core/browser/OWNERS index c23e8389686..711aa7cc667 100644 --- a/chromium/components/autofill/core/browser/OWNERS +++ b/chromium/components/autofill/core/browser/OWNERS @@ -1,4 +1,2 @@ -parastoog@google.com - per-file *type_controller*=jkrcal@chromium.org per-file *type_controller*=file://components/sync/OWNERS diff --git a/chromium/components/autofill/core/browser/address_normalization_manager.cc b/chromium/components/autofill/core/browser/address_normalization_manager.cc index a83ef900e07..708572b38d4 100644 --- a/chromium/components/autofill/core/browser/address_normalization_manager.cc +++ b/chromium/components/autofill/core/browser/address_normalization_manager.cc @@ -7,7 +7,7 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/address_normalizer.h" diff --git a/chromium/components/autofill/core/browser/address_normalization_manager_unittest.cc b/chromium/components/autofill/core/browser/address_normalization_manager_unittest.cc index b8a775340c2..e7f89b09415 100644 --- a/chromium/components/autofill/core/browser/address_normalization_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/address_normalization_manager_unittest.cc @@ -7,7 +7,7 @@ #include <memory> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "components/autofill/core/browser/test_address_normalizer.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/components/autofill/core/browser/address_normalizer_impl.cc b/chromium/components/autofill/core/browser/address_normalizer_impl.cc index 85ff881f2fa..335589e92bf 100644 --- a/chromium/components/autofill/core/browser/address_normalizer_impl.cc +++ b/chromium/components/autofill/core/browser/address_normalizer_impl.cc @@ -8,7 +8,7 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/cancelable_callback.h" #include "base/check_op.h" #include "base/location.h" diff --git a/chromium/components/autofill/core/browser/address_normalizer_impl_unittest.cc b/chromium/components/autofill/core/browser/address_normalizer_impl_unittest.cc index a3c5cfe306a..dc3206e2822 100644 --- a/chromium/components/autofill/core/browser/address_normalizer_impl_unittest.cc +++ b/chromium/components/autofill/core/browser/address_normalizer_impl_unittest.cc @@ -7,7 +7,7 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/strings/utf_string_conversions.h" #include "base/test/task_environment.h" #include "components/autofill/core/browser/address_normalizer.h" 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 new file mode 100644 index 00000000000..4081c68239a --- /dev/null +++ b/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager.cc @@ -0,0 +1,25 @@ +// 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 new file mode 100644 index 00000000000..69ac2d01511 --- /dev/null +++ b/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager.h @@ -0,0 +1,39 @@ +// 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 new file mode 100644 index 00000000000..b73b0dc35e5 --- /dev/null +++ b/chromium/components/autofill/core/browser/address_profiles/address_profile_save_manager_unittest.cc @@ -0,0 +1,34 @@ +// 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/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html index 2eb1f5540ac..83dbe8e0995 100644 --- a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html +++ b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html @@ -5,6 +5,7 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> <meta charset="utf-8"> +<script src="chrome://resources/js/assert.js"></script> <script src="chrome://resources/js/util.js"></script> <script src="autofill_and_password_manager_internals.js"></script> <link rel="stylesheet" href="chrome://resources/css/chrome_shared.css"> diff --git a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html index d48f3884ed1..918dd3bfa40 100644 --- a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html +++ b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html @@ -10,6 +10,7 @@ injected by web. --> <script src="chrome://resources/js/ios/web_ui.js"></script> +<script src="chrome://resources/js/assert.js"></script> <script src="chrome://resources/js/util.js"></script> <script src="autofill_and_password_manager_internals.js"></script> <link rel="stylesheet" href="chrome://resources/css/chrome_shared.css"> diff --git a/chromium/components/autofill/core/browser/autofill_data_util.cc b/chromium/components/autofill/core/browser/autofill_data_util.cc index 21daf5d37ac..d8b5520cfc4 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util.cc +++ b/chromium/components/autofill/core/browser/autofill_data_util.cc @@ -185,8 +185,7 @@ bool IsHangulCharacter(UChar32 c) { // characters or spaces. |name| should already be confirmed to be a CJK name, as // per |IsCJKName()|. bool IsHangulName(base::StringPiece16 name) { - for (base::i18n::UTF16CharIterator iter(name.data(), name.length()); - !iter.end(); iter.Advance()) { + for (base::i18n::UTF16CharIterator iter(name); !iter.end(); iter.Advance()) { UChar32 c = iter.get(); if (!IsHangulCharacter(c) && !base::IsUnicodeWhitespace(c)) { return false; @@ -367,8 +366,7 @@ bool IsCJKName(base::StringPiece16 name) { static const base::char16 kMiddleDot = u'\u00B7'; bool previous_was_cjk = false; size_t word_count = 0; - for (base::i18n::UTF16CharIterator iter(name.data(), name.length()); - !iter.end(); iter.Advance()) { + for (base::i18n::UTF16CharIterator iter(name); !iter.end(); iter.Advance()) { UChar32 c = iter.get(); const bool is_cjk = IsCJKCharacter(c); if (!is_cjk && !base::IsUnicodeWhitespace(c) && c != kKatakanaMiddleDot && 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 74b0009b7e3..923f8c9071c 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc @@ -1341,7 +1341,7 @@ class AutofillServerCommunicationTest driver_ = std::make_unique<TestAutofillDriver>(); driver_->SetSharedURLLoaderFactory(shared_url_loader_factory_); driver_->SetIsolationInfo(net::IsolationInfo::Create( - net::IsolationInfo::RedirectMode::kUpdateNothing, + net::IsolationInfo::RequestType::kOther, url::Origin::Create(GURL("https://abc.com")), url::Origin::Create(GURL("https://xyz.com")), net::SiteForCookies())); @@ -2062,8 +2062,7 @@ TEST_P(AutofillUploadTest, ThrottlingDisabled) { // Enabled. {}, // Disabled - {features::kAutofillUploadThrottling, - features::kAutofillEnforceMinRequiredFieldsForUpload}); + {features::kAutofillUploadThrottling}); FormData form; FormData small_form; diff --git a/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc b/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc index e9f6e3c3c50..23b169a68b2 100644 --- a/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc @@ -8,7 +8,7 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/test/task_environment.h" #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/browser/test_autofill_driver.h" diff --git a/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc b/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc index 47c3f1bd4a2..7e0893d3adb 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc @@ -4,7 +4,7 @@ #include "components/autofill/core/browser/autofill_experiments.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_metrics.h" diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc index 5cd990ebb73..6a81dc37934 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc @@ -13,7 +13,6 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/user_action_tester.h" -#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "build/build_config.h" #include "components/autofill/core/browser/autofill_experiments.h" diff --git a/chromium/components/autofill/core/browser/autofill_field.cc b/chromium/components/autofill/core/browser/autofill_field.cc index 376f1b8381f..41610aa4482 100644 --- a/chromium/components/autofill/core/browser/autofill_field.cc +++ b/chromium/components/autofill/core/browser/autofill_field.cc @@ -143,6 +143,27 @@ AutofillType AutofillField::ComputedType() const { believe_server = believe_server && !(AutofillType(server_type_).group() == PASSWORD_FIELD && heuristic_type_ == CREDIT_CARD_VERIFICATION_CODE); + + // For new name tokens the heuristic predictions get precedence over the + // server predictions. + // TODO(crbug.com/1098943): Remove feature check once launched. + believe_server = + believe_server && + !(base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForMoreStructureInNames) && + (heuristic_type_ == NAME_LAST_SECOND || + heuristic_type_ == NAME_LAST_FIRST)); + + // For new address tokens the heuristic predictions get precedence over the + // server predictions. + // TODO(crbug.com/1098943): Remove feature check once launched. + believe_server = + believe_server && + !(base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForMoreStructureInAddresses) && + (heuristic_type_ == ADDRESS_HOME_STREET_NAME || + heuristic_type_ == ADDRESS_HOME_HOUSE_NUMBER)); + if (believe_server) return AutofillType(server_type_); } 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 177004fffb2..f8889cfce9f 100644 --- a/chromium/components/autofill/core/browser/autofill_form_test_utils.cc +++ b/chromium/components/autofill/core/browser/autofill_form_test_utils.cc @@ -48,6 +48,10 @@ FormFieldData CreateFieldByRole(ServerFieldType role) { field.label = ASCIIToUTF16("E-mail address"); field.name = ASCIIToUTF16("email"); break; + case ServerFieldType::ADDRESS_HOME_LINE1: + field.label = ASCIIToUTF16("Address"); + field.name = ASCIIToUTF16("home_line_one"); + break; case ServerFieldType::ADDRESS_HOME_CITY: field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); @@ -84,11 +88,19 @@ FormFieldData CreateFieldByRole(ServerFieldType role) { return field; } -FormData GetFormData(const FormAttributes& form_attributes) { +FormData GetFormData(const TestFormAttributes& test_form_attributes) { FormData form_data; - form_data.url = GURL(form_attributes.form_url); - for (const FieldDataDescription& field_description : form_attributes.fields) { + form_data.url = GURL(test_form_attributes.url); + form_data.action = GURL(test_form_attributes.action); + form_data.name = ASCIIToUTF16(test_form_attributes.name); + static int field_count = 0; + if (test_form_attributes.unique_renderer_id) + form_data.unique_renderer_id = *test_form_attributes.unique_renderer_id; + if (test_form_attributes.main_frame_origin) + form_data.main_frame_origin = *test_form_attributes.main_frame_origin; + for (const FieldDataDescription& field_description : + test_form_attributes.fields) { FormFieldData field = CreateFieldByRole(field_description.role); field.form_control_type = field_description.form_control_type; field.is_focusable = field_description.is_focusable; @@ -98,11 +110,16 @@ FormData GetFormData(const FormAttributes& form_attributes) { field.label = ASCIIToUTF16(field_description.label); if (ASCIIToUTF16(field_description.name) != ASCIIToUTF16(kNameText)) field.name = ASCIIToUTF16(field_description.name); + if (field_description.value) + field.value = ASCIIToUTF16(*field_description.value); + if (field_description.is_autofilled) + field.is_autofilled = *field_description.is_autofilled; + field.unique_renderer_id = FieldRendererId(field_count++); field.should_autocomplete = field_description.should_autocomplete; form_data.fields.push_back(field); } - form_data.is_formless_checkout = form_attributes.is_formless_checkout; - form_data.is_form_tag = form_attributes.is_form_tag; + form_data.is_formless_checkout = test_form_attributes.is_formless_checkout; + form_data.is_form_tag = test_form_attributes.is_form_tag; return form_data; } diff --git a/chromium/components/autofill/core/browser/autofill_form_test_utils.h b/chromium/components/autofill/core/browser/autofill_form_test_utils.h index 6ec1180c09c..0c4c9b936c1 100644 --- a/chromium/components/autofill/core/browser/autofill_form_test_utils.h +++ b/chromium/components/autofill/core/browser/autofill_form_test_utils.h @@ -26,7 +26,10 @@ constexpr char kLabelText[] = "label"; constexpr char kNameText[] = "name"; // Default form url. -constexpr char kFormUrl[] = "http://www.foo.com/"; +constexpr char kFormUrl[] = "http://example.com/form.html"; + +// Default form action url. +constexpr char kFormActionUrl[] = "http://example.com/submit.html"; } // namespace @@ -39,17 +42,23 @@ struct FieldDataDescription { bool is_focusable = true; const char* label = kLabelText; const char* name = kNameText; + base::Optional<const char*> value = base::nullopt; const char* autocomplete_attribute = nullptr; const char* form_control_type = "text"; bool should_autocomplete = true; + base::Optional<bool> is_autofilled = base::nullopt; }; // Attributes provided to the test form. template <typename = void> -struct FormAttributes { - const char* description_for_logging = ""; - std::vector<FieldDataDescription<>> fields = {}; - const char* form_url = kFormUrl; +struct TestFormAttributes { + const char* description_for_logging; + std::vector<FieldDataDescription<>> fields; + base::Optional<FormRendererId> unique_renderer_id = base::nullopt; + const char* name = "TestForm"; + const char* url = kFormUrl; + const char* action = kFormActionUrl; + base::Optional<url::Origin> main_frame_origin = base::nullopt; bool is_formless_checkout = false; bool is_form_tag = true; }; @@ -57,7 +66,7 @@ struct FormAttributes { // Flags determining whether the corresponding check should be run on the test // form. template <typename = void> -struct FormFlags { +struct TestFormFlags { // false means the function is not to be called. bool determine_heuristic_type = false; bool parse_query_response = false; @@ -90,15 +99,15 @@ struct ExpectedFieldTypeValues { // Describes a test case for the parser. template <typename = void> struct FormStructureTestCase { - FormAttributes<> form_attributes; - FormFlags<> form_flags; + TestFormAttributes<> form_attributes; + TestFormFlags<> form_flags; ExpectedFieldTypeValues<> expected_field_types; }; } // namespace internal using FieldDataDescription = internal::FieldDataDescription<>; -using FormAttributes = internal::FormAttributes<>; +using TestFormAttributes = internal::TestFormAttributes<>; using FormStructureTestCase = internal::FormStructureTestCase<>; // Describes the |form_data|. Use this in SCOPED_TRACE if other logging @@ -109,7 +118,7 @@ testing::Message DescribeFormData(const FormData& form_data); FormFieldData CreateFieldByRole(ServerFieldType role); // Creates a FormData to be fed to the parser. -FormData GetFormData(const FormAttributes& form_attributes); +FormData GetFormData(const TestFormAttributes& test_form_attributes); class FormStructureTest : public testing::Test { protected: diff --git a/chromium/components/autofill/core/browser/autofill_handler.cc b/chromium/components/autofill/core/browser/autofill_handler.cc index c7474a25dfa..b8f647955ae 100644 --- a/chromium/components/autofill/core/browser/autofill_handler.cc +++ b/chromium/components/autofill/core/browser/autofill_handler.cc @@ -30,6 +30,10 @@ const size_t kAutofillHandlerMaxFormCacheSize = 100; // if not found. AutofillField* FindAutofillFillField(const FormStructure& form, const FormFieldData& field) { + for (const auto& f : form) { + if (field.unique_renderer_id == f->unique_renderer_id) + return f.get(); + } for (const auto& cur_field : form) { if (cur_field->SameFieldAs(field)) { return cur_field.get(); @@ -99,17 +103,11 @@ void AutofillHandler::OnFormsSeen(const std::vector<FormData>& forms, // code would have ignored the cache hit we update the FormStructure's // FormSignature. // Otherwise, if the experiment disabled, we just ignore the cache hit. - // - // TODO(crbug.com/1100231) Clean up when experiment is complete. - const bool kOldBehavior = !base::FeatureList::IsEnabled( - features::kAutofillKeepInitialFormValuesInCache); 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; - if (kOldBehavior) - cached_form_structure = nullptr; break; } } @@ -120,7 +118,7 @@ void AutofillHandler::OnFormsSeen(const std::vector<FormData>& forms, continue; DCHECK(form_structure); - if (update_form_signature && !kOldBehavior) + if (update_form_signature) form_structure->set_form_signature(CalculateFormSignature(form)); new_forms.push_back(&form); diff --git a/chromium/components/autofill/core/browser/autofill_handler.h b/chromium/components/autofill/core/browser/autofill_handler.h index d476cd02d5a..18b7d0b42c3 100644 --- a/chromium/components/autofill/core/browser/autofill_handler.h +++ b/chromium/components/autofill/core/browser/autofill_handler.h @@ -89,8 +89,9 @@ class AutofillHandler { void OnFormsSeen(const std::vector<FormData>& forms, const base::TimeTicks timestamp); - // Invoked when focus is no longer on form. - virtual void OnFocusNoLongerOnForm() = 0; + // 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. + virtual void OnFocusNoLongerOnForm(bool had_interacted_form) = 0; // Invoked when |form| has been filled with the value given by // SendFormDataToRenderer. diff --git a/chromium/components/autofill/core/browser/autofill_handler_proxy.cc b/chromium/components/autofill/core/browser/autofill_handler_proxy.cc index 4a917d09f4b..41fd6d405e2 100644 --- a/chromium/components/autofill/core/browser/autofill_handler_proxy.cc +++ b/chromium/components/autofill/core/browser/autofill_handler_proxy.cc @@ -74,8 +74,8 @@ void AutofillHandlerProxy::OnFormsParsed( const std::vector<const FormData*>& form_structures, const base::TimeTicks timestamp) {} -void AutofillHandlerProxy::OnFocusNoLongerOnForm() { - provider_->OnFocusNoLongerOnForm(this); +void AutofillHandlerProxy::OnFocusNoLongerOnForm(bool had_interacted_form) { + provider_->OnFocusNoLongerOnForm(this, had_interacted_form); } void AutofillHandlerProxy::OnDidFillAutofillFormData( diff --git a/chromium/components/autofill/core/browser/autofill_handler_proxy.h b/chromium/components/autofill/core/browser/autofill_handler_proxy.h index 741b30da102..8d2f0287888 100644 --- a/chromium/components/autofill/core/browser/autofill_handler_proxy.h +++ b/chromium/components/autofill/core/browser/autofill_handler_proxy.h @@ -20,7 +20,7 @@ class AutofillHandlerProxy : public AutofillHandler { AutofillProvider* provider); ~AutofillHandlerProxy() override; - void OnFocusNoLongerOnForm() override; + void OnFocusNoLongerOnForm(bool had_interacted_form) override; void OnDidFillAutofillFormData(const FormData& form, const base::TimeTicks timestamp) override; diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc index a9dd2e727dd..9f4f5f3688e 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_manager.cc @@ -451,7 +451,9 @@ AutofillManager::FillingContext::FillingContext( *optional_credit_card, optional_cvc ? *optional_cvc : base::string16())) : base::nullopt), - filled_field_name(field.unique_name()), + 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); @@ -1137,7 +1139,13 @@ void AutofillManager::FillProfileForm(const autofill::AutofillProfile& profile, /*query_id=*/-1, form, field, profile); } -void AutofillManager::OnFocusNoLongerOnForm() { +void AutofillManager::OnFocusNoLongerOnForm(bool had_interacted_form) { + // For historical reasons, Chrome takes action on this message only if focus + // was previously on a form with which the user had interacted. + // TODO(crbug.com/1140473): Remove need for this short-circuit. + if (!had_interacted_form) + return; + ProcessPendingFormForUpload(); #if defined(OS_CHROMEOS) @@ -1607,7 +1615,8 @@ void AutofillManager::Reset() { credit_card_action_ = AutofillDriver::FORM_DATA_ACTION_PREVIEW; initial_interaction_timestamp_ = TimeTicks(); external_delegate_->Reset(); - filling_contexts_map_.clear(); + filling_context_by_renderer_id_.clear(); + filling_context_by_unique_name_.clear(); } AutofillManager::AutofillManager( @@ -1739,9 +1748,9 @@ void AutofillManager::FillOrPreviewDataModelForm( DCHECK_EQ(form_structure->field_count(), form.fields.size()); if (action == AutofillDriver::FORM_DATA_ACTION_FILL && !is_refill) { - filling_contexts_map_[form_structure->GetIdentifierForRefill()] = - std::make_unique<FillingContext>(*autofill_field, optional_profile, - optional_credit_card, optional_cvc); + SetFillingContext(*form_structure, std::make_unique<FillingContext>( + *autofill_field, optional_profile, + optional_credit_card, optional_cvc)); } // Only record the types that are filled for an eventual refill if all the @@ -1750,11 +1759,7 @@ void AutofillManager::FillOrPreviewDataModelForm( // A form with the given name is already filled. // A refill has not been attempted for that form yet. // This fill is not a refill attempt. - FillingContext* filling_context = nullptr; - auto itr = - filling_contexts_map_.find(form_structure->GetIdentifierForRefill()); - if (itr != filling_contexts_map_.end()) - filling_context = itr->second.get(); + FillingContext* filling_context = GetFillingContext(*form_structure); bool could_attempt_refill = filling_context != nullptr && !filling_context->attempted_refill && !is_refill; @@ -2094,10 +2099,8 @@ void AutofillManager::OnFormsParsed(const std::vector<const FormData*>& forms, // been a refill attempt on that form yet, start the process of triggering a // refill. if (ShouldTriggerRefill(*form_structure)) { - auto itr = - filling_contexts_map_.find(form_structure->GetIdentifierForRefill()); - DCHECK(itr != filling_contexts_map_.end()); - FillingContext* filling_context = itr->second.get(); + 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. @@ -2464,18 +2467,44 @@ void AutofillManager::FillFieldWithValue(AutofillField* autofill_field, } } +// TODO(crbug/896689): Remove code duplication once experiment is finished. +void AutofillManager::SetFillingContext( + const FormStructure& form, + std::unique_ptr<FillingContext> context) { + if (base::FeatureList::IsEnabled(features::kAutofillRefillWithRendererIds)) { + filling_context_by_renderer_id_[form.unique_renderer_id()] = + std::move(context); + } else { + filling_context_by_unique_name_[form.GetIdentifierForRefill()] = + std::move(context); + } +} + +// TODO(crbug/896689): Remove code duplication once experiment is finished. +AutofillManager::FillingContext* AutofillManager::GetFillingContext( + const FormStructure& form) { + if (base::FeatureList::IsEnabled(features::kAutofillRefillWithRendererIds)) { + auto it = filling_context_by_renderer_id_.find(form.unique_renderer_id()); + return it != filling_context_by_renderer_id_.end() ? it->second.get() + : nullptr; + } else { + auto it = + filling_context_by_unique_name_.find(form.GetIdentifierForRefill()); + return it != filling_context_by_unique_name_.end() ? it->second.get() + : nullptr; + } +} + bool AutofillManager::ShouldTriggerRefill(const FormStructure& form_structure) { - // Should not refill if a form with the same name has not been filled - // before. - auto itr = - filling_contexts_map_.find(form_structure.GetIdentifierForRefill()); - if (itr == filling_contexts_map_.end()) + // Should not refill if a form with the same FormRendererId has not been + // filled before. + FillingContext* filling_context = GetFillingContext(form_structure); + if (filling_context == nullptr) return false; address_form_event_logger_->OnDidSeeFillableDynamicForm(sync_state_, form_structure); - FillingContext* filling_context = itr->second.get(); base::TimeTicks now = AutofillTickClock::NowTicks(); base::TimeDelta delta = now - filling_context->original_fill_time; @@ -2499,16 +2528,14 @@ void AutofillManager::TriggerRefill(const FormData& form) { address_form_event_logger_->OnDidRefill(sync_state_, *form_structure); - auto itr = - filling_contexts_map_.find(form_structure->GetIdentifierForRefill()); + FillingContext* filling_context = GetFillingContext(*form_structure); // Since GetIdentifierForRefill() is not stable across dynamic changes, - // |filling_contexts_map_| may not contain the element anymore. - if (itr == filling_contexts_map_.end()) + // |filling_context_by_unique_name_| may not contain the element anymore. + // TODO(crbug/896689): Can be replaced with DCHECK once experiment is over. + if (filling_context == nullptr) return; - FillingContext* filling_context = itr->second.get(); - // The refill attempt can happen from different paths, some of which happen // after waiting for a while. Therefore, although this condition has been // checked prior to calling TriggerRefill, it may not hold, when we get @@ -2519,14 +2546,44 @@ void AutofillManager::TriggerRefill(const FormData& form) { filling_context->attempted_refill = true; // Try to find the field from which the original field originated. + // Precedence is given to look up by |filled_field_renderer_id|. + // If that is unsuccessful, look up is done by |filled_field_signature|. + // TODO(crbug/896689): Clean up after feature launch. AutofillField* autofill_field = nullptr; for (const std::unique_ptr<AutofillField>& field : *form_structure) { - if (field->unique_name() == filling_context->filled_field_name) { + // TODO(crbug/896689): Clean up once experiment is over. + if ((base::FeatureList::IsEnabled( + features::kAutofillRefillWithRendererIds) && + field->unique_renderer_id == + filling_context->filled_field_renderer_id) || + (!base::FeatureList::IsEnabled( + features::kAutofillRefillWithRendererIds) && + field->unique_name() == filling_context->filled_field_unique_name)) { autofill_field = field.get(); break; } } + // If the field was deleted, look for one with a matching signature. Prefer + // visible and newer fields over invisible or older ones. + if (base::FeatureList::IsEnabled(features::kAutofillRefillWithRendererIds) && + autofill_field == nullptr) { + auto is_better = [](const AutofillField& f, const AutofillField& g) { + return std::forward_as_tuple(f.IsVisible(), + f.unique_renderer_id.value()) > + std::forward_as_tuple(g.IsVisible(), g.unique_renderer_id.value()); + }; + + for (const std::unique_ptr<AutofillField>& field : *form_structure) { + if (field->GetFieldSignature() == + filling_context->filled_field_signature) { + if (autofill_field == nullptr || is_better(*field, *autofill_field)) { + autofill_field = field.get(); + } + } + } + } + // If it was not found cancel the refill. if (!autofill_field) return; diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h index fc6e8bc4714..d2cecaf5e00 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.h +++ b/chromium/components/autofill/core/browser/autofill_manager.h @@ -217,7 +217,7 @@ class AutofillManager : public AutofillHandler, void DidSuppressPopup(const FormData& form, const FormFieldData& field); // AutofillHandler: - void OnFocusNoLongerOnForm() override; + void OnFocusNoLongerOnForm(bool had_interacted_form) override; void OnFocusOnFormFieldImpl(const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box) override; @@ -388,6 +388,9 @@ class AutofillManager : public AutofillHandler, // Exposed for testing. bool is_rich_query_enabled() const { return is_rich_query_enabled_; } + // Exposed for testing. + FormData* pending_form_data() { return pending_form_data_.get(); } + private: // Keeps track of the filling context for a form, used to make refill attemps. struct FillingContext { @@ -406,8 +409,12 @@ class AutofillManager : public AutofillHandler, // empty. const base::Optional<AutofillProfile> profile; const base::Optional<std::pair<CreditCard, base::string16>> credit_card; - // The name of the field that was initially filled. - const base::string16 filled_field_name; + // 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|. + const FieldRendererId filled_field_renderer_id; + const FieldSignature filled_field_signature; + const base::string16 filled_field_unique_name; // The time at which the initial fill occurred. const base::TimeTicks original_fill_time; // The timer used to trigger a refill. @@ -582,7 +589,14 @@ class AutofillManager : public AutofillHandler, uint32_t profile_form_bitmask, std::string* failure_to_fill); - // Whether there should be an attemps to refill the form. Returns true if all + // TODO(crbug/896689): Remove code duplication once experiment is finished. + void SetFillingContext(const FormStructure& form, + std::unique_ptr<FillingContext> context); + + // TODO(crbug/896689): Remove code duplication once experiment is finished. + FillingContext* GetFillingContext(const FormStructure& form); + + // Whether there should be an attempts to refill the form. Returns true if all // the following are satisfied: // There have been no refill on that page yet. // A non empty form name was recorded in a previous fill @@ -618,6 +632,7 @@ class AutofillManager : public AutofillHandler, void SetDataList(const std::vector<base::string16>& values, const std::vector<base::string16>& labels); + AutofillClient* const client_; LogManager* log_manager_; @@ -714,8 +729,11 @@ class AutofillManager : public AutofillHandler, // A map of form names to FillingContext instances used to make refill // attempts for dynamic forms. + // TODO(crbug/896689): Remove code duplication once experiment is finished. + std::map<FormRendererId, std::unique_ptr<FillingContext>> + filling_context_by_renderer_id_; std::map<base::string16, std::unique_ptr<FillingContext>> - filling_contexts_map_; + filling_context_by_unique_name_; // Tracks whether or not rich query encoding is enabled for this client. const bool is_rich_query_enabled_ = false; diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc index 7d296d95b25..d801bda0abb 100644 --- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc @@ -37,6 +37,7 @@ #include "components/autofill/core/browser/data_model/credit_card.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" @@ -94,9 +95,6 @@ using testing::UnorderedElementsAre; namespace autofill { -using features::kAutofillEnforceMinRequiredFieldsForHeuristics; -using features::kAutofillEnforceMinRequiredFieldsForQuery; -using features::kAutofillEnforceMinRequiredFieldsForUpload; using features::kAutofillRestrictUnownedFieldsToFormlessCheckout; using mojom::SubmissionIndicatorEvent; using mojom::SubmissionSource; @@ -637,6 +635,7 @@ class AutofillManagerTest : public testing::Test { 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) { @@ -953,9 +952,6 @@ TEST_P(AutofillManagerStructuredProfileTest, // them have an autocomplete attribute. TEST_P(AutofillManagerStructuredProfileTest, GetProfileSuggestions_MinFieldsEnforced_NoAutocomplete) { - base::test::ScopedFeatureList features; - features.InitAndEnableFeature( - features::kAutofillEnforceMinRequiredFieldsForHeuristics); // Set up our form data. FormData form; form.name = ASCIIToUTF16("MyForm"); @@ -987,9 +983,6 @@ TEST_P(AutofillManagerStructuredProfileTest, // suggestions are only made for the one that has the attribute. TEST_P(AutofillManagerStructuredProfileTest, GetProfileSuggestions_MinFieldsEnforced_WithOneAutocomplete) { - base::test::ScopedFeatureList features; - features.InitAndEnableFeature( - features::kAutofillEnforceMinRequiredFieldsForHeuristics); // Set up our form data. FormData form; form.name = ASCIIToUTF16("MyForm"); @@ -1018,86 +1011,11 @@ TEST_P(AutofillManagerStructuredProfileTest, EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen()); } -// Test that suggestions are returned by default when there are less than -// three fields and none of them have an autocomplete attribute. -TEST_P(AutofillManagerStructuredProfileTest, - GetProfileSuggestions_NoMinFieldsEnforced_NoAutocomplete) { - base::test::ScopedFeatureList features; - features.InitAndDisableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - // Set up our form data. - FormData form; - form.name = ASCIIToUTF16("MyForm"); - 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); - form.fields.push_back(field); - test::CreateTestFormField("Last Name", "lastname", "", "text", &field); - form.fields.push_back(field); - - std::vector<FormData> forms(1, form); - FormsSeen(forms); - - // Ensure that autocomplete manager is called for both fields. - EXPECT_CALL(*(autocomplete_history_manager_.get()), - OnGetAutocompleteSuggestions) - .Times(0); - - GetAutofillSuggestions(form, form.fields[0]); - CheckSuggestions(kDefaultPageID, - Suggestion("Charles", "Charles Hardin Holley", "", 1), - Suggestion("Elvis", "Elvis Aaron Presley", "", 2)); - - GetAutofillSuggestions(form, form.fields[1]); - CheckSuggestions(kDefaultPageID, - Suggestion("Holley", "Charles Hardin Holley", "", 1), - Suggestion("Presley", "Elvis Aaron Presley", "", 2)); -} - -// Test that for form with two fields with one that has an autocomplete -// attribute, suggestions are made for both if small form support is enabled -// (no minimum number of fields enforced). -TEST_P(AutofillManagerStructuredProfileTest, - GetProfileSuggestions_NoMinFieldsEnforced_WithOneAutocomplete) { - base::test::ScopedFeatureList features; - features.InitAndDisableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - // Set up our form data. - FormData form; - form.name = ASCIIToUTF16("MyForm"); - 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); - field.autocomplete_attribute = "given-name"; - form.fields.push_back(field); - test::CreateTestFormField("Last Name", "lastname", "", "text", &field); - field.autocomplete_attribute = ""; - form.fields.push_back(field); - - std::vector<FormData> forms(1, form); - FormsSeen(forms); - - GetAutofillSuggestions(form, form.fields[0]); - CheckSuggestions(kDefaultPageID, - Suggestion("Charles", "Charles Hardin Holley", "", 1), - Suggestion("Elvis", "Elvis Aaron Presley", "", 2)); - - GetAutofillSuggestions(form, form.fields[1]); - CheckSuggestions(kDefaultPageID, - Suggestion("Holley", "Charles Hardin Holley", "", 1), - Suggestion("Presley", "Elvis Aaron Presley", "", 2)); -} - // Test that for a form with two fields with autocomplete attributes, // suggestions are made for both fields. This is true even if a minimum number // of fields is enforced. TEST_P(AutofillManagerStructuredProfileTest, GetProfileSuggestions_SmallFormWithTwoAutocomplete) { - base::test::ScopedFeatureList features; - features.InitAndEnableFeature( - features::kAutofillEnforceMinRequiredFieldsForHeuristics); // Set up our form data. FormData form; form.name = ASCIIToUTF16("MyForm"); @@ -2123,6 +2041,79 @@ TEST_P(AutofillManagerStructuredProfileTest, EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen()); } +// Test that the correct section is filled. +TEST_F(AutofillManagerTest, FillTriggeredSection) { + // Set up our form data. + FormData form; + test::CreateTestAddressFormData(&form); + size_t index_of_trigger_field = form.fields.size(); + test::CreateTestAddressFormData(&form); + FormsSeen({form}); + + // Check that the form has been parsed into two sections. + ASSERT_NE(form.fields.size(), 0u); + ASSERT_EQ(index_of_trigger_field, form.fields.size() / 2); + { + FormStructure* form_structure; + AutofillField* autofill_field; + bool found = autofill_manager_->GetCachedFormAndField( + form, form.fields[index_of_trigger_field], &form_structure, + &autofill_field); + ASSERT_TRUE(found); + for (size_t i = 0; i < form.fields.size() / 2; ++i) { + size_t j = form.fields.size() / 2 + i; + ASSERT_EQ(form_structure->field(i)->name, form_structure->field(j)->name); + ASSERT_NE(form_structure->field(i)->section, + form_structure->field(j)->section); + ASSERT_TRUE(form_structure->field(i)->SameFieldAs(form.fields[j])); + ASSERT_TRUE(form_structure->field(j)->SameFieldAs(form.fields[i])); + } + } + + const char guid[] = "00000000-0000-0000-0000-000000000001"; + AutofillProfile* profile = personal_data_.GetProfileWithGUID(guid); + ASSERT_TRUE(profile); + EXPECT_EQ(1U, profile->use_count()); + EXPECT_NE(base::Time(), profile->use_date()); + + int response_page_id = 0; + FormData response_data; + FillAutofillFormDataAndSaveResults( + kDefaultPageID, form, form.fields[index_of_trigger_field], + MakeFrontendID(std::string(), guid), &response_page_id, &response_data); + // Extract the sections into individual forms to reduce boiler plate code. + size_t mid = response_data.fields.size() / 2; + FormData section1 = response_data; + FormData section2 = response_data; + section1.fields.erase(section1.fields.begin() + mid, section1.fields.end()); + section2.fields.erase(section2.fields.begin(), section2.fields.end() - mid); + // First section should be empty, second should be filled. + ExpectFilledForm(response_page_id, section1, kDefaultPageID, "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", true, false, + false); + ExpectFilledAddressFormElvis(response_page_id, section2, kDefaultPageID, + false); +} + +// 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 +// needed. +TEST_F(AutofillManagerTest, + ShouldIgnoreLossOfFocusWithNoPreviouslyInteractedForm) { + FormData form; + test::CreateTestAddressFormData(&form); + + autofill_manager_->UpdatePendingForm(form); + ASSERT_TRUE(autofill_manager_->pending_form_data()->SameFormAs(form)); + + // Receiving a notification that focus is no longer on the form *without* the + // renderer having a previously-interacted form should not result in + // any changes to the pending form. + autofill_manager_->OnFocusNoLongerOnForm(/*had_interacted_form=*/false); + EXPECT_TRUE(autofill_manager_->pending_form_data()->SameFormAs(form)); +} + TEST_F(AutofillManagerTest, ShouldNotShowCreditCardsSuggestionsIfCreditCardAutofillDisabled) { DisableCreditCardAutofill(); @@ -6484,7 +6475,7 @@ TEST_P(AutofillManagerStructuredProfileTest, form, form.fields.front(), gfx::RectF(), AutofillTickClock::NowTicks()); // Simulate lost of focus on the form. - autofill_manager_->OnFocusNoLongerOnForm(); + autofill_manager_->OnFocusNoLongerOnForm(true); } // Test that navigating with a filled form sends an upload with types matching @@ -6589,7 +6580,7 @@ TEST_P(AutofillManagerStructuredProfileTest, AutofillTickClock::NowTicks()); // Simulate lost of focus on the form. - autofill_manager_->OnFocusNoLongerOnForm(); + autofill_manager_->OnFocusNoLongerOnForm(true); } // Test that suggestions are returned for credit card fields with an @@ -7291,60 +7282,18 @@ TEST_P(AutofillManagerStructuredProfileTest, ShouldUploadForm) { test::CreateTestFormField("Name", "name", "", "text", &field); form.fields.push_back(field); - // With min required fields enabled. - { - base::test::ScopedFeatureList features; - features.InitAndEnableFeature( - features::kAutofillEnforceMinRequiredFieldsForUpload); - EXPECT_FALSE(autofill_manager_->ShouldUploadForm(FormStructure(form))); - } - - // With min required fields disabled. - { - base::test::ScopedFeatureList features; - features.InitAndDisableFeature( - features::kAutofillEnforceMinRequiredFieldsForUpload); - EXPECT_TRUE(autofill_manager_->ShouldUploadForm(FormStructure(form))); - } + EXPECT_TRUE(autofill_manager_->ShouldUploadForm(FormStructure(form))); // Add a second field to the form. test::CreateTestFormField("Email", "email", "", "text", &field); form.fields.push_back(field); - // With min required fields enabled. - { - base::test::ScopedFeatureList features; - features.InitAndEnableFeature( - features::kAutofillEnforceMinRequiredFieldsForUpload); - EXPECT_FALSE(autofill_manager_->ShouldUploadForm(FormStructure(form))); - } - - // With min required fields disabled. - { - base::test::ScopedFeatureList features; - features.InitAndDisableFeature( - features::kAutofillEnforceMinRequiredFieldsForUpload); - EXPECT_TRUE(autofill_manager_->ShouldUploadForm(FormStructure(form))); - } + EXPECT_TRUE(autofill_manager_->ShouldUploadForm(FormStructure(form))); // Has less than 3 fields but has autocomplete attribute. form.fields[0].autocomplete_attribute = "given-name"; - // With min required fields enabled. - { - base::test::ScopedFeatureList features; - features.InitAndEnableFeature( - features::kAutofillEnforceMinRequiredFieldsForUpload); - EXPECT_FALSE(autofill_manager_->ShouldUploadForm(FormStructure(form))); - } - - // With min required fields disabled. - { - base::test::ScopedFeatureList features; - features.InitAndDisableFeature( - features::kAutofillEnforceMinRequiredFieldsForUpload); - EXPECT_TRUE(autofill_manager_->ShouldUploadForm(FormStructure(form))); - } + EXPECT_TRUE(autofill_manager_->ShouldUploadForm(FormStructure(form))); // Has more than 3 fields, no autocomplete attribute. form.fields[0].autocomplete_attribute = ""; @@ -7370,21 +7319,8 @@ TEST_P(AutofillManagerStructuredProfileTest, ShouldUploadForm) { test::CreateTestFormField("Password", "password", "", "password", &field); form.fields.push_back(field); - // With min required fields enabled. - { - base::test::ScopedFeatureList features; - features.InitAndEnableFeature( - features::kAutofillEnforceMinRequiredFieldsForUpload); - EXPECT_FALSE(autofill_manager_->ShouldUploadForm(FormStructure(form))); - } - // With min required fields disabled. - { - base::test::ScopedFeatureList features; - features.InitAndDisableFeature( - features::kAutofillEnforceMinRequiredFieldsForUpload); - EXPECT_TRUE(autofill_manager_->ShouldUploadForm(FormStructure(form))); - } + EXPECT_TRUE(autofill_manager_->ShouldUploadForm(FormStructure(form))); // Autofill disabled. autofill_manager_->SetAutofillProfileEnabled(false); @@ -7593,55 +7529,6 @@ TEST_P(AutofillManagerStructuredProfileTest, } } -// Test that with small form upload enabled but heuristics and query disabled -// we get uploads but not quality metrics. -TEST_P(AutofillManagerStructuredProfileTest, - SmallForm_Upload_NoHeuristicsOrQuery) { - // Setup the feature environment. - base::test::ScopedFeatureList features; - features.InitWithFeatures( - // Enabled. - {kAutofillEnforceMinRequiredFieldsForHeuristics, - kAutofillEnforceMinRequiredFieldsForQuery}, - // Disabled. - {kAutofillEnforceMinRequiredFieldsForUpload}); - - // Add a local card to allow data matching for upload votes. - CreditCard credit_card = - autofill::test::GetRandomCreditCard(CreditCard::LOCAL_CARD); - personal_data_.AddCreditCard(credit_card); - - // Set up the form. - FormData form; - form.url = GURL("http://myform.com/form.html"); - form.action = GURL("http://myform.com/submit.html"); - FormFieldData field; - test::CreateTestFormField("Unknown", "unknown", "", "text", &field); - form.fields.push_back(field); - - // Have the browser encounter the form. - FormsSeen({form}); - - // Populate the form with a credit card value. - form.fields.back().value = credit_card.number(); - - // Setup expectation on the test autofill manager (these are validated - // during the simlulated submit). - autofill_manager_->SetExpectedSubmittedFieldTypes({{CREDIT_CARD_NUMBER}}); - autofill_manager_->SetExpectedObservedSubmission(true); - autofill_manager_->SetCallParentUploadFormData(true); - EXPECT_CALL(*download_manager_, - StartUploadRequest(_, false, _, std::string(), true, _)); - - base::HistogramTester histogram_tester; - FormSubmitted(form); - - EXPECT_EQ(FormStructure(form).FormSignatureAsStr(), - autofill_manager_->GetSubmittedFormSignature()); - - histogram_tester.ExpectTotalCount("Autofill.FieldPrediction.CreditCard", 0); -} - // Test that is_all_server_suggestions is true if there are only // full_server_card and masked_server_card on file. TEST_P(AutofillManagerStructuredProfileTest, @@ -8990,10 +8877,7 @@ class OnFocusOnFormFieldTest : public AutofillManagerTest, // Enabled {}, // Disabled - {kAutofillEnforceMinRequiredFieldsForHeuristics, - kAutofillEnforceMinRequiredFieldsForQuery, - kAutofillEnforceMinRequiredFieldsForUpload, - kAutofillRestrictUnownedFieldsToFormlessCheckout}); + {kAutofillRestrictUnownedFieldsToFormlessCheckout}); } void TearDown() override { diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc index b3d84109576..b9da1edcafb 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc @@ -26,11 +26,13 @@ #include "build/build_config.h" #include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/autofill_external_delegate.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/metrics/address_form_event_logger.h" #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" @@ -80,9 +82,6 @@ using ::testing::UnorderedPointwise; namespace autofill { -using features::kAutofillEnforceMinRequiredFieldsForHeuristics; -using features::kAutofillEnforceMinRequiredFieldsForQuery; -using features::kAutofillEnforceMinRequiredFieldsForUpload; using mojom::SubmissionSource; using SyncSigninState = AutofillSyncSigninState; @@ -357,10 +356,6 @@ class AutofillMetricsTest : public testing::Test { bool include_masked_server_credit_card, bool include_full_server_credit_card); - // Creates a masked server card with a nickname, and adds it to existing - // credit card list. - void AddMaskedServerCreditCardWithNickname(); - void AddMaskedServerCreditCardWithOffer(std::string guid, std::string offer_reward_amount, GURL url, @@ -388,6 +383,7 @@ class AutofillMetricsTest : public testing::Test { std::unique_ptr<MockAutocompleteHistoryManager> autocomplete_history_manager_; std::unique_ptr<AutofillExternalDelegate> external_delegate_; base::test::ScopedFeatureList scoped_feature_list_; + TestPatternProvider test_pattern_provider_; private: void CreateTestAutofillProfiles(); @@ -562,13 +558,6 @@ void AutofillMetricsTest::RecreateCreditCards( personal_data_->Refresh(); } -void AutofillMetricsTest::AddMaskedServerCreditCardWithNickname() { - CreditCard masked_server_credit_card = - test::GetMaskedServerCardWithNickname(); - personal_data_->AddServerCreditCard(masked_server_credit_card); - personal_data_->Refresh(); -} - void AutofillMetricsTest::AddMaskedServerCreditCardWithOffer( std::string guid, std::string offer_reward_amount, @@ -640,53 +629,43 @@ INSTANTIATE_TEST_SUITE_P(AutofillMetricsTest, // Test that we log quality metrics appropriately. TEST_F(AutofillMetricsTest, QualityMetrics) { // Set up our form data. - 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.main_frame_origin = url::Origin::Create(autofill_client_.form_origin()); - - std::vector<ServerFieldType> heuristic_types, server_types; - FormFieldData field; - - test::CreateTestFormField("Autofilled", "autofilled", "Elvis Aaron Presley", - "text", &field); - field.is_autofilled = true; - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FIRST); - - test::CreateTestFormField("Autofill Failed", "autofillfailed", - "buddy@gmail.com", "text", &field); - field.is_autofilled = false; - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_NUMBER); - server_types.push_back(EMAIL_ADDRESS); - - test::CreateTestFormField("Empty", "empty", "", "text", &field); - field.is_autofilled = false; - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FIRST); - - test::CreateTestFormField("Unknown", "unknown", "garbage", "text", &field); - field.is_autofilled = false; - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_NUMBER); - server_types.push_back(EMAIL_ADDRESS); - - test::CreateTestFormField("Select", "select", "USA", "select-one", &field); - field.is_autofilled = false; - form.fields.push_back(field); - heuristic_types.push_back(UNKNOWN_TYPE); - server_types.push_back(NO_SERVER_DATA); - - test::CreateTestFormField("Phone", "phone", "2345678901", "tel", &field); - field.is_autofilled = true; - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); + FormData form = + test::GetFormData({.description_for_logging = "QualityMetrics", + .fields = {{.label = "Autofilled", + .name = "autofilled", + .value = "Elvis Aaron Presley", + .is_autofilled = true}, + {.label = "Autofill Failed", + .name = "autofillfailed", + .value = "buddy@gmail.com", + .is_autofilled = false}, + {.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); @@ -829,50 +808,31 @@ TEST_F(AutofillMetricsTest, QualityMetrics) { // Test that the ProfileImportStatus logs a no import. TEST_F(AutofillMetricsTest, ProfileImportStatus_NoImport) { // 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"); - - std::vector<ServerFieldType> heuristic_types, server_types; - FormFieldData field; - - test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FULL); - - test::CreateTestFormField("Address", "home_line_one", - "3734 Elvis Presley Blvd.", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_LINE1); - server_types.push_back(ADDRESS_HOME_LINE1); - - test::CreateTestFormField("City", "city", "New York", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_CITY); - server_types.push_back(ADDRESS_HOME_CITY); - - test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - - test::CreateTestFormField("State", "state", "InvalidState", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_STATE); - server_types.push_back(ADDRESS_HOME_STATE); - - test::CreateTestFormField("ZIP", "zip", "00000000000000000", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_ZIP); - server_types.push_back(ADDRESS_HOME_ZIP); - - test::CreateTestFormField("Country", "country", "NoACountry", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_COUNTRY); - server_types.push_back(ADDRESS_HOME_COUNTRY); + FormData form = test::GetFormData( + {.description_for_logging = "ProfileImportStatus_NoImport", + .fields = { + {.role = ServerFieldType::NAME_FULL, .value = "Elvis Aaron Presley"}, + {.role = ServerFieldType::ADDRESS_HOME_LINE1, + .value = "3734 Elvis Presley Blvd."}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, .value = "New York"}, + {.role = ServerFieldType::PHONE_HOME_NUMBER, .value = "2345678901"}, + {.role = ServerFieldType::ADDRESS_HOME_STATE, + .value = "Invalid State"}, + {.role = ServerFieldType::ADDRESS_HOME_ZIP, + .value = "00000000000000000"}, + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY, + .value = "NoACountry"}}}); + + std::vector<ServerFieldType> heuristic_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; + std::vector<ServerFieldType> server_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; // Simulate having seen this form on page load. autofill_manager_->AddSeenForm(form, heuristic_types, server_types); @@ -902,50 +862,28 @@ TEST_F(AutofillMetricsTest, ProfileImportStatus_NoImport) { // Test that the ProfileImportStatus logs a regular import. TEST_F(AutofillMetricsTest, ProfileImportStatus_RegularImport) { // 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"); - - std::vector<ServerFieldType> heuristic_types, server_types; - FormFieldData field; - - test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FULL); - - test::CreateTestFormField("Address", "home_line_one", - "3734 Elvis Presley Blvd.", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_LINE1); - server_types.push_back(ADDRESS_HOME_LINE1); - - test::CreateTestFormField("City", "city", "New York", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_CITY); - server_types.push_back(ADDRESS_HOME_CITY); - - test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - - test::CreateTestFormField("State", "state", "CA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_STATE); - server_types.push_back(ADDRESS_HOME_STATE); - - test::CreateTestFormField("ZIP", "zip", "37373", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_ZIP); - server_types.push_back(ADDRESS_HOME_ZIP); - - test::CreateTestFormField("Country", "country", "USA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_COUNTRY); - server_types.push_back(ADDRESS_HOME_COUNTRY); + FormData form = test::GetFormData( + {.description_for_logging = "ProfileImportStatus_RegularImport", + .fields = { + {.role = ServerFieldType::NAME_FULL, .value = "Elvis Aaron Presley"}, + {.role = ServerFieldType::ADDRESS_HOME_LINE1, + .value = "3734 Elvis Presley Blvd."}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, .value = "New York"}, + {.role = ServerFieldType::PHONE_HOME_NUMBER, .value = "2345678901"}, + {.role = ServerFieldType::ADDRESS_HOME_STATE, .value = "CA"}, + {.role = ServerFieldType::ADDRESS_HOME_ZIP, .value = "37373"}, + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY, .value = "USA"}}}); + + std::vector<ServerFieldType> heuristic_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; + std::vector<ServerFieldType> server_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; // Simulate having seen this form on page load. autofill_manager_->AddSeenForm(form, heuristic_types, server_types); @@ -975,54 +913,40 @@ TEST_F(AutofillMetricsTest, ProfileImportStatus_RegularImport) { // Test that the ProfileImportStatus logs a section union mport. TEST_F(AutofillMetricsTest, ProfileImportStatus_UnionImport) { // 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"); - - std::vector<ServerFieldType> heuristic_types, server_types; - FormFieldData field; - - test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FULL); - - test::CreateTestFormField("Address", "home_line_one", - "3734 Elvis Presley Blvd.", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_LINE1); - server_types.push_back(ADDRESS_HOME_LINE1); - - test::CreateTestFormField("ZIP", "zip", "37373", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_ZIP); - server_types.push_back(ADDRESS_HOME_ZIP); - - test::CreateTestFormField("Country", "country", "USA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_COUNTRY); - server_types.push_back(ADDRESS_HOME_COUNTRY); - - test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - - test::CreateTestFormField("City", "city", "New York", "text", &field); - // Assign a specific section. - field.autocomplete_attribute = "section-billing locality"; - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_CITY); - server_types.push_back(ADDRESS_HOME_CITY); - - test::CreateTestFormField("State", "state", "CA", "text", &field); - // Make the state a different section than the city. - field.autocomplete_attribute = "section-shipping address-level1"; - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_STATE); - server_types.push_back(ADDRESS_HOME_STATE); + FormData form = test::GetFormData( + {.description_for_logging = "ProfileImportStatus_UnionImport", + .fields = { + {.role = ServerFieldType::NAME_FULL, .value = "Elvis Aaron Presley"}, + {.role = ServerFieldType::ADDRESS_HOME_LINE1, + .value = "3734 Elvis Presley Blvd."}, + {.role = ServerFieldType::ADDRESS_HOME_ZIP, .value = "37373"}, + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY, .value = "USA"}, + {.role = ServerFieldType::PHONE_HOME_NUMBER, .value = "2345678901"}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, + .value = "New York", + .autocomplete_attribute = "section-billing locality"}, + // Add the last field of the form into a new section. + {.role = ServerFieldType::ADDRESS_HOME_STATE, + .value = "CA", + .autocomplete_attribute = "section-shipping address-level1"}}}); + + // Set the heuristic types. + std::vector<ServerFieldType> heuristic_types = {NAME_FULL, + ADDRESS_HOME_LINE1, + ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY, + PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_CITY, + ADDRESS_HOME_STATE}; + + // Set the server types. + std::vector<ServerFieldType> server_types = {NAME_FULL, + ADDRESS_HOME_LINE1, + ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY, + PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_CITY, + ADDRESS_HOME_STATE}; // Simulate having seen this form on page load. autofill_manager_->AddSeenForm(form, heuristic_types, server_types); @@ -1034,39 +958,16 @@ TEST_F(AutofillMetricsTest, ProfileImportStatus_UnionImport) { base::HistogramTester histogram_tester; std::string histogram = "Autofill.AddressProfileImportStatus"; - // Disable the union import feature. - scoped_feature_list_.InitAndDisableFeature( - features::kAutofillProfileImportFromUnifiedSection); - - // Simulate form submission. autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); + // Verify that one profile was imported using the union of the two sections. histogram_tester.ExpectBucketCount( histogram, AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT, 0); histogram_tester.ExpectBucketCount( histogram, AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT, - 1); - histogram_tester.ExpectBucketCount( - histogram, - AutofillMetrics::AddressProfileImportStatusMetric::SECTION_UNION_IMPORT, 0); - - // Enable the union import feature. - scoped_feature_list_.Reset(); - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillProfileImportFromUnifiedSection); - // Simulate form submission. - autofill_manager_->OnFormSubmitted(form, false, - SubmissionSource::FORM_SUBMISSION); - - histogram_tester.ExpectBucketCount( - histogram, - AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT, 0); - histogram_tester.ExpectBucketCount( - histogram, AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT, - 1); histogram_tester.ExpectBucketCount( histogram, AutofillMetrics::AddressProfileImportStatusMetric::SECTION_UNION_IMPORT, @@ -1077,50 +978,28 @@ TEST_F(AutofillMetricsTest, ProfileImportStatus_UnionImport) { // 'perfect' profile import. TEST_F(AutofillMetricsTest, ProfileImportRequirements_AllFulfilled) { // 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"); - - std::vector<ServerFieldType> heuristic_types, server_types; - FormFieldData field; - - test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FULL); - - test::CreateTestFormField("Address", "home_line_one", - "3734 Elvis Presley Blvd.", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_LINE1); - server_types.push_back(ADDRESS_HOME_LINE1); - - test::CreateTestFormField("City", "city", "New York", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_CITY); - server_types.push_back(ADDRESS_HOME_CITY); - - test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - - test::CreateTestFormField("State", "state", "CA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_STATE); - server_types.push_back(ADDRESS_HOME_STATE); - - test::CreateTestFormField("ZIP", "zip", "37373", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_ZIP); - server_types.push_back(ADDRESS_HOME_ZIP); - - test::CreateTestFormField("Country", "country", "USA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_COUNTRY); - server_types.push_back(ADDRESS_HOME_COUNTRY); + FormData form = test::GetFormData( + {.description_for_logging = "ProfileImportRequirements_AllFulfilled", + .fields = { + {.role = ServerFieldType::NAME_FULL, .value = "Elvis Aaron Presley"}, + {.role = ServerFieldType::ADDRESS_HOME_LINE1, + .value = "3734 Elvis Presley Blvd."}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, .value = "New York"}, + {.role = ServerFieldType::PHONE_HOME_NUMBER, .value = "2345678901"}, + {.role = ServerFieldType::ADDRESS_HOME_STATE, .value = "CA"}, + {.role = ServerFieldType::ADDRESS_HOME_ZIP, .value = "37373"}, + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY, .value = "USA"}}}); + + std::vector<ServerFieldType> heuristic_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; + std::vector<ServerFieldType> server_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; // Simulate having seen this form on page load. autofill_manager_->AddSeenForm(form, heuristic_types, server_types); @@ -1178,49 +1057,28 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_AllFulfilled) { // ADDRESS_HOME_LINE1 is missing. TEST_F(AutofillMetricsTest, ProfileImportRequirements_MissingHomeLineOne) { // 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"); - - std::vector<ServerFieldType> heuristic_types, server_types; - FormFieldData field; - - test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FULL); - - test::CreateTestFormField("Address", "home_line_one", "", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_LINE1); - server_types.push_back(ADDRESS_HOME_LINE1); - - test::CreateTestFormField("City", "city", "New York", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_CITY); - server_types.push_back(ADDRESS_HOME_CITY); - - test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - - test::CreateTestFormField("State", "state", "CA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_STATE); - server_types.push_back(ADDRESS_HOME_STATE); - - test::CreateTestFormField("ZIP", "zip", "37373", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_ZIP); - server_types.push_back(ADDRESS_HOME_ZIP); - - test::CreateTestFormField("Country", "country", "USA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_COUNTRY); - server_types.push_back(ADDRESS_HOME_COUNTRY); + FormData form = test::GetFormData( + {.description_for_logging = + "ProfileImportRequirements_MissingHomeLineOne", + .fields = { + {.role = ServerFieldType::NAME_FULL, .value = "Elvis Aaron Presley"}, + {.role = ServerFieldType::ADDRESS_HOME_LINE1, .value = ""}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, .value = "New York"}, + {.role = ServerFieldType::PHONE_HOME_NUMBER, .value = "2345678901"}, + {.role = ServerFieldType::ADDRESS_HOME_STATE, .value = "CA"}, + {.role = ServerFieldType::ADDRESS_HOME_ZIP, .value = "37373"}, + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY, .value = "USA"}}}); + + std::vector<ServerFieldType> heuristic_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; + std::vector<ServerFieldType> server_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; // Simulate having seen this form on page load. autofill_manager_->AddSeenForm(form, heuristic_types, server_types); @@ -1280,50 +1138,30 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_MissingHomeLineOne) { TEST_F(AutofillMetricsTest, ProfileImportRequirements_AllFulfilledForNonStateCountry) { // 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"); - - std::vector<ServerFieldType> heuristic_types, server_types; - FormFieldData field; - - test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FULL); - - test::CreateTestFormField("Address", "home_line_one", - "3734 Elvis Presley Blvd.", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_LINE1); - server_types.push_back(ADDRESS_HOME_LINE1); - - test::CreateTestFormField("City", "city", "New York", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_CITY); - server_types.push_back(ADDRESS_HOME_CITY); - - test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - - test::CreateTestFormField("State", "state", "", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_STATE); - server_types.push_back(ADDRESS_HOME_STATE); - - test::CreateTestFormField("ZIP", "zip", "37373", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_ZIP); - server_types.push_back(ADDRESS_HOME_ZIP); - - test::CreateTestFormField("Country", "country", "Germany", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_COUNTRY); - server_types.push_back(ADDRESS_HOME_COUNTRY); + FormData form = test::GetFormData( + {.description_for_logging = + "ProfileImportRequirements_AllFulfilledForNonStateCountry", + .fields = { + {.role = ServerFieldType::NAME_FULL, .value = "Elvis Aaron Presley"}, + {.role = ServerFieldType::ADDRESS_HOME_LINE1, + .value = "3734 Elvis Presley Blvd."}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, .value = "New York"}, + {.role = ServerFieldType::PHONE_HOME_NUMBER, .value = "2345678901"}, + {.role = ServerFieldType::ADDRESS_HOME_STATE, .value = ""}, + {.role = ServerFieldType::ADDRESS_HOME_ZIP, .value = "37373"}, + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY, + .value = "Germany"}}}); + + std::vector<ServerFieldType> heuristic_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; + std::vector<ServerFieldType> server_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; // Simulate having seen this form on page load. autofill_manager_->AddSeenForm(form, heuristic_types, server_types); @@ -1381,56 +1219,38 @@ TEST_F(AutofillMetricsTest, TEST_F(AutofillMetricsTest, ProfileImportRequirements_FilledButInvalidZipEmailAndState) { // 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"); - - std::vector<ServerFieldType> heuristic_types, server_types; - FormFieldData field; - - test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FULL); - - test::CreateTestFormField("Address", "home_line_one", - "3734 Elvis Presley Blvd.", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_LINE1); - server_types.push_back(ADDRESS_HOME_LINE1); - - test::CreateTestFormField("City", "city", "New York", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_CITY); - server_types.push_back(ADDRESS_HOME_CITY); - - test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - - test::CreateTestFormField("State", "state", "DefNotAState", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_STATE); - server_types.push_back(ADDRESS_HOME_STATE); - - test::CreateTestFormField("ZIP", "zip", "1234567890", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_ZIP); - server_types.push_back(ADDRESS_HOME_ZIP); - - test::CreateTestFormField("Country", "country", "USA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_COUNTRY); - server_types.push_back(ADDRESS_HOME_COUNTRY); - - test::CreateTestFormField("Email1", "email1", "test_noat_test.io", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(EMAIL_ADDRESS); - server_types.push_back(EMAIL_ADDRESS); + FormData form = test::GetFormData( + {.description_for_logging = + "ProfileImportRequirements_FilledButInvalidZipEmailAndState", + .fields = { + {.role = ServerFieldType::NAME_FULL, .value = "Elvis Aaron Presley"}, + {.role = ServerFieldType::ADDRESS_HOME_LINE1, + .value = "3734 Elvis Presley Blvd."}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, .value = "New York"}, + {.role = ServerFieldType::PHONE_HOME_NUMBER, .value = "2345678901"}, + {.role = ServerFieldType::ADDRESS_HOME_STATE, + .value = "DefNotAState"}, + {.role = ServerFieldType::ADDRESS_HOME_ZIP, .value = "1234567890"}, + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY, .value = "USA"}, + {.role = ServerFieldType::EMAIL_ADDRESS, + .value = "test_noat_test.io"}}}); + + std::vector<ServerFieldType> heuristic_types = {NAME_FULL, + ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, + PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, + ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY, + EMAIL_ADDRESS}; + std::vector<ServerFieldType> server_types = {NAME_FULL, + ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, + PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, + ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY, + EMAIL_ADDRESS}; // Simulate having seen this form on page load. autofill_manager_->AddSeenForm(form, heuristic_types, server_types); @@ -1488,61 +1308,41 @@ TEST_F(AutofillMetricsTest, // profile with multiple email addresses. TEST_F(AutofillMetricsTest, ProfileImportRequirements_NonUniqueEmail) { // 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"); - - std::vector<ServerFieldType> heuristic_types, server_types; - FormFieldData field; - - test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FULL); - - test::CreateTestFormField("Address", "home_line_one", - "3734 Elvis Presley Blvd.", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_LINE1); - server_types.push_back(ADDRESS_HOME_LINE1); - - test::CreateTestFormField("City", "city", "New York", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_CITY); - server_types.push_back(ADDRESS_HOME_CITY); - - test::CreateTestFormField("Phone", "phone", "2345678901", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - - test::CreateTestFormField("State", "state", "CA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_STATE); - server_types.push_back(ADDRESS_HOME_STATE); - - test::CreateTestFormField("ZIP", "zip", "37373", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_ZIP); - server_types.push_back(ADDRESS_HOME_ZIP); - - test::CreateTestFormField("Country", "country", "USA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_COUNTRY); - server_types.push_back(ADDRESS_HOME_COUNTRY); - - test::CreateTestFormField("Email1", "email1", "test@test.io", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(EMAIL_ADDRESS); - server_types.push_back(EMAIL_ADDRESS); - - test::CreateTestFormField("Email2", "email2", "not_test@test.io", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(EMAIL_ADDRESS); - server_types.push_back(EMAIL_ADDRESS); + FormData form = test::GetFormData( + {.description_for_logging = "ProfileImportRequirements_NonUniqueEmail", + .fields = { + {.role = ServerFieldType::NAME_FULL, .value = "Elvis Aaron Presley"}, + {.role = ServerFieldType::ADDRESS_HOME_LINE1, + .value = "3734 Elvis Presley Blvd."}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, .value = "New York"}, + {.role = ServerFieldType::PHONE_HOME_NUMBER, .value = "2345678901"}, + {.role = ServerFieldType::ADDRESS_HOME_STATE, .value = "CA"}, + {.role = ServerFieldType::ADDRESS_HOME_ZIP, .value = "37373"}, + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY, .value = "USA"}, + {.role = ServerFieldType::EMAIL_ADDRESS, + .value = "test_noat_test.io"}, + {.label = "Email1", + .name = ".email1", + .value = "not_test@test.io"}}}); + + std::vector<ServerFieldType> heuristic_types = {NAME_FULL, + ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, + PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, + ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY, + EMAIL_ADDRESS, + EMAIL_ADDRESS}; + std::vector<ServerFieldType> server_types = {NAME_FULL, + ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, + PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, + ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY, + EMAIL_ADDRESS, + EMAIL_ADDRESS}; // Simulate having seen this form on page load. autofill_manager_->AddSeenForm(form, heuristic_types, server_types); @@ -1600,50 +1400,29 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_NonUniqueEmail) { // missing. TEST_F(AutofillMetricsTest, ProfileImportRequirements_OnlyAddressLineOne) { // 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"); - - std::vector<ServerFieldType> heuristic_types, server_types; - FormFieldData field; - - test::CreateTestFormField("Name", "name", "Elvis Aaron Presley", "text", - &field); - form.fields.push_back(field); - heuristic_types.push_back(NAME_FULL); - server_types.push_back(NAME_FULL); - - test::CreateTestFormField("Address", "home_line_one", - "3734 Elvis Presley Blvd.", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_LINE1); - server_types.push_back(ADDRESS_HOME_LINE1); - - test::CreateTestFormField("City", "city", "", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_CITY); - server_types.push_back(ADDRESS_HOME_CITY); - - test::CreateTestFormField("Phone", "phone", "", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - server_types.push_back(PHONE_HOME_CITY_AND_NUMBER); - - test::CreateTestFormField("State", "state", "", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_STATE); - server_types.push_back(ADDRESS_HOME_STATE); - - test::CreateTestFormField("ZIP", "zip", "", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_ZIP); - server_types.push_back(ADDRESS_HOME_ZIP); - - test::CreateTestFormField("Country", "", "USA", "text", &field); - form.fields.push_back(field); - heuristic_types.push_back(ADDRESS_HOME_COUNTRY); - server_types.push_back(ADDRESS_HOME_COUNTRY); + FormData form = test::GetFormData( + {.description_for_logging = + "ProfileImportRequirements_OnlyAddressLineOne", + .fields = { + {.role = ServerFieldType::NAME_FULL, .value = "Elvis Aaron Presley"}, + {.role = ServerFieldType::ADDRESS_HOME_LINE1, + .value = "3734 Elvis Presley Blvd."}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, .value = ""}, + {.role = ServerFieldType::PHONE_HOME_NUMBER, .value = ""}, + {.role = ServerFieldType::ADDRESS_HOME_STATE, .value = ""}, + {.role = ServerFieldType::ADDRESS_HOME_ZIP, .value = ""}, + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY, .value = ""}}}); + + std::vector<ServerFieldType> heuristic_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; + std::vector<ServerFieldType> server_types = { + NAME_FULL, ADDRESS_HOME_LINE1, + ADDRESS_HOME_CITY, PHONE_HOME_CITY_AND_NUMBER, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; // Simulate having seen this form on page load. autofill_manager_->AddSeenForm(form, heuristic_types, server_types); @@ -3200,92 +2979,6 @@ TEST_F(AutofillMetricsTest, UpiVirtualPaymentAddress) { histogram_tester.ExpectTotalCount("Autofill.UserHappiness.Unknown", 0); } -// Verify that when a field is annotated with the autocomplete attribute, its -// predicted type is remembered when quality metrics are logged. -TEST_F(AutofillMetricsTest, PredictedMetricsWithAutocomplete) { - // Allow heuristics to run (and be accepted) for small forms. - base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - - // Set up our form data. Note that the fields have default values not found - // in the user profiles. They will be changed between the time the form is - // seen/parsed, and the time it is submitted. - FormData form; - FormFieldData field; - 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.main_frame_origin = - url::Origin::Create(GURL("http://example_root.com/form.html")); - - test::CreateTestFormField("Select", "select", "USA", "select-one", &field); - form.fields.push_back(field); - form.fields.back().autocomplete_attribute = "country"; - - test::CreateTestFormField("Unknown", "Unknown", "", "text", &field); - form.fields.push_back(field); - - test::CreateTestFormField("Phone", "phone", "", "tel", &field); - form.fields.push_back(field); - - std::vector<FormData> forms(1, form); - - base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); - - // We change the value of the text fields to change the default/seen values - // (hence the values are not cleared in UpdateFromCache). The new values - // match what is in the test profile. - form.fields[1].value = base::ASCIIToUTF16("79401"); - form.fields[2].value = base::ASCIIToUTF16("2345678901"); - autofill_manager_->OnFormSubmitted(form, false, - SubmissionSource::FORM_SUBMISSION); - - for (const std::string source : {"Heuristic", "Server", "Overall"}) { - std::string histogram_name = - "Autofill.FieldPredictionQuality.ByFieldType." + source; - // First verify that country was not predicted by client or server. - { - SCOPED_TRACE("ADDRESS_HOME_COUNTRY"); - histogram_tester.ExpectBucketCount( - histogram_name, - GetFieldTypeGroupPredictionQualityMetric( - ADDRESS_HOME_COUNTRY, - source == "Overall" ? AutofillMetrics::TRUE_POSITIVE - : AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), - 1); - } - - // We did not predict zip code because it did not have an autocomplete - // attribute, nor client or server predictions. - { - SCOPED_TRACE("ADDRESS_HOME_ZIP"); - histogram_tester.ExpectBucketCount( - histogram_name, - GetFieldTypeGroupPredictionQualityMetric( - ADDRESS_HOME_ZIP, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), - 1); - } - - // Phone should have been predicted by the heuristics but not the server. - { - SCOPED_TRACE("PHONE_HOME_WHOLE_NUMBER"); - histogram_tester.ExpectBucketCount( - histogram_name, - GetFieldTypeGroupPredictionQualityMetric( - PHONE_HOME_WHOLE_NUMBER, - source == "Server" ? AutofillMetrics::FALSE_NEGATIVE_UNKNOWN - : AutofillMetrics::TRUE_POSITIVE), - 1); - } - - // Sanity check. - histogram_tester.ExpectTotalCount(histogram_name, 3); - } -} - // Test that we behave sanely when the cached form differs from the submitted // one. TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) { @@ -3426,8 +3119,6 @@ TEST_F(AutofillMetricsTest, StoredProfileCountAutofillableFormSubmission) { // Verify that when submitting a non-autofillable form, the stored profile // metric is not logged. TEST_F(AutofillMetricsTest, StoredProfileCountNonAutofillableFormSubmission) { - base::test::ScopedFeatureList features; - features.InitAndEnableFeature(kAutofillEnforceMinRequiredFieldsForHeuristics); // Construct a non-fillable form. FormData form; form.unique_renderer_id = MakeFormRendererId(); @@ -3729,19 +3420,6 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) { histogram_tester.ExpectTotalCount("Autofill.DeveloperEngagement", 0); } - // Otherwise, log developer engagement for all forms. - { - base::test::ScopedFeatureList features; - features.InitAndDisableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); - autofill_manager_->Reset(); - histogram_tester.ExpectUniqueSample( - "Autofill.DeveloperEngagement", - AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS, 1); - } - // Add another field to the form, so that it becomes fillable. test::CreateTestFormField("Phone", "phone", "", "text", &field); forms.back().fields.push_back(field); @@ -3827,9 +3505,6 @@ TEST_F(AutofillMetricsTest, // Ensure no entries are logged when loading a non-fillable form. { - base::test::ScopedFeatureList features; - features.InitAndEnableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); autofill_manager_->Reset(); @@ -3907,14 +3582,6 @@ TEST_F(AutofillMetricsTest, // Verify that we correctly log UKM for form parsed with type hints regarding // developer engagement. TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) { - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures( - // Enabled. - {kAutofillEnforceMinRequiredFieldsForHeuristics, - kAutofillEnforceMinRequiredFieldsForQuery, - kAutofillEnforceMinRequiredFieldsForUpload}, - // Disabled. - {}); FormData form; form.unique_renderer_id = MakeFormRendererId(); form.name = ASCIIToUTF16("TestForm"); @@ -3930,27 +3597,11 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) { test::CreateTestFormField("Payment", "payment", "", "text", &field); field.autocomplete_attribute = "upi-vpa"; form.fields.push_back(field); - - std::vector<FormData> forms(1, form); - - // Expect the "upi-vpa hint" metric to be logged and the "form loaded" form - // interaction event to be logged. - { - SCOPED_TRACE("VPA is the only hint"); - autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); - - VerifyDeveloperEngagementUkm( - test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false, - /* UPI VPA has Unknown form type.*/ - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE}, - {AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT}); - PurgeUKM(); - } - - // Add another field with an author-specified field type to the form. test::CreateTestFormField("", "", "", "text", &field); field.autocomplete_attribute = "address-line1"; - forms.back().fields.push_back(field); + form.fields.push_back(field); + + std::vector<FormData> forms(1, form); { SCOPED_TRACE("VPA and other autocomplete hint present"); @@ -6665,349 +6316,6 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { } } -// Test that we log form events for masked server card nickname. -// TODO(crbug.com/1059087): Remove histogram logging for server nickname. -TEST_F(AutofillMetricsTest, LogServerNicknameFormEvents) { - // 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.main_frame_origin = url::Origin::Create(autofill_client_.form_origin()); - - FormFieldData field; - std::vector<ServerFieldType> field_types; - test::CreateTestFormField("Month", "card_month", "", "text", &field); - form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_EXP_MONTH); - test::CreateTestFormField("Year", "card_year", "", "text", &field); - form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_EXP_2_DIGIT_YEAR); - test::CreateTestFormField("Credit card", "card", "", "text", &field); - form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_NUMBER); - - // Creating all kinds of cards. None of them has nicknames. - RecreateCreditCards(true /* include_local_credit_card */, - true /* include_masked_server_credit_card */, - true /* include_full_server_credit_card */); - - // Simulate having seen this form on page load. - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { - // A masked server card with nickname. - // Simulating activating the autofill popup for the credit card field, new - // popup being shown and filling a local card suggestion. - base::HistogramTester histogram_tester; - autofill_manager_->OnQueryFormFieldAutofill( - 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false); - autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); - std::string guid("10000000-0000-0000-0000-000000000001"); // local card - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(), - autofill_manager_->MakeFrontendIDForTest(guid, std::string())); - histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard", - FORM_EVENT_SUGGESTIONS_SHOWN, 1); - histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard", - FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1); - histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard", - FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1); - // Check that the nickname sub-histogram was not recorded. - // ExpectBucketCount() can't be used here because it expects the histogram - // to exist. - EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix( - "Autofill.FormEvents.CreditCard") - ["Autofill.FormEvents.CreditCard.WithServerNickname"]); - } - - // Add another masked server card with nickname. - AddMaskedServerCreditCardWithNickname(); - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { - // A masked server card with nickname. - // Simulating activating the autofill popup for the credit card field, new - // popup being shown and filling a local card suggestion. Both general - // histogram and nickname sub-histogram are logged. - base::HistogramTester histogram_tester; - autofill_manager_->OnQueryFormFieldAutofill( - 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false); - autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); - // Select the local card, still log to sub-histogram because user has - // another masked servr card with nickname. - std::string guid("10000000-0000-0000-0000-000000000001"); - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(), - autofill_manager_->MakeFrontendIDForTest(guid, std::string())); - // The general credit card form event historgram is still logged. - histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard", - FORM_EVENT_SUGGESTIONS_SHOWN, 1); - histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard", - FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1); - histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard", - FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1); - // Sub-histogratm CreditCard.WithServerNickname is also logged - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_SUGGESTIONS_SHOWN, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1); - } - - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { - // A masked server card with nickname. - // Simulating activating the autofill popup for the credit card field, new - // popup being shown, selecting a masked card server suggestion and - // submitting the form. Verify that all related form events are correctly - // logged to nickname sub-histogram. - base::HistogramTester histogram_tester; - autofill_manager_->OnQueryFormFieldAutofill( - 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false); - autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); - // Select the masked server card without nickname, still log to nickname - // sub-histogram because user has another masked server card with nickname. - std::string guid("10000000-0000-0000-0000-000000000002"); - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.back(), - autofill_manager_->MakeFrontendIDForTest(guid, std::string())); - OnDidGetRealPan(AutofillClient::SUCCESS, "6011000990139424"); - autofill_manager_->OnFormSubmitted(form, false, - SubmissionSource::FORM_SUBMISSION); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_INTERACTED_ONCE, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_SUGGESTIONS_SHOWN, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 1); - } -} - -// Test that we log suggestion selection duration for masked server card -// nickname. -// TODO(crbug.com/1059087): Remove histogram logging for server nickname. -TEST_F(AutofillMetricsTest, LogServerNicknameSelectionDuration) { - base::TimeTicks now = AutofillTickClock::NowTicks(); - TestAutofillTickClock test_clock; - test_clock.SetNowTicks(now); - - // 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.main_frame_origin = url::Origin::Create(autofill_client_.form_origin()); - - FormFieldData field; - std::vector<ServerFieldType> field_types; - test::CreateTestFormField("Month", "card_month", "", "text", &field); - form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_EXP_MONTH); - test::CreateTestFormField("Year", "card_year", "", "text", &field); - form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_EXP_2_DIGIT_YEAR); - test::CreateTestFormField("Credit card", "card", "", "text", &field); - form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_NUMBER); - - // Creating all kinds of cards. None of them have nicknames. - RecreateCreditCards(true /* include_local_credit_card */, - true /* include_masked_server_credit_card */, - true /* include_full_server_credit_card */); - - // Simulate having seen this form on page load. - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { - // No masked server card has nickname. - // Simulating activating the autofill popup for the credit card field, new - // popup being shown and filling a local card suggestion. - base::HistogramTester histogram_tester; - autofill_manager_->OnQueryFormFieldAutofill( - 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false); - autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); - // Simulate choosing a suggestion after 1 second. - base::TimeDelta selection_delta = base::TimeDelta::FromSeconds(1); - test_clock.SetNowTicks(now + selection_delta); - std::string guid("10000000-0000-0000-0000-000000000001"); // local card - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(), - autofill_manager_->MakeFrontendIDForTest(guid, std::string())); - // Check that the nickname sub-histogram was not recorded. - histogram_tester.ExpectTotalCount( - "Autofill.FormEvents.CreditCard.WithServerNickname.SelectionDuration", - 0); - } - - // Add another masked server card with nickname. - AddMaskedServerCreditCardWithNickname(); - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { - // A masked server card with nickname. - // Simulating activating the autofill popup for the credit card field, new - // popup being shown and filling a local card suggestion. - base::HistogramTester histogram_tester; - test_clock.SetNowTicks(now); - autofill_manager_->OnQueryFormFieldAutofill( - 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false); - autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); - // Simulate choosing a suggestion after 1 second. - base::TimeDelta selection_delta = base::TimeDelta::FromSeconds(1); - test_clock.SetNowTicks(now + selection_delta); - // Select the local card, log nickname selection duration because user has - // another masked servr card with nickname. - std::string guid("10000000-0000-0000-0000-000000000001"); - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(), - autofill_manager_->MakeFrontendIDForTest(guid, std::string())); - histogram_tester.ExpectTotalCount( - "Autofill.FormEvents.CreditCard.WithServerNickname.SelectionDuration", - 1); - histogram_tester.ExpectTimeBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname.SelectionDuration", - selection_delta, 1); - } - - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { - // A masked server card with nickname. - // Simulating activating the autofill popup for the credit card field, new - // popup being shown and selecting a masked card server suggestion. - base::HistogramTester histogram_tester; - test_clock.SetNowTicks(now); - autofill_manager_->OnQueryFormFieldAutofill( - 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false); - autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); - // Simulate choosing a suggestion after 1 second. - base::TimeDelta selection_delta = base::TimeDelta::FromSeconds(1); - test_clock.SetNowTicks(now + selection_delta); - // Select the masked server card without nickname, still log select - // duration, even though GetRealPan fails afterwards, because user has - // another masked server card with nickname. - std::string guid("10000000-0000-0000-0000-000000000002"); - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.back(), - autofill_manager_->MakeFrontendIDForTest(guid, std::string())); - OnDidGetRealPan(AutofillClient::PERMANENT_FAILURE, std::string()); - histogram_tester.ExpectTotalCount( - "Autofill.FormEvents.CreditCard.WithServerNickname.SelectionDuration", - 1); - histogram_tester.ExpectTimeBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname.SelectionDuration", - selection_delta, 1); - } - - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { - // A masked server card with nickname. - // Simulating suggestions are shown twice, then choosing a local card. - base::HistogramTester histogram_tester; - test_clock.SetNowTicks(now); - autofill_manager_->OnQueryFormFieldAutofill( - 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false); - autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); - // Simulate the first popup was dismissed and the second popup is shown - // after 1 second. - base::TimeDelta suggestion_delta = base::TimeDelta::FromSeconds(1); - test_clock.SetNowTicks(now + suggestion_delta); - autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); - // Simulate choosing a suggestion on the second popup after 2 seconds. - // The overall selection duration will be 3 seconds, between the first time - // suggestion was shown and the first card is chosen. - base::TimeDelta total_selection_delta = - suggestion_delta + base::TimeDelta::FromSeconds(2); - test_clock.SetNowTicks(now + total_selection_delta); - std::string guid("10000000-0000-0000-0000-000000000001"); // local card - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.back(), - autofill_manager_->MakeFrontendIDForTest(guid, std::string())); - histogram_tester.ExpectTotalCount( - "Autofill.FormEvents.CreditCard.WithServerNickname.SelectionDuration", - 1); - histogram_tester.ExpectTimeBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname.SelectionDuration", - total_selection_delta, 1); - } - - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { - // A masked server card with nickname. - // Simulating suggestions being shown and selecting masked server card - // multiple times. - base::HistogramTester histogram_tester; - test_clock.SetNowTicks(now); - autofill_manager_->OnQueryFormFieldAutofill( - 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false); - autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); - // Simulate the first selection happens 1 second after suggestion is shown. - base::TimeDelta first_suggestion_delta = base::TimeDelta::FromSeconds(1); - test_clock.SetNowTicks(now + first_suggestion_delta); - std::string guid1( - "10000000-0000-0000-0000-000000000002"); // masked server card - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.back(), - autofill_manager_->MakeFrontendIDForTest(guid1, std::string())); - // Simulate choosing another local card 2 seconds after user chooses the - // server card. - base::TimeDelta second_selection_delta = - first_suggestion_delta + base::TimeDelta::FromSeconds(2); - test_clock.SetNowTicks(now + second_selection_delta); - std::string guid2("10000000-0000-0000-0000-000000000001"); // local card - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.back(), - autofill_manager_->MakeFrontendIDForTest(guid2, std::string())); - // The selection duration should be only logged once. - histogram_tester.ExpectTotalCount( - "Autofill.FormEvents.CreditCard.WithServerNickname.SelectionDuration", - 1); - // The logged selection duration should be 1 second, between the suggestion - // was shown and the first card is chosen. - histogram_tester.ExpectTimeBucketCount( - "Autofill.FormEvents.CreditCard.WithServerNickname.SelectionDuration", - first_suggestion_delta, 1); - } -} - // Test that we log form events for masked server card with offers. TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) { scoped_feature_list_.InitAndEnableFeature( @@ -8657,45 +7965,6 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { // Clear out the third field's value. form.fields[2].value = base::string16(); forms.front() = form; - - // This form is non-fillable if small form support is disabled (min number - // of fields enforced.) - { - base::test::ScopedFeatureList features; - features.InitWithFeatures( - // Enabled - {kAutofillEnforceMinRequiredFieldsForHeuristics, - kAutofillEnforceMinRequiredFieldsForQuery}, - // Disabled - {}); - base::HistogramTester histogram_tester; - base::UserActionTester user_action_tester; - autofill_manager_->OnFormSubmitted(form, false, - SubmissionSource::FORM_SUBMISSION); - histogram_tester.ExpectUniqueSample( - "Autofill.FormSubmittedState", - AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1); - EXPECT_EQ(1, user_action_tester.GetActionCount( - "Autofill_FormSubmitted_NonFillable")); - - expected_form_submission_ukm_metrics.push_back( - {{UkmFormSubmittedType::kAutofillFormSubmittedStateName, - AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, - {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, - {UkmFormSubmittedType::kIsForCreditCardName, false}, - {UkmFormSubmittedType::kHasUpiVpaFieldName, false}, - {UkmFormSubmittedType::kFormTypesName, - AutofillMetrics::FormTypesToBitVector( - {FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE})}, - {UkmFormSubmittedType::kFormSignatureName, - Collapse(CalculateFormSignature(form)).value()}}); - VerifyUkm(test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName, - expected_form_submission_ukm_metrics); - - AppendFieldFillStatusUkm(form, &expected_field_fill_status_ukm_metrics); - VerifyUkm(test_ukm_recorder_, form, UkmFieldFillStatusType::kEntryName, - expected_field_fill_status_ukm_metrics); - } } // Verify that we correctly log the submitted form's state with fields 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 267ad01698e..f82bcdf9778 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc @@ -48,6 +48,8 @@ VerificationStatus ConvertSpecificsToProfileVerificationStatus( return VerificationStatus::kObserved; case sync_pb::AutofillProfileSpecifics_VerificationStatus_USER_VERIFIED: return VerificationStatus::kUserVerified; + case sync_pb::AutofillProfileSpecifics_VerificationStatus_SERVER_PARSED: + return VerificationStatus::kServerParsed; } } @@ -67,6 +69,8 @@ ConvertProfileToSpecificsVerificationStatus(VerificationStatus profile_status) { return sync_pb::AutofillProfileSpecifics_VerificationStatus_OBSERVED; case (VerificationStatus::kUserVerified): return sync_pb::AutofillProfileSpecifics_VerificationStatus_USER_VERIFIED; + case (VerificationStatus::kServerParsed): + return sync_pb::AutofillProfileSpecifics_VerificationStatus_SERVER_PARSED; } } diff --git a/chromium/components/autofill/core/browser/autofill_profile_sync_util.h b/chromium/components/autofill/core/browser/autofill_profile_sync_util.h index c3078c34aae..a43595262bf 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_sync_util.h +++ b/chromium/components/autofill/core/browser/autofill_profile_sync_util.h @@ -7,8 +7,6 @@ #include <memory> #include <string> -// TODO(crbug.com/904390): Remove when the investigation is over. -#include <vector> namespace syncer { struct EntityData; diff --git a/chromium/components/autofill/core/browser/autofill_profile_validator.cc b/chromium/components/autofill/core/browser/autofill_profile_validator.cc index e175fadb0e1..898069ea87f 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_validator.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_validator.cc @@ -8,7 +8,7 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/cancelable_callback.h" #include "base/check.h" #include "base/strings/utf_string_conversions.h" diff --git a/chromium/components/autofill/core/browser/autofill_provider.h b/chromium/components/autofill/core/browser/autofill_provider.h index 6eae4082e4b..1d9b93d8535 100644 --- a/chromium/components/autofill/core/browser/autofill_provider.h +++ b/chromium/components/autofill/core/browser/autofill_provider.h @@ -52,7 +52,8 @@ class AutofillProvider { bool known_success, mojom::SubmissionSource source) = 0; - virtual void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler) = 0; + virtual void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler, + bool had_interacted_form) = 0; virtual void OnFocusOnFormField(AutofillHandlerProxy* handler, const FormData& form, diff --git a/chromium/components/autofill/core/common/autofill_regex_constants.cc b/chromium/components/autofill/core/browser/autofill_regex_constants.cc index 97cae9baf26..60997b8bb58 100644 --- a/chromium/components/autofill/core/common/autofill_regex_constants.cc +++ b/chromium/components/autofill/core/browser/autofill_regex_constants.cc @@ -6,7 +6,7 @@ // different compilers, we use a script to convert the UTF8 strings into // numeric literals (\x##). -#include "components/autofill/core/common/autofill_regex_constants.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" namespace autofill { @@ -31,10 +31,16 @@ const char kCompanyRe[] = "|شرکت" // fa "|회사|직장"; // ko-KR const char kStreetNameRe[] = - "stra(ss|ß)e" // de - "|street" // en - "|rua|avenida"; // br -const char kHouseNumberRe[] = "(house |^)number|(haus|^)nummer|^número$"; + "stra(ss|ß)e" // de + "|street" // en + "|улица|название.?улицы" // ru + "|rua|avenida" // pt-PT, pt-BR + "|((?<!do |de )endereço)"; // pt-BR +const char kHouseNumberRe[] = + "(house.?|street.?|^)number" // en + "|(haus|^)nummer" // de + "|^\\*?.?número(.?\\*?$| da residência)" // pt-BR, pt-PT + "|дом|номер.?дома"; // ru const char kAddressLine1Re[] = "^address$|address[_-]?line(one)?|address1|addr1|street" "|(?:shipping|billing)address$" @@ -44,7 +50,7 @@ const char kAddressLine1Re[] = "|adresse" // fr-FR "|indirizzo" // it-IT "|^住所$|住所1" // ja-JP - "|morada|((?<!identificação do )endereço)" // pt-BR, pt-PT + "|morada|((?<!do |de )endereço)" // pt-BR, pt-PT "|Адрес" // ru "|地址" // zh-CN "|(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)" // tr @@ -121,8 +127,8 @@ const char kCityRe[] = "|ville|commune" // fr-FR "|localita" // it-IT "|市区町村" // ja-JP - "|cidade" // pt-BR, pt-PT - "|Город" // ru + "|cidade|município" // pt-BR, pt-PT + "|Город|Населённый.?пункт" // ru "|市" // zh-CN "|分區" // zh-TW "|شهر" // fa @@ -241,13 +247,13 @@ const char kExpirationYearRe[] = // - (optional) Exactly two adjacent m's before the y's. // - (optional) Separated by white-space and/or a dash or slash. // - (optional) Prepended with some text similar to "Expiration Date". -// Tested in components/autofill/core/common/autofill_regexes_unittest.cc +// Tested in components/autofill/core/browser/autofill_regexes_unittest.cc const char kExpirationDate2DigitYearRe[] = "(?:exp.*date[^y\\n\\r]*|mm\\s*[-/]?\\s*)yy(?:[^y]|$)"; // Used to match a expiration date field with a four digit year. // Same requirements as |kExpirationDate2DigitYearRe| except: // - Exactly four adjacent y's. -// Tested in components/autofill/core/common/autofill_regexes_unittest.cc +// Tested in components/autofill/core/browser/autofill_regexes_unittest.cc const char kExpirationDate4DigitYearRe[] = "(?:exp.*date[^y\\n\\r]*|mm\\s*[-/]?\\s*)yyyy(?:[^y]|$)"; // Used to match expiration date fields that do not specify a year length. diff --git a/chromium/components/autofill/core/common/autofill_regex_constants.h b/chromium/components/autofill/core/browser/autofill_regex_constants.h index 407600d571c..de9ef0ecba2 100644 --- a/chromium/components/autofill/core/common/autofill_regex_constants.h +++ b/chromium/components/autofill/core/browser/autofill_regex_constants.h @@ -2,8 +2,8 @@ // 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_AUTOFILL_REGEX_CONSTANTS_H_ -#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_REGEX_CONSTANTS_H_ +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_ namespace autofill { @@ -96,4 +96,4 @@ extern const char kUrlSearchActionRe[]; } // namespace autofill -#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_REGEX_CONSTANTS_H_ +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_ diff --git a/chromium/components/autofill/core/common/autofill_regexes.cc b/chromium/components/autofill/core/browser/autofill_regexes.cc index 02254fa266a..4875a113f8b 100644 --- a/chromium/components/autofill/core/common/autofill_regexes.cc +++ b/chromium/components/autofill/core/browser/autofill_regexes.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/autofill/core/common/autofill_regexes.h" +#include "components/autofill/core/browser/autofill_regexes.h" #include <memory> #include <unordered_map> @@ -43,7 +43,7 @@ class AutofillRegexes { icu::RegexMatcher* AutofillRegexes::GetMatcher(const base::string16& pattern) { auto it = matchers_.find(pattern); if (it == matchers_.end()) { - const icu::UnicodeString icu_pattern(FALSE, pattern.data(), + const icu::UnicodeString icu_pattern(false, pattern.data(), pattern.length()); UErrorCode status = U_ZERO_ERROR; @@ -71,21 +71,21 @@ bool MatchesPattern(const base::string16& input, base::AutoLock lock(*g_lock); icu::RegexMatcher* matcher = g_autofill_regexes->GetMatcher(pattern); - icu::UnicodeString icu_input(FALSE, input.data(), input.length()); + icu::UnicodeString icu_input(false, input.data(), input.length()); matcher->reset(icu_input); UErrorCode status = U_ZERO_ERROR; UBool matched = matcher->find(0, status); DCHECK(U_SUCCESS(status)); - if (matched == TRUE && match) { + if (matched && match) { icu::UnicodeString match_unicode = matcher->group(group_to_be_captured, status); DCHECK(U_SUCCESS(status)); *match = base::i18n::UnicodeStringToString16(match_unicode); } - return matched == TRUE; + return matched; } } // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_regexes.h b/chromium/components/autofill/core/browser/autofill_regexes.h index 837130b582c..585e88e20b6 100644 --- a/chromium/components/autofill/core/common/autofill_regexes.h +++ b/chromium/components/autofill/core/browser/autofill_regexes.h @@ -2,8 +2,8 @@ // 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_AUTOFILL_REGEXES_H_ -#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_REGEXES_H_ +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEXES_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEXES_H_ #include "base/strings/string16.h" @@ -20,4 +20,4 @@ bool MatchesPattern(const base::string16& input, } // namespace autofill -#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_REGEXES_H_ +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEXES_H_ diff --git a/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc b/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc new file mode 100644 index 00000000000..2adddede044 --- /dev/null +++ b/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc @@ -0,0 +1,232 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/autofill_regexes.h" + +#include <stddef.h> + +#include "base/macros.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::ASCIIToUTF16; + +namespace autofill { + +struct InputPatternTestCase { + const char* const input; + const char* const pattern; +}; + +class PositiveSampleTest : public testing::TestWithParam<InputPatternTestCase> { +}; + +TEST_P(PositiveSampleTest, SampleRegexes) { + auto test_case = GetParam(); + SCOPED_TRACE(test_case.input); + SCOPED_TRACE(test_case.pattern); + EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), + ASCIIToUTF16(test_case.pattern))); +} + +INSTANTIATE_TEST_SUITE_P(AutofillRegexes, + PositiveSampleTest, + testing::Values( + // Empty pattern + InputPatternTestCase{"", ""}, + InputPatternTestCase{ + "Look, ma' -- a non-empty string!", ""}, + // Substring + InputPatternTestCase{"string", "tri"}, + // Substring at beginning + InputPatternTestCase{"string", "str"}, + InputPatternTestCase{"string", "^str"}, + // Substring at end + InputPatternTestCase{"string", "ring"}, + InputPatternTestCase{"string", "ring$"}, + // Case-insensitive + InputPatternTestCase{"StRiNg", "string"})); + +class NegativeSampleTest : public testing::TestWithParam<InputPatternTestCase> { +}; + +TEST_P(NegativeSampleTest, SampleRegexes) { + auto test_case = GetParam(); + SCOPED_TRACE(test_case.input); + SCOPED_TRACE(test_case.pattern); + EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), + ASCIIToUTF16(test_case.pattern))); +} + +INSTANTIATE_TEST_SUITE_P(AutofillRegexes, + NegativeSampleTest, + testing::Values( + // Empty string + InputPatternTestCase{ + "", "Look, ma' -- a non-empty pattern!"}, + // Substring + InputPatternTestCase{"string", "trn"}, + // Substring at beginning + InputPatternTestCase{"string", " str"}, + InputPatternTestCase{"string", "^tri"}, + // Substring at end + InputPatternTestCase{"string", "ring "}, + InputPatternTestCase{"string", "rin$"})); + +struct InputTestCase { + const char* const input; +}; + +class ExpirationDate2DigitYearPositive + : public testing::TestWithParam<InputTestCase> {}; + +TEST_P(ExpirationDate2DigitYearPositive, ExpirationDate2DigitYearRegexes) { + auto test_case = GetParam(); + SCOPED_TRACE(test_case.input); + const base::string16 pattern = ASCIIToUTF16(kExpirationDate2DigitYearRe); + EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); +} + +INSTANTIATE_TEST_SUITE_P( + AutofillRegexes, + ExpirationDate2DigitYearPositive, + testing::Values(InputTestCase{"mm / yy"}, + InputTestCase{"mm/ yy"}, + InputTestCase{"mm /yy"}, + InputTestCase{"mm/yy"}, + InputTestCase{"mm - yy"}, + InputTestCase{"mm- yy"}, + InputTestCase{"mm -yy"}, + InputTestCase{"mm-yy"}, + InputTestCase{"mmyy"}, + // Complex two year cases + InputTestCase{"Expiration Date (MM / YY)"}, + InputTestCase{"Expiration Date (MM/YY)"}, + InputTestCase{"Expiration Date (MM - YY)"}, + InputTestCase{"Expiration Date (MM-YY)"}, + InputTestCase{"Expiration Date MM / YY"}, + InputTestCase{"Expiration Date MM/YY"}, + InputTestCase{"Expiration Date MM - YY"}, + InputTestCase{"Expiration Date MM-YY"}, + InputTestCase{"expiration date yy"}, + InputTestCase{"Exp Date (MM / YY)"})); + +class ExpirationDate2DigitYearNegative + : public testing::TestWithParam<InputTestCase> {}; + +TEST_P(ExpirationDate2DigitYearNegative, ExpirationDate2DigitYearRegexes) { + auto test_case = GetParam(); + SCOPED_TRACE(test_case.input); + const base::string16 pattern = ASCIIToUTF16(kExpirationDate2DigitYearRe); + EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); +} + +INSTANTIATE_TEST_SUITE_P( + AutofillRegexes, + ExpirationDate2DigitYearNegative, + testing::Values(InputTestCase{""}, + InputTestCase{"Look, ma' -- an invalid string!"}, + InputTestCase{"mmfavouritewordyy"}, + InputTestCase{"mm a yy"}, + InputTestCase{"mm a yyyy"}, + // Simple four year cases + InputTestCase{"mm / yyyy"}, + InputTestCase{"mm/ yyyy"}, + InputTestCase{"mm /yyyy"}, + InputTestCase{"mm/yyyy"}, + InputTestCase{"mm - yyyy"}, + InputTestCase{"mm- yyyy"}, + InputTestCase{"mm -yyyy"}, + InputTestCase{"mm-yyyy"}, + InputTestCase{"mmyyyy"}, + // Complex four year cases + InputTestCase{"Expiration Date (MM / YYYY)"}, + InputTestCase{"Expiration Date (MM/YYYY)"}, + InputTestCase{"Expiration Date (MM - YYYY)"}, + InputTestCase{"Expiration Date (MM-YYYY)"}, + InputTestCase{"Expiration Date MM / YYYY"}, + InputTestCase{"Expiration Date MM/YYYY"}, + InputTestCase{"Expiration Date MM - YYYY"}, + InputTestCase{"Expiration Date MM-YYYY"}, + InputTestCase{"expiration date yyyy"}, + InputTestCase{"Exp Date (MM / YYYY)"})); + +class ExpirationDate4DigitYearPositive + : public testing::TestWithParam<InputTestCase> {}; + +TEST_P(ExpirationDate4DigitYearPositive, ExpirationDate4DigitYearRegexes) { + auto test_case = GetParam(); + const base::string16 pattern = ASCIIToUTF16(kExpirationDate4DigitYearRe); + SCOPED_TRACE(test_case.input); + EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); +} + +INSTANTIATE_TEST_SUITE_P(AutofillRegexes, + ExpirationDate4DigitYearPositive, + testing::Values( + // Simple four year cases + InputTestCase{"mm / yyyy"}, + InputTestCase{"mm/ yyyy"}, + InputTestCase{"mm /yyyy"}, + InputTestCase{"mm/yyyy"}, + InputTestCase{"mm - yyyy"}, + InputTestCase{"mm- yyyy"}, + InputTestCase{"mm -yyyy"}, + InputTestCase{"mm-yyyy"}, + InputTestCase{"mmyyyy"}, + // Complex four year cases + InputTestCase{"Expiration Date (MM / YYYY)"}, + InputTestCase{"Expiration Date (MM/YYYY)"}, + InputTestCase{"Expiration Date (MM - YYYY)"}, + InputTestCase{"Expiration Date (MM-YYYY)"}, + InputTestCase{"Expiration Date MM / YYYY"}, + InputTestCase{"Expiration Date MM/YYYY"}, + InputTestCase{"Expiration Date MM - YYYY"}, + InputTestCase{"Expiration Date MM-YYYY"}, + InputTestCase{"expiration date yyyy"}, + InputTestCase{"Exp Date (MM / YYYY)"})); + +class ExpirationDate4DigitYearNegative + : public testing::TestWithParam<InputTestCase> {}; + +TEST_P(ExpirationDate4DigitYearNegative, ExpirationDate4DigitYearRegexes) { + auto test_case = GetParam(); + const base::string16 pattern = ASCIIToUTF16(kExpirationDate4DigitYearRe); + SCOPED_TRACE(test_case.input); + EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); +} + +INSTANTIATE_TEST_SUITE_P( + AutofillRegexes, + ExpirationDate4DigitYearNegative, + testing::Values(InputTestCase{""}, + InputTestCase{"Look, ma' -- an invalid string!"}, + InputTestCase{"mmfavouritewordyy"}, + InputTestCase{"mm a yy"}, + InputTestCase{"mm a yyyy"}, + // Simple two year cases + InputTestCase{"mm / yy"}, + InputTestCase{"mm/ yy"}, + InputTestCase{"mm /yy"}, + InputTestCase{"mm/yy"}, + InputTestCase{"mm - yy"}, + InputTestCase{"mm- yy"}, + InputTestCase{"mm -yy"}, + InputTestCase{"mm-yy"}, + InputTestCase{"mmyy"}, + // Complex two year cases + InputTestCase{"Expiration Date (MM / YY)"}, + InputTestCase{"Expiration Date (MM/YY)"}, + InputTestCase{"Expiration Date (MM - YY)"}, + InputTestCase{"Expiration Date (MM-YY)"}, + InputTestCase{"Expiration Date MM / YY"}, + InputTestCase{"Expiration Date MM/YY"}, + InputTestCase{"Expiration Date MM - YY"}, + InputTestCase{"Expiration Date MM-YY"}, + InputTestCase{"expiration date yy"}, + InputTestCase{"Exp Date (MM / YY)"})); + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/address.cc b/chromium/components/autofill/core/browser/data_model/address.cc index cd7f8e6dd57..284283981ee 100644 --- a/chromium/components/autofill/core/browser/data_model/address.cc +++ b/chromium/components/autofill/core/browser/data_model/address.cc @@ -65,7 +65,17 @@ bool Address::FinalizeAfterImport(bool profile_is_verified) { // fully launched. if (structured_address::StructuredAddressesEnabled()) { structured_address_.MigrateLegacyStructure(profile_is_verified); - return structured_address_.CompleteFullTree(); + bool result = structured_address_.CompleteFullTree(); + // If the address could not be completed, it is possible that it contains an + // invalid structure. + if (!result) { + if (structured_address_.WipeInvalidStructure()) { + // If the structure was wiped because it is invalid, try to complete the + // address again. + result = structured_address_.CompleteFullTree(); + } + } + return result; } return true; } @@ -158,6 +168,22 @@ void Address::SetRawInfoWithVerificationStatus(ServerFieldType type, // TODO(crbug.com/1130194): Clean legacy implementation once structured // addresses are fully launched. if (structured_address::StructuredAddressesEnabled()) { + // The street address has a structure that may have already been set before + // using the settings dialog. In case the settings dialog was used to change + // the address to contain different tokens, the structure must be reset. + if (type == ADDRESS_HOME_STREET_ADDRESS) { + const base::string16 current_value = + structured_address_.GetValueForType(type); + if (!current_value.empty()) { + bool token_equivalent = structured_address::AreStringTokenEquivalent( + value, structured_address_.GetValueForType(type)); + structured_address_.SetValueForTypeIfPossible( + ADDRESS_HOME_STREET_ADDRESS, value, status, + /*invalidate_child_nodes=*/!token_equivalent); + return; + } + } + structured_address_.SetValueForTypeIfPossible(type, value, status); return; } @@ -366,21 +392,17 @@ bool Address::SetInfoWithVerificationStatusImpl(const AutofillType& type, if (type.html_type() == HTML_TYPE_COUNTRY_CODE) { std::string country_code = base::ToUpperASCII(base::UTF16ToASCII(value)); if (!data_util::IsValidCountryCode(country_code)) { - // Some popular websites use the HTML_TYPE_COUNTRY_CODE attribute for - // full text names (e.g. alliedelec.com). Try to convert the value to a - // country code as a fallback. - if (base::FeatureList::IsEnabled( - features::kAutofillAllowHtmlTypeCountryCodesWithFullNames)) { - CountryNames* country_names = - !value.empty() ? CountryNames::GetInstance() : nullptr; - country_code = - country_names - ? country_names->GetCountryCodeForLocalizedCountryName(value, - locale) - : std::string(); - } else { - country_code = std::string(); - } + // To counteract the misuse of autocomplete=country attribute when used + // with full country names, if the supplied country code is not a valid, + // it is tested if a country code can be derived from the value when it is + // interpreted as a full country name. Otherwise an empty string is + // assigned to |country_code|. + CountryNames* country_names = + !value.empty() ? CountryNames::GetInstance() : nullptr; + country_code = country_names + ? country_names->GetCountryCodeForLocalizedCountryName( + value, locale) + : std::string(); } // TODO(crbug.com/1130194): Clean legacy implementation once structured 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 8a3f79fe3da..ee3aed0d76c 100644 --- a/chromium/components/autofill/core/browser/data_model/address_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/address_unittest.cc @@ -84,12 +84,6 @@ TEST_P(AddressTest, SetHtmlCountryCodeTypeWithFullCountryName) { Address address; EXPECT_EQ(base::string16(), address.GetRawInfo(ADDRESS_HOME_COUNTRY)); - // Enable the feature that allows for full country names although the - // field type explicitly set to HTML_TYPE_COUNTRY_CODE. - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature( - features::kAutofillAllowHtmlTypeCountryCodesWithFullNames); - // Create an autofill type from HTML_TYPE_COUNTRY_CODE. AutofillType autofill_type(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE); @@ -131,18 +125,6 @@ TEST_P(AddressTest, SetHtmlCountryCodeTypeWithFullCountryName) { AutofillType(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE), "en-US"); EXPECT_EQ(ASCIIToUTF16("DE"), actual_country_code); EXPECT_EQ(ASCIIToUTF16("Germany"), actual_country); - - // By disabling the feature, test that the country name deduction actually - // uses the path for HTML_TYPE_COUNTRY_CODE. - feature.Reset(); - feature.InitAndDisableFeature( - features::kAutofillAllowHtmlTypeCountryCodesWithFullNames); - address.SetInfo(autofill_type, ASCIIToUTF16("Germany"), "en-US"); - actual_country = address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US"); - actual_country_code = address.GetInfo( - AutofillType(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE), "en-US"); - EXPECT_EQ(ASCIIToUTF16(""), actual_country); - EXPECT_EQ(ASCIIToUTF16(""), actual_country_code); } // Test that we properly detect country codes appropriate for each country. @@ -687,6 +669,71 @@ TEST_P(AddressTest, TestGettingTheStructuredAddress) { base::UTF8ToUTF16("12345")); } +// For structured address, test that the structured information is wiped +// correctly when the unstructured street address changes. +TEST_P(AddressTest, ResetStructuredTokens) { + // This test is only applicable for structured addresses. + if (!StructuredAddresses()) + return; + + Address address; + // Set a structured address line and call the finalization routine. + address.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, base::ASCIIToUTF16("Erika-Mann-Str 12"), + structured_address::VerificationStatus::kUserVerified); + address.FinalizeAfterImport(); + + // Verify that structured tokens have been assigned correctly. + EXPECT_EQ(address.GetRawInfo(ADDRESS_HOME_STREET_NAME), + base::ASCIIToUTF16("Erika-Mann-Str")); + EXPECT_EQ(address.GetVerificationStatus(ADDRESS_HOME_STREET_NAME), + structured_address::VerificationStatus::kParsed); + ASSERT_EQ(address.GetRawInfo(ADDRESS_HOME_HOUSE_NUMBER), + base::ASCIIToUTF16("12")); + EXPECT_EQ(address.GetVerificationStatus(ADDRESS_HOME_HOUSE_NUMBER), + structured_address::VerificationStatus::kParsed); + + // Lift the verification status of the house number to be |kObserved|. + address.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_HOUSE_NUMBER, base::ASCIIToUTF16("12"), + structured_address::VerificationStatus::kObserved); + EXPECT_EQ(address.GetVerificationStatus(ADDRESS_HOME_HOUSE_NUMBER), + structured_address::VerificationStatus::kObserved); + + // Now, set a new unstructured street address that has the same tokens in a + // different order. + address.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, base::ASCIIToUTF16("12 Erika-Mann-Str"), + structured_address::VerificationStatus::kUserVerified); + + // After this operation, the structure should be maintained including the + // observed status of the house number. + EXPECT_EQ(address.GetRawInfo(ADDRESS_HOME_STREET_NAME), + base::ASCIIToUTF16("Erika-Mann-Str")); + EXPECT_EQ(address.GetVerificationStatus(ADDRESS_HOME_STREET_NAME), + structured_address::VerificationStatus::kParsed); + ASSERT_EQ(address.GetRawInfo(ADDRESS_HOME_HOUSE_NUMBER), + base::ASCIIToUTF16("12")); + EXPECT_EQ(address.GetVerificationStatus(ADDRESS_HOME_HOUSE_NUMBER), + structured_address::VerificationStatus::kObserved); + + // Now set a different street address. + address.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, base::ASCIIToUTF16("Marienplatz"), + structured_address::VerificationStatus::kUserVerified); + + // The set address is not parsable and the this should unset both the street + // name and the house number. + EXPECT_EQ(address.GetRawInfo(ADDRESS_HOME_STREET_NAME), + base::ASCIIToUTF16("")); + EXPECT_EQ(address.GetVerificationStatus(ADDRESS_HOME_STREET_NAME), + structured_address::VerificationStatus::kNoStatus); + ASSERT_EQ(address.GetRawInfo(ADDRESS_HOME_HOUSE_NUMBER), + base::ASCIIToUTF16("")); + EXPECT_EQ(address.GetVerificationStatus(ADDRESS_HOME_HOUSE_NUMBER), + structured_address::VerificationStatus::kNoStatus); +} + // Runs the suite with the feature // |kAutofillSupportForStructuredStructuredNames| enabled and disabled. // TODO(crbug.com/1130194): Remove parameterized test once structured addresses 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 daf20027c29..7da13d15f85 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 @@ -72,8 +72,8 @@ double AutofillDataModel::GetFrecencyScore(base::Time time) const { // of the profile and leveraging the properties of the logarithmic function. // DaysSinceLastUse() and |use_count_| are offset because their minimum values // are respectively 0 and 1 but the formula requires at least a value of 2. - // Please update getFrecencyScore in PaymentRequestImpl.java as well if below - // formula needs update. + // Please update getFrecencyScore in ChromePaymentRequestService.java as well + // if below formula needs update. return -log((time - use_date_).InDays() + 2) / log(use_count_ + 1); } 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 94e43c09ffa..e9668df416b 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile.cc @@ -449,15 +449,20 @@ int AutofillProfile::Compare(const AutofillProfile& profile) const { for (ServerFieldType type : types) { int comparison = GetRawInfo(type).compare(profile.GetRawInfo(type)); - if (comparison != 0) + if (comparison != 0) { return comparison; + } } for (ServerFieldType type : types) { - if (GetVerificationStatus(type) < profile.GetVerificationStatus(type)) + if (structured_address::IsLessSignificantVerificationStatus( + GetVerificationStatus(type), profile.GetVerificationStatus(type))) { return -1; - if (GetVerificationStatus(type) > profile.GetVerificationStatus(type)) + } + if (structured_address::IsLessSignificantVerificationStatus( + profile.GetVerificationStatus(type), GetVerificationStatus(type))) { return 1; + } } // TODO(crbug.com/1130194): Remove feature check once structured addresses are @@ -474,15 +479,22 @@ int AutofillProfile::Compare(const AutofillProfile& profile) const { }; for (ServerFieldType type : new_types) { int comparison = GetRawInfo(type).compare(profile.GetRawInfo(type)); - if (comparison != 0) + if (comparison != 0) { return comparison; + } } - for (ServerFieldType type : new_types) { - if (GetVerificationStatus(type) < profile.GetVerificationStatus(type)) + for (ServerFieldType type : types) { + if (structured_address::IsLessSignificantVerificationStatus( + GetVerificationStatus(type), + profile.GetVerificationStatus(type))) { return -1; - if (GetVerificationStatus(type) > profile.GetVerificationStatus(type)) + } + if (structured_address::IsLessSignificantVerificationStatus( + profile.GetVerificationStatus(type), + GetVerificationStatus(type))) { return 1; + } } } @@ -637,35 +649,47 @@ bool AutofillProfile::MergeStructuredDataFrom(const AutofillProfile& profile, // Should only be called if the profile is already verified. DCHECK(IsVerified()); - // Only applicable for structured names. - if (!base::FeatureList::IsEnabled( - features::kAutofillEnableSupportForMoreStructureInNames)) { - return false; - } - AutofillProfileComparator comparator(app_locale); - NameInfo name; + DVLOG(1) << "Merging profile structure information :\nSource = " << profile << "\nDest = " << *this; - // It is already verified upstream that the profiles and therefore also the - // names are mergeable. - // However, the structure should only be merged if the full names are token - // equivalent. - if (!structured_address::AreStringTokenEquivalent( - GetRawInfo(NAME_FULL), profile.GetRawInfo(NAME_FULL))) - return false; + bool merged = false; - if (!comparator.MergeNames(profile, *this, &name)) { - NOTREACHED(); - return false; + // It is already verified upstream that the profiles and therefore also the + // names and addresses are mergeable. + // However, the structure should only be merged if the full names or addresses + // are token equivalent. + if (structured_address::StructuredNamesEnabled() && + structured_address::AreStringTokenEquivalent( + GetRawInfo(NAME_FULL), profile.GetRawInfo(NAME_FULL))) { + NameInfo name; + if (!comparator.MergeNames(profile, *this, &name)) { + NOTREACHED(); + return false; + } + if (name_ != name) { + name_ = name; + merged = true; + } } - if (name_ != name) { - name_ = name; - return true; + if (structured_address::StructuredAddressesEnabled() && + structured_address::AreStringTokenEquivalent( + GetRawInfo(ADDRESS_HOME_STREET_ADDRESS), + profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS))) { + Address address; + if (!comparator.MergeAddresses(profile, *this, &address)) { + NOTREACHED(); + return false; + } + if (address_ != address) { + address_ = address; + merged = true; + } } - return false; + + return merged; } bool AutofillProfile::MergeDataFrom(const AutofillProfile& profile, 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 9c39c693e56..e24375a36eb 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 @@ -122,7 +122,7 @@ NormalizingIterator::NormalizingIterator( : previous_was_skippable_(false), collapse_skippable_(whitespace_spec == AutofillProfileComparator::RETAIN_WHITESPACE), - iter_(base::i18n::UTF16CharIterator(text.data(), text.length())) { + iter_(text) { int32_t character = iter_.get(); while (!iter_.end() && IsPunctuationOrWhitespace(u_charType(character))) { @@ -282,8 +282,7 @@ base::string16 AutofillProfileComparator::NormalizeForComparison( base::string16 result; result.reserve(text.length()); bool previous_was_whitespace = (whitespace_spec == RETAIN_WHITESPACE); - for (base::i18n::UTF16CharIterator iter(text.data(), text.length()); - !iter.end(); iter.Advance()) { + for (base::i18n::UTF16CharIterator iter(text); !iter.end(); iter.Advance()) { if (IsPunctuationOrWhitespace(u_charType(iter.get()))) { if (!previous_was_whitespace && whitespace_spec == RETAIN_WHITESPACE) { result.push_back(' '); 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 c745377720b..b5fa1ea03e3 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 @@ -4,7 +4,6 @@ #include "components/autofill/core/browser/data_model/autofill_structured_address.h" -#include <iostream> #include <utility> #include "base/i18n/case_conversion.h" #include "base/strings/strcat.h" @@ -136,7 +135,8 @@ void StreetAddress::ParseValueAndAssignSubcomponentsByFallbackMethod() { bool StreetAddress::HasNewerValuePrecendenceInMerging( const AddressComponent& newer_component) const { // If the newer component has a better verification status, use the newer one. - if (GetVerificationStatus() < newer_component.GetVerificationStatus()) + if (IsLessSignificantVerificationStatus( + GetVerificationStatus(), newer_component.GetVerificationStatus())) return true; // If the verification statuses are the same, do not use the newer component @@ -353,6 +353,13 @@ Address::Address(const Address& other) : Address() { *this = 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(); +} + // Addresses are mergeable when all of their children are mergeable. // Reformat the address from their children after merge. Address::Address(AddressComponent* parent) @@ -367,7 +374,6 @@ Address::~Address() = default; void Address::MigrateLegacyStructure(bool is_verified_profile) { // If this component already has a verification status, no profile is regarded // as already verified. - std::cout << "APply migration" << std::endl; if (GetVerificationStatus() != VerificationStatus::kNoStatus) return; 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 1e2c0201dbd..7e872bfc114 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 @@ -226,6 +226,10 @@ class Address : public AddressComponent { // a status. void MigrateLegacyStructure(bool is_verified_profile); + // Checks if the street address contains an invalid structure and wipes it if + // necessary. + bool WipeInvalidStructure() override; + private: StreetAddress street_address_{this}; PostalCode postal_code_{this}; 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 fccb0bd9725..50e6e86f2d2 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 @@ -27,6 +27,28 @@ namespace autofill { namespace structured_address { +bool IsLessSignificantVerificationStatus(VerificationStatus left, + VerificationStatus right) { + // Both the KUserVerified and kObserved are larger then kServerParsed although + // the underlying integer suggests differently. + if (left == VerificationStatus::kServerParsed && + (right == VerificationStatus::kObserved || + right == VerificationStatus::kUserVerified)) { + return true; + } + + if (right == VerificationStatus::kServerParsed && + (left == VerificationStatus::kObserved || + left == VerificationStatus::kUserVerified)) { + return false; + } + + // In all other cases, it is sufficient to compare the underlying integer + // values. + return static_cast<std::underlying_type_t<VerificationStatus>>(left) < + static_cast<std::underlying_type_t<VerificationStatus>>(right); +} + AddressComponent::AddressComponent(ServerFieldType storage_type, AddressComponent* parent, std::vector<AddressComponent*> subcomponents, @@ -482,6 +504,27 @@ void AddressComponent::ParseValueAndAssignSubcomponentsByFallbackMethod() { DCHECK(success); } +bool AddressComponent::WipeInvalidStructure() { + if (IsAtomic()) { + return false; + } + + // 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 false; +} + void AddressComponent::FormatValueFromSubcomponents() { // Get the most suited format string. base::string16 format_string = GetBestFormatString(); @@ -697,7 +740,7 @@ void AddressComponent::UnsetParsedAndFormattedValuesInEntireTree() { void AddressComponent::MergeVerificationStatuses( const AddressComponent& newer_component) { if (IsValueAssigned() && (GetValue() == newer_component.GetValue()) && - (GetVerificationStatus() < newer_component.GetVerificationStatus())) { + HasNewerValuePrecendenceInMerging(newer_component)) { value_verification_status_ = newer_component.GetVerificationStatus(); } @@ -807,7 +850,7 @@ bool AddressComponent::MergeWithComponent( // If the normalized values are the same, optimize the verification status. if ((merge_mode_ & kUseBetterOrNewerForSameValue) && (value == value_newer)) { - if (newer_component.GetVerificationStatus() >= GetVerificationStatus()) { + if (HasNewerValuePrecendenceInMerging(newer_component)) { *this = newer_component; } return true; @@ -900,7 +943,8 @@ bool AddressComponent::MergeWithComponent( bool AddressComponent::HasNewerValuePrecendenceInMerging( const AddressComponent& newer_component) const { - return newer_component.GetVerificationStatus() >= GetVerificationStatus(); + return !IsLessSignificantVerificationStatus( + newer_component.GetVerificationStatus(), GetVerificationStatus()); } bool AddressComponent::MergeTokenEquivalentComponent( @@ -1109,6 +1153,7 @@ int AddressComponent::GetStructureVerificationScore() const { case VerificationStatus::kNoStatus: case VerificationStatus::kParsed: case VerificationStatus::kFormatted: + case VerificationStatus::kServerParsed: break; case VerificationStatus::kObserved: result += 1; 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 9cfc4d1c310..7adba19a86e 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 @@ -37,8 +37,15 @@ enum class VerificationStatus { kObserved = 3, // The user used the autofill settings to verify and store this token. kUserVerified = 4, + // The token was parsed by the server. + kServerParsed = 5, }; +// Returns true if |left| has a less significant verification status compared to +// |right|. +bool IsLessSignificantVerificationStatus(VerificationStatus left, + VerificationStatus right); + // The merge mode defines if and how two components are merged. enum MergeMode { // If one component has an empty value, use the non-empty one. @@ -57,8 +64,7 @@ enum MergeMode { kUseNewerIfDifferent = 1 << 5, // If the newer component contains one token more, apply a recursive strategy // to merge the tokens. - kRecursivelyMergeSingleTokenSubset = - 1 << 6 | kRecursivelyMergeTokenEquivalentValues, + kRecursivelyMergeSingleTokenSubset = 1 << 6, // If one is a substring use the most recent one. kUseMostRecentSubstring = 1 << 7, // Merge the child nodes and reformat the node from its children after merge. @@ -335,6 +341,11 @@ 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. + virtual bool WipeInvalidStructure(); + #ifdef UNIT_TEST // Initiates the formatting of the values from the subcomponents. void FormatValueFromSubcomponentsForTesting() { 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 2a4a7fd1c8f..a9356b83cd8 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 @@ -1661,5 +1661,26 @@ TEST(AutofillStructuredAddressAddressComponent, MergeChildsAndReformatRoot) { VerifyTestValues(&older, older_values); } +// Test the comparison of different Verification statuses. +TEST(AutofillStructuredAddressAddressComponent, + TestIsLessSignificantVerificationStatus) { + EXPECT_TRUE(IsLessSignificantVerificationStatus( + VerificationStatus::kParsed, VerificationStatus::kFormatted)); + EXPECT_TRUE(IsLessSignificantVerificationStatus( + VerificationStatus::kParsed, VerificationStatus::kServerParsed)); + EXPECT_TRUE(IsLessSignificantVerificationStatus( + VerificationStatus::kServerParsed, VerificationStatus::kObserved)); + EXPECT_TRUE(IsLessSignificantVerificationStatus( + VerificationStatus::kServerParsed, VerificationStatus::kUserVerified)); + EXPECT_FALSE(IsLessSignificantVerificationStatus( + VerificationStatus::kServerParsed, VerificationStatus::kFormatted)); + EXPECT_FALSE(IsLessSignificantVerificationStatus( + VerificationStatus::kServerParsed, VerificationStatus::kParsed)); + EXPECT_FALSE(IsLessSignificantVerificationStatus( + VerificationStatus::kObserved, VerificationStatus::kServerParsed)); + EXPECT_FALSE(IsLessSignificantVerificationStatus( + VerificationStatus::kUserVerified, VerificationStatus::kServerParsed)); +} + } // 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 ed1ffa76558..ea5ba89c845 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 @@ -11,7 +11,6 @@ #include "base/feature_list.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/data_model/autofill_structured_address_test_utils.h" #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h" #include "components/autofill/core/common/autofill_features.h" 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 07f58617c0e..ba400f32f08 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 @@ -267,11 +267,12 @@ std::string ParseFirstMiddleLastNameExpression() { return CaptureTypeWithPattern( NAME_FULL, {CaptureTypeWithPattern(NAME_HONORIFIC_PREFIX, kHonorificPrefixRe, - {.quantifier = MATCH_OPTIONAL}), + CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPattern(NAME_FIRST, kSingleWordRe, - {.quantifier = MATCH_OPTIONAL}), - CaptureTypeWithPattern(NAME_MIDDLE, kMultipleLazyWordsRe, - {.quantifier = MATCH_LAZY_OPTIONAL}), + CaptureOptions{.quantifier = MATCH_OPTIONAL}), + CaptureTypeWithPattern( + NAME_MIDDLE, kMultipleLazyWordsRe, + CaptureOptions{.quantifier = MATCH_LAZY_OPTIONAL}), CaptureTypeWithPattern(NAME_LAST, {kOptionalLastNamePrefixRe, kSingleWordRe}), kOptionalLastNameSuffixRe}); @@ -288,14 +289,15 @@ std::string ParseLastCommaFirstMiddleExpression() { return CaptureTypeWithPattern( NAME_FULL, {CaptureTypeWithPattern(NAME_HONORIFIC_PREFIX, kHonorificPrefixRe, - {.quantifier = MATCH_OPTIONAL}), + CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPattern(NAME_LAST, {kOptionalLastNamePrefixRe, kSingleWordRe}, {.separator = "\\s*,\\s*"}), CaptureTypeWithPattern(NAME_FIRST, kSingleWordRe, - {.quantifier = MATCH_OPTIONAL}), - CaptureTypeWithPattern(NAME_MIDDLE, kMultipleLazyWordsRe, - {.quantifier = MATCH_LAZY_OPTIONAL})}); + CaptureOptions{.quantifier = MATCH_OPTIONAL}), + CaptureTypeWithPattern( + NAME_MIDDLE, kMultipleLazyWordsRe, + CaptureOptions{.quantifier = MATCH_LAZY_OPTIONAL})}); } // Returns an expression to parse an Hispanic/Latinx last name. @@ -313,7 +315,7 @@ std::string ParseHispanicLastNameExpression() { {kOptionalLastNamePrefixRe, kSingleWordRe}), CaptureTypeWithPattern(NAME_LAST_CONJUNCTION, kHispanicLastNameConjunctionsRe, - {.quantifier = MATCH_OPTIONAL}), + CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPattern(NAME_LAST_SECOND, {kOptionalLastNamePrefixRe, kSingleWordRe})}); } @@ -325,9 +327,10 @@ std::string ParseHispanicFullNameExpression() { return CaptureTypeWithPattern( NAME_FULL, {CaptureTypeWithPattern(NAME_HONORIFIC_PREFIX, kHonorificPrefixRe, - {.quantifier = MATCH_OPTIONAL}), - CaptureTypeWithPattern(NAME_FIRST, kMultipleLazyWordsRe, - {.quantifier = MATCH_LAZY_OPTIONAL}), + CaptureOptions{.quantifier = MATCH_OPTIONAL}), + CaptureTypeWithPattern( + NAME_FIRST, kMultipleLazyWordsRe, + CaptureOptions{.quantifier = MATCH_LAZY_OPTIONAL}), ParseHispanicLastNameExpression()}); } @@ -358,14 +361,15 @@ std::string ParseStreetNameHouseNumberExpression() { CaptureTypeWithPattern( ADDRESS_HOME_SUBPREMISE, { - CaptureTypeWithPrefixedPattern(ADDRESS_HOME_FLOOR, kFloorAffixRe, - "(?:(\\d{0,3}\\w?))", - {.quantifier = MATCH_OPTIONAL}), + CaptureTypeWithPrefixedPattern( + ADDRESS_HOME_FLOOR, kFloorAffixRe, "(?:(\\d{0,3}\\w?))", + CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPrefixedPattern( ADDRESS_HOME_APT_NUM, kApartmentNumberPrefix, - "(?:(\\d{0,3}\\w?))", {.quantifier = MATCH_OPTIONAL}), + "(?:(\\d{0,3}\\w?))", + CaptureOptions{.quantifier = MATCH_OPTIONAL}), }, - {.quantifier = MATCH_OPTIONAL})}); + CaptureOptions{.quantifier = MATCH_OPTIONAL})}); } // Returns an expression to parse a street address into the street name, the @@ -392,12 +396,13 @@ std::string ParseStreetNameHouseNumberExpressionSuffixedFloor() { { CaptureTypeWithSuffixedPattern( ADDRESS_HOME_FLOOR, "(?:(\\d{0,3}\\w?))", kFloorAffixRe, - {.quantifier = MATCH_OPTIONAL}), + CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPrefixedPattern( ADDRESS_HOME_APT_NUM, kApartmentNumberPrefix, - "(?:(\\d{0,3}\\w?))", {.quantifier = MATCH_OPTIONAL}), + "(?:(\\d{0,3}\\w?))", + CaptureOptions{.quantifier = MATCH_OPTIONAL}), }, - {.quantifier = MATCH_OPTIONAL})}); + CaptureOptions{.quantifier = MATCH_OPTIONAL})}); } // Returns an expression to parse a street address into the street name, the @@ -417,14 +422,15 @@ std::string ParseHouseNumberStreetNameExpression() { CaptureTypeWithPattern( ADDRESS_HOME_SUBPREMISE, { - CaptureTypeWithPrefixedPattern(ADDRESS_HOME_FLOOR, kFloorAffixRe, - "(?:(\\d{0,3}\\w?))", - {.quantifier = MATCH_OPTIONAL}), + CaptureTypeWithPrefixedPattern( + ADDRESS_HOME_FLOOR, kFloorAffixRe, "(?:(\\d{0,3}\\w?))", + CaptureOptions{.quantifier = MATCH_OPTIONAL}), CaptureTypeWithPrefixedPattern( ADDRESS_HOME_APT_NUM, kApartmentNumberPrefix, - "(?:(\\d{0,3}\\w?))", {.quantifier = MATCH_OPTIONAL}), + "(?:(\\d{0,3}\\w?))", + CaptureOptions{.quantifier = MATCH_OPTIONAL}), }, - {.quantifier = MATCH_OPTIONAL})}); + CaptureOptions{.quantifier = MATCH_OPTIONAL})}); } } // namespace 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 1a8bf503d31..c00710a40d4 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 @@ -12,7 +12,6 @@ #include "base/feature_list.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/data_model/autofill_structured_address_test_utils.h" #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h" #include "components/autofill/core/common/autofill_features.h" @@ -482,6 +481,67 @@ TEST(AutofillStructuredAddress, TestMigrationAndFinalization_AlreadyMigrated) { // Verify that the address was not changed by the migration. VerifyTestValues(&address, test_values); } + +// Tests that a valid address structure is not wiped. +TEST(AutofillStructuredAddress, + TestWipingAnInvalidSubstructure_ValidStructure) { + Address address; + AddressComponentTestValues address_with_valid_structure = { + // This structure is valid because all structured components are contained + // in the unstructured representation. + {.type = ADDRESS_HOME_STREET_ADDRESS, + .value = "123 Street name", + .status = VerificationStatus::kObserved}, + {.type = ADDRESS_HOME_STREET_NAME, + .value = "Street name", + .status = VerificationStatus::kParsed}, + {.type = ADDRESS_HOME_HOUSE_NUMBER, + .value = "123", + .status = VerificationStatus::kParsed}, + }; + + SetTestValues(&address, address_with_valid_structure, /*finalize=*/false); + + EXPECT_FALSE(address.WipeInvalidStructure()); + VerifyTestValues(&address, address_with_valid_structure); +} + +// Tests that an invalid address structure is wiped. +TEST(AutofillStructuredAddress, + TestWipingAnInvalidSubstructure_InValidStructure) { + Address address; + AddressComponentTestValues address_with_valid_structure = { + {.type = ADDRESS_HOME_STREET_ADDRESS, + .value = "Some other name", + .status = VerificationStatus::kObserved}, + {.type = ADDRESS_HOME_STREET_NAME, + .value = "Street name", + .status = VerificationStatus::kParsed}, + // The structure is invalid, because the house number is not contained in + // the unstructured street address. + {.type = ADDRESS_HOME_HOUSE_NUMBER, + .value = "123", + .status = VerificationStatus::kParsed}, + }; + + SetTestValues(&address, address_with_valid_structure, /*finalize=*/false); + + EXPECT_TRUE(address.WipeInvalidStructure()); + + AddressComponentTestValues address_with_wiped_structure = { + {.type = ADDRESS_HOME_STREET_ADDRESS, + .value = "Some other name", + .status = VerificationStatus::kObserved}, + {.type = ADDRESS_HOME_STREET_NAME, + .value = "", + .status = VerificationStatus::kNoStatus}, + {.type = ADDRESS_HOME_HOUSE_NUMBER, + .value = "", + .status = VerificationStatus::kNoStatus}, + }; + VerifyTestValues(&address, address_with_wiped_structure); +} + } // namespace } // namespace structured_address } // namespace autofill 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 8464c16d6a7..bd31bdcaa3a 100644 --- a/chromium/components/autofill/core/browser/data_model/contact_info.cc +++ b/chromium/components/autofill/core/browser/data_model/contact_info.cc @@ -14,12 +14,12 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_data_util.h" +#include "components/autofill/core/browser/autofill_regexes.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" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_l10n_util.h" -#include "components/autofill/core/common/autofill_regexes.h" namespace autofill { 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 022464200ad..484b13711c5 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card.cc +++ b/chromium/components/autofill/core/browser/data_model/credit_card.cc @@ -27,6 +27,7 @@ #include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_metrics.h" +#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/data_model/autofill_metadata.h" #include "components/autofill/core/browser/data_model/data_model_utils.h" @@ -35,7 +36,6 @@ #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_regexes.h" #include "components/autofill/core/common/form_field_data.h" #include "components/grit/components_scaled_resources.h" #include "components/strings/grit/components_strings.h" diff --git a/chromium/components/autofill/core/browser/data_model/data_model_utils.cc b/chromium/components/autofill/core/browser/data_model/data_model_utils.cc index 518d6a7949d..023b70402cc 100644 --- a/chromium/components/autofill/core/browser/data_model/data_model_utils.cc +++ b/chromium/components/autofill/core/browser/data_model/data_model_utils.cc @@ -11,9 +11,9 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" +#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/common/autofill_clock.h" -#include "components/autofill/core/common/autofill_regex_constants.h" -#include "components/autofill/core/common/autofill_regexes.h" #include "third_party/icu/source/common/unicode/uloc.h" #include "third_party/icu/source/i18n/unicode/dtfmtsym.h" diff --git a/chromium/components/autofill/core/browser/form_data_importer.cc b/chromium/components/autofill/core/browser/form_data_importer.cc index 203fbe516ab..37c6daaa7a4 100644 --- a/chromium/components/autofill/core/browser/form_data_importer.cc +++ b/chromium/components/autofill/core/browser/form_data_importer.cc @@ -19,6 +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/autofill_client.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/autofill_type.h" @@ -102,16 +103,13 @@ bool IsMinimumAddress(const AutofillProfile& profile, << "Country entry in form." << CTag{}; } - if (base::FeatureList::IsEnabled( - features::kAutofillUseVariationCountryCode)) { - // As a fallback, use the finch state to get a country code. - if (country_code.empty() && !variation_country_code.empty()) { - country_code = variation_country_code; - if (import_log_buffer && !country_code.empty()) { - *import_log_buffer - << LogMessage::kImportAddressProfileFromFormCountrySource - << "Variations service." << CTag{}; - } + // As a fallback, use the finch state to get a country code. + if (country_code.empty() && !variation_country_code.empty()) { + country_code = variation_country_code; + if (import_log_buffer && !country_code.empty()) { + *import_log_buffer + << LogMessage::kImportAddressProfileFromFormCountrySource + << "Variations service." << CTag{}; } } @@ -230,6 +228,8 @@ FormDataImporter::FormDataImporter(AutofillClient* client, payments_client, app_locale, personal_data_manager)), + address_profile_save_manager_( + std::make_unique<AddressProfileSaveManager>(personal_data_manager)), #if !defined(OS_ANDROID) && !defined(OS_IOS) local_card_migration_manager_( std::make_unique<LocalCardMigrationManager>(client, @@ -470,15 +470,12 @@ bool FormDataImporter::ImportAddressProfiles(const FormStructure& form) { // And close the div of the section import log. import_log_buffer << CTag{"div"}; } - // TODO(crbug.com/1097125): Remove feature test. // 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) { AutofillMetrics::LogAddressFormImportStatustMetric( AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT); - } else if (base::FeatureList::IsEnabled( - features::kAutofillProfileImportFromUnifiedSection) && - sections.size() > 1) { + } else if (sections.size() > 1) { // Try to import by combining all sections. if (ImportAddressProfileForSection(form, "", &import_log_buffer)) { num_saved_profiles++; @@ -542,14 +539,8 @@ 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, 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()) + // information into the field, then skip it. + if (!field->IsFieldFillable() || value.empty()) continue; AutofillType field_type = field->Type(); @@ -591,24 +582,20 @@ bool FormDataImporter::ImportAddressProfileForSection( // Reject profiles with invalid country information. if (server_field_type == ADDRESS_HOME_COUNTRY && candidate_profile.GetRawInfo(ADDRESS_HOME_COUNTRY).empty()) { - // TODO(crbug.com/1075604): Remove branch with disabled feature. - if (base::FeatureList::IsEnabled( - features::kAutofillUsePageLanguageToTranslateCountryNames)) { - // The country code was not successfully determined from the value in - // the country field. This can be caused by a localization that does not - // match the |app_locale|. Try setting the value again using the - // language of the page. Note, there should be a locale associated with - // every language code. - std::string page_language; - const translate::LanguageState* language_state = - client_->GetLanguageState(); - if (language_state) - page_language = language_state->original_language(); - // Retry to set the country of there is known page language. - if (!page_language.empty()) { - candidate_profile.SetInfoWithVerificationStatus( - field_type, value, page_language, VerificationStatus::kObserved); - } + // The country code was not successfully determined from the value in + // the country field. This can be caused by a localization that does not + // match the |app_locale|. Try setting the value again using the + // language of the page. Note, there should be a locale associated with + // every language code. + std::string page_language; + const translate::LanguageState* language_state = + client_->GetLanguageState(); + if (language_state) + page_language = language_state->original_language(); + // Retry to set the country of there is known page language. + if (!page_language.empty()) { + candidate_profile.SetInfoWithVerificationStatus( + field_type, value, page_language, VerificationStatus::kObserved); } // Check if the country code was still not determined correctly. if (candidate_profile.GetRawInfo(ADDRESS_HOME_COUNTRY).empty()) { @@ -687,7 +674,7 @@ bool FormDataImporter::ImportAddressProfileForSection( return false; std::string guid = - personal_data_manager_->SaveImportedProfile(candidate_profile); + address_profile_save_manager_->SaveProfile(candidate_profile); return !guid.empty(); } diff --git a/chromium/components/autofill/core/browser/form_data_importer.h b/chromium/components/autofill/core/browser/form_data_importer.h index 38d0b12fd9a..4ef28d575a6 100644 --- a/chromium/components/autofill/core/browser/form_data_importer.h +++ b/chromium/components/autofill/core/browser/form_data_importer.h @@ -24,6 +24,8 @@ class SaveCardOfferObserver; namespace autofill { +class AddressProfileSaveManager; + // Manages logic for importing address profiles and credit card information from // web forms into the user's Autofill profile via the PersonalDataManager. // Owned by AutofillManager. @@ -151,6 +153,9 @@ class FormDataImporter { // Responsible for managing credit card save flows (local or upload). std::unique_ptr<CreditCardSaveManager> credit_card_save_manager_; + // Responsible for managing address profiles save flows. + std::unique_ptr<AddressProfileSaveManager> address_profile_save_manager_; + #if !defined(OS_ANDROID) && !defined(OS_IOS) // Responsible for migrating locally saved credit cards to Google Pay. std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager_; 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 31e772c644a..b15cee9ccae 100644 --- a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc +++ b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc @@ -14,7 +14,7 @@ #include <utility> #include <vector> -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/feature_list.h" #include "base/guid.h" @@ -29,8 +29,10 @@ #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_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" @@ -230,6 +232,7 @@ 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 @@ -580,18 +583,6 @@ TEST_P(FormDataImporterTest, ImportAddressProfileFromUnifiedSection) { // Assign the address field another section than the other fields. form_structure.field(3)->section = "another_section"; - base::test::ScopedFeatureList scoped_feature; - scoped_feature.InitAndDisableFeature( - features::kAutofillProfileImportFromUnifiedSection); - - // Without the feature, the import is expected to fail. - ImportAddressProfiles(/*extraction_successful=*/false, form_structure); - - // After enabled the feature, the import is expected to succeed. - scoped_feature.Reset(); - scoped_feature.InitAndEnableFeature( - features::kAutofillProfileImportFromUnifiedSection); - ImportAddressProfiles(/*extraction_successful=*/true, form_structure); AutofillProfile expected(base::GenerateGUID(), test::kEmptyOrigin); @@ -842,7 +833,9 @@ TEST_P(FormDataImporterTest, EXPECT_EQ(0, expected.Compare(*results[0])); } -TEST_P(FormDataImporterTest, ImportAddressProfiles_UnFocussableFields) { +// Test that a form is imported correctly even if some fields are not +// focusable. +TEST_P(FormDataImporterTest, ImportAddressProfiles_WithUnFocussableFields) { FormData form; form.url = GURL("https://wwww.foo.com"); @@ -864,7 +857,8 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_UnFocussableFields) { form.fields.push_back(field); test::CreateTestFormField("City:", "city", "San Francisco", "text", &field); - // Set this field to be unfocusable. + + // Set this field to be not focusable. field.is_focusable = false; form.fields.push_back(field); @@ -877,18 +871,6 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_UnFocussableFields) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - // 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); @@ -1605,6 +1587,181 @@ TEST_P(FormDataImporterTest, EXPECT_EQ(0, profile.Compare(*results2[0])); } +TEST_P(FormDataImporterTest, + IncorporateStructuredNameInformationInVerifiedProfile) { + // This test is only applicable to structured names. + if (!structured_address::StructuredNamesEnabled()) { + return; + } + + // Start with a verified profile. + AutofillProfile profile(base::GenerateGUID(), kSettingsOrigin); + test::SetProfileInfo(&profile, "Marion", "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", + "Hollywood", "CA", "91601", "US", "12345678910"); + EXPECT_TRUE(profile.IsVerified()); + + // Set the verification status for the first and middle name to parsed. + profile.SetRawInfoWithVerificationStatus( + NAME_FIRST, base::ASCIIToUTF16("Marion"), + structured_address::VerificationStatus::kParsed); + profile.SetRawInfoWithVerificationStatus( + NAME_FIRST, base::ASCIIToUTF16("Mitchell"), + structured_address::VerificationStatus::kParsed); + + base::RunLoop run_loop; + EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) + .WillOnce(QuitMessageLoop(&run_loop)); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1); + personal_data_manager_->AddProfile(profile); + run_loop.Run(); + + // Simulate a form submission with conflicting info. + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("First name:", "first_name", "Marion Mitchell", + "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Last name:", "last_name", "Morrison", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Email:", "email", "johnwayne@me.xyz", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Address:", "address1", "123 Zoo St.", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("City:", "city", "Hollywood", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("State:", "state", "CA", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Zip:", "zip", "91601", "text", &field); + form.fields.push_back(field); + + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); + + // The form submission should result in a change of name structure. + profile.SetRawInfoWithVerificationStatus( + NAME_FIRST, base::ASCIIToUTF16("Marion Mitchell"), + structured_address::VerificationStatus::kObserved); + profile.SetRawInfoWithVerificationStatus( + NAME_MIDDLE, base::ASCIIToUTF16(""), + structured_address::VerificationStatus::kNoStatus); + profile.SetRawInfoWithVerificationStatus( + NAME_LAST, base::ASCIIToUTF16("Morrison"), + structured_address::VerificationStatus::kObserved); + + // Expect that no new profile is saved. + const std::vector<AutofillProfile*>& results = + personal_data_manager_->GetProfiles(); + ASSERT_EQ(1U, results.size()); + EXPECT_EQ(0, profile.Compare(*results[0])); + + // Try the same thing, but without "Mitchell". The profiles should still match + // because "Marion Morrison" is a variant of the known full name. + test::CreateTestFormField("First name:", "first_name", "Marion", "text", + &field); + form.fields[0] = field; + + FormStructure form_structure2(form); + form_structure2.DetermineHeuristicTypes(); + + ImportAddressProfiles(/*extraction_successful=*/true, form_structure2); + + // Expect that no new profile is saved. + const std::vector<AutofillProfile*>& results2 = + personal_data_manager_->GetProfiles(); + ASSERT_EQ(1U, results2.size()); + EXPECT_EQ(0, profile.Compare(*results2[0])); +} + +TEST_P(FormDataImporterTest, + IncorporateStructuredAddressInformationInVerififedProfile) { + // This test is only applicable to structured addresses. + if (!structured_address::StructuredAddressesEnabled()) { + return; + } + + // Start with a verified profile. + AutofillProfile profile(base::GenerateGUID(), kSettingsOrigin); + test::SetProfileInfo(&profile, "Marion", "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", + "Hollywood", "CA", "91601", "US", "12345678910"); + EXPECT_TRUE(profile.IsVerified()); + + // Reset the structured address to emulate a failed parsing attempt. + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_HOUSE_NUMBER, base::ASCIIToUTF16(""), + structured_address::VerificationStatus::kNoStatus); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_NAME, base::ASCIIToUTF16(""), + structured_address::VerificationStatus::kNoStatus); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_AND_DEPENDENT_STREET_NAME, base::ASCIIToUTF16(""), + structured_address::VerificationStatus::kNoStatus); + + base::RunLoop run_loop; + EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) + .WillOnce(QuitMessageLoop(&run_loop)); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1); + personal_data_manager_->AddProfile(profile); + run_loop.Run(); + + // Simulate a form submission with conflicting info. + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("First name:", "first_name", "Marion Mitchell", + "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Last name:", "last_name", "Morrison", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("Email:", "email", "johnwayne@me.xyz", "text", + &field); + form.fields.push_back(field); + // This forms contains structured address information. + test::CreateTestFormField("Street Name:", "street_name", "Zoo St.", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("House Number:", "house_number", "123", "text", + &field); + form.fields.push_back(field); + test::CreateTestFormField("City:", "city", "Hollywood", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("State:", "state", "CA", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Zip:", "zip", "91601", "text", &field); + form.fields.push_back(field); + + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(); + ImportAddressProfiles(/*extraction_successful=*/true, form_structure); + + // The form submission should result in a change of the address structure. + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_AND_DEPENDENT_STREET_NAME, + base::ASCIIToUTF16("Zoo St."), + structured_address::VerificationStatus::kFormatted); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_NAME, base::ASCIIToUTF16("Zoo St."), + structured_address::VerificationStatus::kObserved); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_HOUSE_NUMBER, base::ASCIIToUTF16("123"), + structured_address::VerificationStatus::kObserved); + + // Expect that no new profile is saved. + const std::vector<AutofillProfile*>& results = + personal_data_manager_->GetProfiles(); + ASSERT_EQ(1U, results.size()); + EXPECT_EQ(0, profile.Compare(*results[0])); +} + // Tests that no profile is inferred if the country is not recognized. TEST_P(FormDataImporterTest, ImportAddressProfiles_UnrecognizedCountry) { FormData form; @@ -1690,37 +1847,6 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_LocalizedCountryName) { // Set the page language to match the localized country value and try again. autofill_client_->GetLanguageState()->SetOriginalLanguage("de"); - // TODO(crbug.com/1075604): Remove test with disabled feature. - // Verify that nothing is changed if using the page language feature is not - // enabled. - scoped_feature_list_.Reset(); - if (StructuredNames()) { - scoped_feature_list_.InitWithFeatures( - {features::kAutofillEnableSupportForMoreStructureInNames}, - {features::kAutofillUsePageLanguageToTranslateCountryNames}); - } else { - scoped_feature_list_.InitWithFeatures( - {}, {features::kAutofillEnableSupportForMoreStructureInNames, - features::kAutofillUsePageLanguageToTranslateCountryNames}); - } - ImportAddressProfiles(/*extraction_successful=*/false, form_structure); - - // There should be no imported address profile. - ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size()); - ASSERT_EQ(0U, personal_data_manager_->GetCreditCards().size()); - - // Enable the feature and to test if the profile can now be imported. - scoped_feature_list_.Reset(); - if (StructuredNames()) { - scoped_feature_list_.InitWithFeatures( - {features::kAutofillEnableSupportForMoreStructureInNames, - features::kAutofillUsePageLanguageToTranslateCountryNames}, - {}); - } else { - scoped_feature_list_.InitWithFeatures( - {features::kAutofillUsePageLanguageToTranslateCountryNames}, - {features::kAutofillEnableSupportForMoreStructureInNames}); - } ImportAddressProfiles(/*extraction_successful=*/true, form_structure); // There should be one imported address profile. 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 123f7878008..6ed67749e2f 100644 --- a/chromium/components/autofill/core/browser/form_parsing/address_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/address_field.cc @@ -14,10 +14,10 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.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_scanner.h" #include "components/autofill/core/common/autofill_features.h" -#include "components/autofill/core/common/autofill_regex_constants.h" using base::UTF8ToUTF16; @@ -58,9 +58,21 @@ std::unique_ptr<FormField> AddressField::Parse(AutofillScanner* scanner, base::string16 attention_ignored = UTF8ToUTF16(kAttentionIgnoredRe); base::string16 region_ignored = UTF8ToUTF16(kRegionIgnoredRe); - const bool is_enabled_merged_city_state_country_zip = - base::FeatureList::IsEnabled( - features::kAutofillUseParseCityStateCountryZipCodeInHeuristic); + // 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); // Allow address fields to appear in any order. size_t begin_trailing_non_labeled_fields = 0; @@ -68,28 +80,28 @@ 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), nullptr, - {log_manager, "kAddressLookupRe"}) || - ParseField(scanner, base::UTF8ToUTF16(kAddressNameIgnoredRe), nullptr, + if (ParseField(scanner, base::UTF8ToUTF16(kAddressLookupRe), patterns_al, + nullptr, {log_manager, "kAddressLookupRe"}) || + ParseField(scanner, base::UTF8ToUTF16(kAddressNameIgnoredRe), + patterns_ni, nullptr, {log_manager, "kAddressNameIgnoreRe"})) { continue; // Ignore email addresses. } else if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kEmailRe), - MATCH_DEFAULT | MATCH_TEXT_AREA, nullptr, - {log_manager, "kEmailRe"})) { + MATCH_DEFAULT | MATCH_TEXT_AREA, + patterns_email, nullptr, + {log_manager, "kEmailRe"}, + {.augment_types = MATCH_TEXT_AREA})) { continue; - } else if (address_field->ParseAddress(scanner) || - (!is_enabled_merged_city_state_country_zip && - (address_field->ParseCityStateZipCode(scanner) || - address_field->ParseCountry(scanner))) || - (is_enabled_merged_city_state_country_zip && - address_field->ParseCityStateCountryZipCode(scanner)) || - address_field->ParseCompany(scanner)) { + } else if (address_field->ParseAddress(scanner, page_language) || + address_field->ParseCityStateCountryZipCode(scanner, + page_language) || + address_field->ParseCompany(scanner, page_language)) { has_trailing_non_labeled_fields = false; continue; - } else if (ParseField(scanner, attention_ignored, nullptr, + } else if (ParseField(scanner, attention_ignored, patterns_ai, nullptr, {log_manager, "kAttentionIgnoredRe"}) || - ParseField(scanner, region_ignored, nullptr, + ParseField(scanner, region_ignored, patterns_ri, nullptr, {log_manager, "kRegionIgnoredRe"})) { // We ignore the following: // * Attention. @@ -170,15 +182,20 @@ void AddressField::AddClassifications( kBaseAddressParserScore, field_candidates); } -bool AddressField::ParseCompany(AutofillScanner* scanner) { +bool AddressField::ParseCompany(AutofillScanner* scanner, + const std::string& page_language) { if (company_) return false; + // In JSON : COMPANY + auto& patterns_c = + PatternProvider::GetInstance().GetMatchPatterns("COMPANY", page_language); - return ParseField(scanner, UTF8ToUTF16(kCompanyRe), &company_, + return ParseField(scanner, UTF8ToUTF16(kCompanyRe), patterns_c, &company_, {log_manager_, "kCompanyRe"}); } -bool AddressField::ParseAddressFieldSequence(AutofillScanner* scanner) { +bool AddressField::ParseAddressFieldSequence(AutofillScanner* scanner, + const std::string& 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. @@ -190,16 +207,24 @@ 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); while (!scanner->IsEnd()) { if (!street_name_ && ParseFieldSpecifics(scanner, UTF8ToUTF16(kStreetNameRe), MATCH_DEFAULT, - &street_name_, {log_manager_, "kStreetNameRe"})) { + patterns_s, &street_name_, + {log_manager_, "kStreetNameRe"})) { continue; } if (!house_number_ && ParseFieldSpecifics(scanner, UTF8ToUTF16(kHouseNumberRe), MATCH_DEFAULT, - &house_number_, {log_manager_, "kHouseNumberRe"})) { + patterns_h, &house_number_, + {log_manager_, "kHouseNumberRe"})) { continue; } @@ -218,14 +243,17 @@ bool AddressField::ParseAddressFieldSequence(AutofillScanner* scanner) { return false; } -bool AddressField::ParseAddress(AutofillScanner* scanner) { +bool AddressField::ParseAddress(AutofillScanner* scanner, + const std::string& page_language) { if (street_name_ && house_number_) { return false; } - return ParseAddressFieldSequence(scanner) || ParseAddressLines(scanner); + return ParseAddressFieldSequence(scanner, page_language) || + ParseAddressLines(scanner, page_language); } -bool AddressField::ParseAddressLines(AutofillScanner* scanner) { +bool AddressField::ParseAddressLines(AutofillScanner* scanner, + const std::string& 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 @@ -239,17 +267,23 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner) { base::string16 pattern = UTF8ToUTF16(kAddressLine1Re); base::string16 label_pattern = UTF8ToUTF16(kAddressLine1LabelRe); - if (!ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT, &address1_, - {log_manager_, "kAddressLine1Re"}) && + // 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"}) && !ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT, - &address1_, + patterns_l1, &address1_, {log_manager_, "kAddressLine1LabelRe"}) && !ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_TEXT_AREA, - &street_address_, - {log_manager_, "kAddressLine1Re"}) && - !ParseFieldSpecifics(scanner, label_pattern, - MATCH_LABEL | MATCH_TEXT_AREA, &street_address_, - {log_manager_, "kAddressLine1LabelRe"})) + patterns_l1, &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})) return false; if (street_address_) @@ -260,19 +294,33 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner) { // discussion on https://codereview.chromium.org/741493003/ pattern = UTF8ToUTF16(kAddressLine2Re); label_pattern = UTF8ToUTF16(kAddressLine2LabelRe); - if (!ParseField(scanner, pattern, &address2_, + // 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_, {log_manager_, "kAddressLine2Re"}) && !ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT, - &address2_, {log_manager_, "kAddressLine2LabelRe"})) + patterns_l2, &address2_, + {log_manager_, "kAddressLine2LabelRe"})) return true; // Optionally parse address line 3. This uses the same label regexp as // address 2 above. pattern = UTF8ToUTF16(kAddressLinesExtraRe); - if (!ParseField(scanner, pattern, &address3_, + if (!ParseField(scanner, pattern, patterns_le, &address3_, {log_manager_, "kAddressLinesExtraRe"}) && !ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT, - &address3_, {log_manager_, "kAddressLine2LabelRe"})) + patterns_l2, &address3_, + {log_manager_, "kAddressLine2LabelRe"})) return true; // Try for surplus lines, which we will promptly discard. Some pages have 4 @@ -281,21 +329,29 @@ 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, nullptr, + while (ParseField(scanner, pattern, patterns_le, nullptr, {log_manager_, "kAddressLinesExtraRe"})) { // Consumed a surplus line, try for another. } return true; } -bool AddressField::ParseCountry(AutofillScanner* scanner) { +bool AddressField::ParseCountry(AutofillScanner* scanner, + const std::string& page_language) { if (country_) return false; + // In JSON : COUNTRY + auto& patterns_c = + PatternProvider::GetInstance().GetMatchPatterns("COUNTRY", page_language); + auto& patterns_cl = PatternProvider::GetInstance().GetMatchPatterns( + "COUNTRY_LOCATION", page_language); + scanner->SaveCursor(); if (ParseFieldSpecifics(scanner, UTF8ToUTF16(kCountryRe), MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH, - &country_, {log_manager_, "kCountryRe"})) { + patterns_c, &country_, + {log_manager_, "kCountryRe"})) { return true; } @@ -304,46 +360,67 @@ bool AddressField::ParseCountry(AutofillScanner* scanner) { scanner->Rewind(); return ParseFieldSpecifics( scanner, UTF8ToUTF16(kCountryLocationRe), - MATCH_LABEL | MATCH_NAME | MATCH_SELECT | MATCH_SEARCH, &country_, - {log_manager_, "kCountryLocationRe"}); + MATCH_LABEL | MATCH_NAME | MATCH_SELECT | MATCH_SEARCH, patterns_cl, + &country_, {log_manager_, "kCountryLocationRe"}); } -bool AddressField::ParseZipCode(AutofillScanner* scanner) { +bool AddressField::ParseZipCode(AutofillScanner* scanner, + const std::string& 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 = + PatternProvider::GetInstance().GetMatchPatterns("ZIP_4", page_language); if (!ParseFieldSpecifics(scanner, UTF8ToUTF16(kZipCodeRe), kZipCodeMatchType, - &zip_, {log_manager_, "kZipCodeRe"})) { + patterns_z, &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, &zip4_, - {log_manager_, "kZip4Re"}); + ParseFieldSpecifics(scanner, UTF8ToUTF16(kZip4Re), kZipCodeMatchType, + patterns_z4, &zip4_, {log_manager_, "kZip4Re"}); return true; } -bool AddressField::ParseCity(AutofillScanner* scanner) { +bool AddressField::ParseCity(AutofillScanner* scanner, + const std::string& page_language) { if (city_) return false; + // In JSON : CITY + auto& patterns_city = + PatternProvider::GetInstance().GetMatchPatterns("CITY", page_language); return ParseFieldSpecifics(scanner, UTF8ToUTF16(kCityRe), kCityMatchType, - &city_, {log_manager_, "kCityRe"}); + patterns_city, &city_, {log_manager_, "kCityRe"}); } -bool AddressField::ParseState(AutofillScanner* scanner) { +bool AddressField::ParseState(AutofillScanner* scanner, + const std::string& page_language) { if (state_) return false; + // auto& patterns = PatternProvider::GetInstance().GetMatchPatterns( + // "ADDRESS_HOME_STATE", page_language); + // In JSON : STATE + auto& patterns_state = + PatternProvider::GetInstance().GetMatchPatterns("STATE", page_language); return ParseFieldSpecifics(scanner, UTF8ToUTF16(kStateRe), kStateMatchType, - &state_, {log_manager_, "kStateRe"}); + patterns_state, &state_, + {log_manager_, "kStateRe"}); } AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelSeparately( AutofillScanner* scanner, const base::string16& pattern, int match_type, + const std::vector<MatchingPattern>& patterns, AutofillField** match, const RegExLogging& logging) { if (scanner->IsEnd()) @@ -352,10 +429,12 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelSeparately( AutofillField* cur_match = nullptr; size_t saved_cursor = scanner->SaveCursor(); bool parsed_name = ParseFieldSpecifics( - scanner, pattern, match_type & ~MATCH_LABEL, &cur_match, logging); + scanner, pattern, match_type & ~MATCH_LABEL, patterns, &cur_match, + logging, {.restrict_attributes = MATCH_NAME}); scanner->RewindTo(saved_cursor); bool parsed_label = ParseFieldSpecifics( - scanner, pattern, match_type & ~MATCH_NAME, &cur_match, logging); + scanner, pattern, match_type & ~MATCH_NAME, patterns, &cur_match, logging, + {.restrict_attributes = MATCH_LABEL}); if (parsed_name && parsed_label) { if (match) *match = cur_match; @@ -370,60 +449,9 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelSeparately( return RESULT_MATCH_NONE; } -bool AddressField::ParseCityStateZipCode(AutofillScanner* scanner) { - // Simple cases. - if (scanner->IsEnd()) - return false; - if (city_ && state_ && zip_) - return false; - if (state_ && zip_) - return ParseCity(scanner); - if (city_ && zip_) - return ParseState(scanner); - if (city_ && state_) - return ParseZipCode(scanner); - - // Check for matches to both name and label. - ParseNameLabelResult city_result = ParseNameAndLabelForCity(scanner); - if (city_result == RESULT_MATCH_NAME_LABEL) - return true; - ParseNameLabelResult state_result = ParseNameAndLabelForState(scanner); - if (state_result == RESULT_MATCH_NAME_LABEL) - return true; - ParseNameLabelResult zip_result = ParseNameAndLabelForZipCode(scanner); - if (zip_result == RESULT_MATCH_NAME_LABEL) - return true; - - // 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_zip = zip_result != RESULT_MATCH_NONE; - if (maybe_city && !maybe_state && !maybe_zip) - return SetFieldAndAdvanceCursor(scanner, &city_); - if (maybe_state && !maybe_city && !maybe_zip) - return SetFieldAndAdvanceCursor(scanner, &state_); - if (maybe_zip && !maybe_city && !maybe_state) - return ParseZipCode(scanner); - - // Otherwise give name priority over label. - if (city_result == RESULT_MATCH_NAME) - return SetFieldAndAdvanceCursor(scanner, &city_); - if (state_result == RESULT_MATCH_NAME) - return SetFieldAndAdvanceCursor(scanner, &state_); - if (zip_result == RESULT_MATCH_NAME) - return ParseZipCode(scanner); - - if (city_result == RESULT_MATCH_LABEL) - return SetFieldAndAdvanceCursor(scanner, &city_); - if (state_result == RESULT_MATCH_LABEL) - return SetFieldAndAdvanceCursor(scanner, &state_); - if (zip_result == RESULT_MATCH_LABEL) - return ParseZipCode(scanner); - - return false; -} - -bool AddressField::ParseCityStateCountryZipCode(AutofillScanner* scanner) { +bool AddressField::ParseCityStateCountryZipCode( + AutofillScanner* scanner, + const std::string& page_language) { // The |scanner| is not pointing at a field. if (scanner->IsEnd()) return false; @@ -434,25 +462,29 @@ bool AddressField::ParseCityStateCountryZipCode(AutofillScanner* scanner) { // Exactly one field type is missing. if (state_ && country_ && zip_) - return ParseCity(scanner); + return ParseCity(scanner, page_language); if (city_ && country_ && zip_) - return ParseState(scanner); + return ParseState(scanner, page_language); if (city_ && state_ && zip_) - return ParseCountry(scanner); + return ParseCountry(scanner, page_language); if (city_ && state_ && country_) - return ParseZipCode(scanner); + return ParseZipCode(scanner, page_language); // Check for matches to both the name and the label. - ParseNameLabelResult city_result = ParseNameAndLabelForCity(scanner); + ParseNameLabelResult city_result = + ParseNameAndLabelForCity(scanner, page_language); if (city_result == RESULT_MATCH_NAME_LABEL) return true; - ParseNameLabelResult state_result = ParseNameAndLabelForState(scanner); + ParseNameLabelResult state_result = + ParseNameAndLabelForState(scanner, page_language); if (state_result == RESULT_MATCH_NAME_LABEL) return true; - ParseNameLabelResult country_result = ParseNameAndLabelForCountry(scanner); + ParseNameLabelResult country_result = + ParseNameAndLabelForCountry(scanner, page_language); if (country_result == RESULT_MATCH_NAME_LABEL) return true; - ParseNameLabelResult zip_result = ParseNameAndLabelForZipCode(scanner); + ParseNameLabelResult zip_result = + ParseNameAndLabelForZipCode(scanner, page_language); if (zip_result == RESULT_MATCH_NAME_LABEL) return true; @@ -468,7 +500,7 @@ bool AddressField::ParseCityStateCountryZipCode(AutofillScanner* scanner) { 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); + 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. @@ -483,7 +515,7 @@ bool AddressField::ParseCityStateCountryZipCode(AutofillScanner* scanner) { if (country_result == RESULT_MATCH_NAME) return SetFieldAndAdvanceCursor(scanner, &country_); if (zip_result == RESULT_MATCH_NAME) - return ParseZipCode(scanner); + return ParseZipCode(scanner, page_language); if (city_result == RESULT_MATCH_LABEL) return SetFieldAndAdvanceCursor(scanner, &city_); @@ -492,30 +524,38 @@ bool AddressField::ParseCityStateCountryZipCode(AutofillScanner* scanner) { if (country_result == RESULT_MATCH_LABEL) return SetFieldAndAdvanceCursor(scanner, &country_); if (zip_result == RESULT_MATCH_LABEL) - return ParseZipCode(scanner); + return ParseZipCode(scanner, page_language); return false; } AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForZipCode( - AutofillScanner* scanner) { + AutofillScanner* scanner, + const std::string& 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 = + PatternProvider::GetInstance().GetMatchPatterns("ZIP_4", page_language); + ParseNameLabelResult result = ParseNameAndLabelSeparately( - scanner, UTF8ToUTF16(kZipCodeRe), kZipCodeMatchType, &zip_, + scanner, UTF8ToUTF16(kZipCodeRe), kZipCodeMatchType, patterns_z, &zip_, {log_manager_, "kZipCodeRe"}); if (result != RESULT_MATCH_NAME_LABEL || scanner->IsEnd()) return result; size_t saved_cursor = scanner->SaveCursor(); - bool found_non_zip4 = ParseCity(scanner); + bool found_non_zip4 = ParseCity(scanner, page_language); if (found_non_zip4) city_ = nullptr; scanner->RewindTo(saved_cursor); if (!found_non_zip4) { - found_non_zip4 = ParseState(scanner); + found_non_zip4 = ParseState(scanner, page_language); if (found_non_zip4) state_ = nullptr; scanner->RewindTo(saved_cursor); @@ -525,40 +565,55 @@ AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForZipCode( // Look for a zip+4, whose field name will also often contain // the substring "zip". ParseFieldSpecifics(scanner, UTF8ToUTF16(kZip4Re), kZipCodeMatchType, - &zip4_, {log_manager_, "kZip4Re"}); + patterns_z4, &zip4_, {log_manager_, "kZip4Re"}); } return result; } AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCity( - AutofillScanner* scanner) { + AutofillScanner* scanner, + const std::string& page_language) { if (city_) return RESULT_MATCH_NONE; + // In JSON : CITY + auto& patterns_city = + PatternProvider::GetInstance().GetMatchPatterns("CITY", page_language); return ParseNameAndLabelSeparately(scanner, UTF8ToUTF16(kCityRe), - kCityMatchType, &city_, + kCityMatchType, patterns_city, &city_, {log_manager_, "kCityRe"}); } AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForState( - AutofillScanner* scanner) { + AutofillScanner* scanner, + const std::string& page_language) { if (state_) return RESULT_MATCH_NONE; + // In JSON : STATE + auto& patterns_state = + PatternProvider::GetInstance().GetMatchPatterns("STATE", page_language); return ParseNameAndLabelSeparately(scanner, UTF8ToUTF16(kStateRe), - kStateMatchType, &state_, + kStateMatchType, patterns_state, &state_, {log_manager_, "kStateRe"}); } AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForCountry( - AutofillScanner* scanner) { + AutofillScanner* scanner, + const std::string& page_language) { if (country_) return RESULT_MATCH_NONE; - ParseNameLabelResult country_result = - ParseNameAndLabelSeparately(scanner, UTF8ToUTF16(kCountryRe), - MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH, - &country_, {log_manager_, "kCountryRe"}); + // In JSON : COUNTRY + auto& patterns_c = + PatternProvider::GetInstance().GetMatchPatterns("COUNTRY", page_language); + auto& patterns_cl = PatternProvider::GetInstance().GetMatchPatterns( + "COUNTRY_LOCATION", page_language); + + ParseNameLabelResult country_result = ParseNameAndLabelSeparately( + scanner, UTF8ToUTF16(kCountryRe), + MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH, patterns_c, &country_, + {log_manager_, "kCountryRe"}); if (country_result != RESULT_MATCH_NONE) return country_result; @@ -566,8 +621,8 @@ 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, &country_, - {log_manager_, "kCountryLocationRe"}); + MATCH_LABEL | MATCH_NAME | MATCH_SELECT | MATCH_SEARCH, patterns_cl, + &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 4fb2c6c6758..0ee62ee3853 100644 --- a/chromium/components/autofill/core/browser/form_parsing/address_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/address_field.h @@ -14,6 +14,7 @@ #include "base/strings/string16.h" #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" namespace autofill { @@ -53,45 +54,70 @@ class AddressField : public FormField { explicit AddressField(LogManager* log_manager); - bool ParseCompany(AutofillScanner* scanner); - bool ParseAddress(AutofillScanner* scanner); - bool ParseAddressFieldSequence(AutofillScanner* scanner); - bool ParseAddressLines(AutofillScanner* scanner); - bool ParseCountry(AutofillScanner* scanner); - bool ParseZipCode(AutofillScanner* scanner); - bool ParseCity(AutofillScanner* scanner); - bool ParseState(AutofillScanner* scanner); + bool ParseCompany(AutofillScanner* scanner, const std::string& 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, zip, or none of those. - // TODO(crbug.com/1073555) Delete this once experiment - // |kAutofillUseParseCityStateCountryZipCodeInHeuristic| has been launched. - bool ParseCityStateZipCode(AutofillScanner* scanner); + bool ParseAddress(AutofillScanner* scanner, const std::string& page_language); + + bool ParseAddressFieldSequence(AutofillScanner* scanner, + const std::string& page_language); + + bool ParseAddressLines(AutofillScanner* scanner, + const std::string& page_language); + + bool ParseCountry(AutofillScanner* scanner, const std::string& page_language); + + bool ParseZipCode(AutofillScanner* scanner, const std::string& page_language); + + bool ParseCity(AutofillScanner* scanner, const std::string& page_language); + + bool ParseState(AutofillScanner* scanner, const std::string& 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); + bool ParseCityStateCountryZipCode(AutofillScanner* scanner, + const std::string& 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, int match_type, + const std::vector<MatchingPattern>& patterns, AutofillField** match, const RegExLogging& logging); // Run matches on the name and label separately. If the return result is // RESULT_MATCH_NAME_LABEL, then |scanner| advances and the field is set. // Otherwise |scanner| rewinds and the field is cleared. - ParseNameLabelResult ParseNameAndLabelForZipCode(AutofillScanner* scanner); - ParseNameLabelResult ParseNameAndLabelForCity(AutofillScanner* scanner); - ParseNameLabelResult ParseNameAndLabelForCountry(AutofillScanner* scanner); - ParseNameLabelResult ParseNameAndLabelForState(AutofillScanner* scanner); + ParseNameLabelResult ParseNameAndLabelForZipCode( + AutofillScanner* scanner, + const std::string& page_language); + + ParseNameLabelResult ParseNameAndLabelForCity( + AutofillScanner* scanner, + const std::string& page_language); + + ParseNameLabelResult ParseNameAndLabelForCountry( + AutofillScanner* scanner, + const std::string& page_language); + + ParseNameLabelResult ParseNameAndLabelForState( + AutofillScanner* scanner, + const std::string& page_language); LogManager* log_manager_; AutofillField* company_ = 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 9b59b7c455b..bc6e0908571 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 @@ -14,6 +14,7 @@ #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" @@ -24,13 +25,11 @@ namespace autofill { class AddressFieldTest : public testing::Test { public: - AddressFieldTest() {} + AddressFieldTest() = default; + AddressFieldTest(const AddressFieldTest&) = delete; + AddressFieldTest& operator=(const AddressFieldTest&) = delete; protected: - std::vector<std::unique_ptr<AutofillField>> list_; - std::unique_ptr<AddressField> field_; - FieldCandidatesMap field_candidates_map_; - // Downcast for tests. static std::unique_ptr<AddressField> Parse(AutofillScanner* scanner) { // An empty page_language means the language is unknown and patterns of all @@ -41,8 +40,12 @@ class AddressFieldTest : public testing::Test { static_cast<AddressField*>(field.release())); } - private: - DISALLOW_COPY_AND_ASSIGN(AddressFieldTest); + 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) { @@ -160,6 +163,104 @@ TEST_F(AddressFieldTest, ParseStreetAddressFromTextArea) { field_candidates_map_[ASCIIToUTF16("addr")].BestHeuristicType()); } +// Tests that fields are classified as |ADDRESS_HOME_STREET_NAME| and +// |ADDRESS_HOME_HOUSE_NUMBER| when they are labeled accordingly and +// both are present. +TEST_F(AddressFieldTest, ParseStreetNameAndHouseNumber) { + // 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"))); + + 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()); +} + +// Tests that the field is not classified as |ADDRESS_HOME_STREET_NAME| when +// it is labeled accordingly but 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()); +} + +// Tests that the field is not classified as |ADDRESS_HOME_HOUSE_NUMBER| when +// it is labeled accordingly but adjacent field classified as +// |ADDRESS_HOME_STREET_NAME| is absent. +TEST_F(AddressFieldTest, NotParseHouseNumberWithoutStreetName) { + // 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("House number"); + field.name = ASCIIToUTF16("house-number"); + list_.push_back( + std::make_unique<AutofillField>(field, ASCIIToUTF16("house"))); + + 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_HOUSE_NUMBER, + field_candidates_map_[ASCIIToUTF16("house")].BestHeuristicType()); +} + TEST_F(AddressFieldTest, ParseCity) { FormFieldData field; field.form_control_type = "text"; @@ -308,10 +409,6 @@ TEST_F(AddressFieldTest, ParseCityStateCountryZipcodeTogether) { field.name = ASCIIToUTF16("zip"); list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("zip1"))); - base::test::ScopedFeatureList enabled; - enabled.InitAndEnableFeature( - features::kAutofillUseParseCityStateCountryZipCodeInHeuristic); - AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); @@ -350,10 +447,6 @@ TEST_F(AddressFieldTest, ParseCountryLabelRegion) { list_.push_back( std::make_unique<AutofillField>(field, ASCIIToUTF16("country1"))); - base::test::ScopedFeatureList enabled; - enabled.InitAndEnableFeature( - features::kAutofillUseParseCityStateCountryZipCodeInHeuristic); - AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); @@ -377,10 +470,6 @@ TEST_F(AddressFieldTest, ParseCountryNameRegion) { list_.push_back( std::make_unique<AutofillField>(field, ASCIIToUTF16("country1"))); - base::test::ScopedFeatureList enabled; - enabled.InitAndEnableFeature( - features::kAutofillUseParseCityStateCountryZipCodeInHeuristic); - AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); 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 d9fc85afdce..ddd418eca54 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 @@ -13,31 +13,4 @@ MatchingPattern& MatchingPattern::operator=(const MatchingPattern& mp) = MatchingPattern::~MatchingPattern() = default; -autofill::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.negative_pattern = ""; - m_p.match_field_attributes = MATCH_NAME; - m_p.match_field_input_types = MATCH_TEXT; - m_p.language = "en"; - - return m_p; -} - -autofill::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.negative_pattern = ""; - m_p.match_field_attributes = MATCH_LABEL | MATCH_NAME; - m_p.match_field_input_types = MATCH_TEXT; - m_p.language = "de"; - - return m_p; -} - -} // namespace autofill
\ No newline at end of file +} // 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 8335c216c20..b24b5b5a5d7 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 @@ -5,6 +5,7 @@ #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_AUTOFILL_PARSING_UTILS_H_ #define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_AUTOFILL_PARSING_UTILS_H_ +#include <base/optional.h> #include <string> namespace autofill { @@ -51,17 +52,12 @@ struct MatchingPattern { std::string pattern_identifier; std::string positive_pattern; float positive_score = 1.1f; - std::string negative_pattern; + base::Optional<std::string> negative_pattern; int match_field_attributes; int match_field_input_types; std::string language; }; -// Use these functions instead of storing "non standats type" constants that -// bots might complaining over. -MatchingPattern GetCompanyPatternEn(); -MatchingPattern GetCompanyPatternDe(); - } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_AUTOFILL_PARSING_UTILS_H_ 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 766a9a00fda..27ee112a819 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 @@ -17,13 +17,13 @@ #include "base/strings/utf_string_conversions.h" #include "base/time/time.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/field_filler.h" #include "components/autofill/core/browser/field_types.h" #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_regex_constants.h" -#include "components/autofill/core/common/autofill_regexes.h" #include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" @@ -92,16 +92,28 @@ std::unique_ptr<FormField> CreditCardField::Parse( size_t saved_cursor = scanner->SaveCursor(); int nb_unknown_fields = 0; + 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); + // Credit card fields can appear in many different orders. // We loop until no more credit card related fields are found, see |break| at // the bottom of the loop. for (int fields = 0; !scanner->IsEnd(); ++fields) { // Ignore gift card fields. - if (IsGiftCardField(scanner, log_manager)) + if (IsGiftCardField(scanner, log_manager, page_language)) break; if (!credit_card_field->cardholder_) { - if (ParseField(scanner, base::UTF8ToUTF16(kNameOnCardRe), + if (ParseField(scanner, base::UTF8ToUTF16(kNameOnCardRe), patterns, &credit_card_field->cardholder_, {log_manager, "kNameOnCardRe"})) { continue; @@ -113,9 +125,10 @@ std::unique_ptr<FormField> CreditCardField::Parse( // fields. So we search for "name" only when we've already parsed at // least one other credit card field and haven't yet parsed the // expiration date (which usually appears at the end). + if (fields > 0 && !credit_card_field->expiration_month_ && ParseField(scanner, base::UTF8ToUTF16(kNameOnCardContextualRe), - &credit_card_field->cardholder_, + patterns_cont, &credit_card_field->cardholder_, {log_manager, "kNameOnCardContextualRe"})) { continue; } @@ -125,7 +138,7 @@ 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), + ParseField(scanner, base::UTF8ToUTF16(kLastNameRe), patterns_nl, &credit_card_field->cardholder_last_, {log_manager, "kLastNameRe"})) { continue; @@ -150,10 +163,12 @@ std::unique_ptr<FormField> CreditCardField::Parse( // They also sometimes use type="password" for sensitive types. const int kMatchNumTelAndPwd = MATCH_DEFAULT | MATCH_NUMBER | MATCH_TELEPHONE | MATCH_PASSWORD; + if (!credit_card_field->verification_ && - ParseFieldSpecifics( - scanner, base::UTF8ToUTF16(kCardCvcRe), kMatchNumTelAndPwd, - &credit_card_field->verification_, {log_manager, "kCardCvcRe"})) { + ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kCardCvcRe), + kMatchNumTelAndPwd, patterns_cvc, + &credit_card_field->verification_, + {log_manager, "kCardCvcRe"})) { // A couple of sites have multiple verification codes right after another. // Allow the classification of these codes one by one. AutofillField* const saved_cvv = credit_card_field->verification_; @@ -165,8 +180,9 @@ std::unique_ptr<FormField> CreditCardField::Parse( !credit_card_field->cardholder_ && scanner->SaveCursor() > 1) { // Check if the previous field was a verification code. scanner->RewindTo(scanner->SaveCursor() - 2); + if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kCardCvcRe), - kMatchNumTelAndPwd, + kMatchNumTelAndPwd, patterns_cvc, &credit_card_field->verification_, {log_manager, "kCardCvcRe"})) { // Reset the current cvv (The verification parse overwrote it). @@ -189,8 +205,10 @@ 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); if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kCardNumberRe), - kMatchNumTelAndPwd, ¤t_number_field, + kMatchNumTelAndPwd, patterns, ¤t_number_field, {log_manager, "kCardNumberRe"})) { // Avoid autofilling any credit card number field having very low or high // |start_index| on the HTML form. @@ -215,7 +233,8 @@ std::unique_ptr<FormField> CreditCardField::Parse( continue; } - if (credit_card_field->ParseExpirationDate(scanner, log_manager)) { + if (credit_card_field->ParseExpirationDate(scanner, log_manager, + page_language)) { nb_unknown_fields = 0; continue; } @@ -311,8 +330,10 @@ bool CreditCardField::LikelyCardMonthSelectField(AutofillScanner* scanner) { } // static -bool CreditCardField::LikelyCardYearSelectField(AutofillScanner* scanner, - LogManager* log_manager) { +bool CreditCardField::LikelyCardYearSelectField( + AutofillScanner* scanner, + LogManager* log_manager, + const std::string& page_language) { if (scanner->IsEnd()) return false; @@ -331,9 +352,12 @@ bool CreditCardField::LikelyCardYearSelectField(AutofillScanner* scanner, } // Another way to eliminate days - filter out 'day' fields. + // In JSON : DAY (only in JSON) + auto& patterns_day = + PatternProvider::GetInstance().GetMatchPatterns("DAY", page_language); if (FormField::ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kDayRe), - MATCH_DEFAULT | MATCH_SELECT, nullptr, - {log_manager, "kDayRe"})) { + MATCH_DEFAULT | MATCH_SELECT, patterns_day, + nullptr, {log_manager, "kDayRe"})) { return false; } @@ -389,28 +413,40 @@ bool CreditCardField::LikelyCardTypeSelectField(AutofillScanner* scanner) { // static bool CreditCardField::IsGiftCardField(AutofillScanner* scanner, - LogManager* log_manager) { + LogManager* log_manager, + const std::string& page_language) { if (scanner->IsEnd()) return false; const int kMatchFieldTypes = 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); + if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kDebitCardRe), - kMatchFieldTypes, nullptr, + kMatchFieldTypes, patterns_d, nullptr, {log_manager, "kDebitCardRe"})) { scanner->RewindTo(saved_cursor); return false; } if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kDebitGiftCardRe), - kMatchFieldTypes, nullptr, + kMatchFieldTypes, patterns_dg, nullptr, {log_manager, "kDebitGiftCardRe"})) { scanner->RewindTo(saved_cursor); return false; } return ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kGiftCardRe), - kMatchFieldTypes, nullptr, + kMatchFieldTypes, patterns_g, nullptr, {log_manager, "kGiftCardRe"}); } @@ -467,7 +503,8 @@ void CreditCardField::AddClassifications( } bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, - LogManager* log_manager) { + LogManager* log_manager, + const std::string& page_language) { if (!expiration_date_ && base::LowerCaseEqualsASCII( scanner->Cursor()->form_control_type, "month")) { expiration_date_ = scanner->Cursor(); @@ -487,7 +524,7 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, if (LikelyCardMonthSelectField(scanner)) { expiration_month_ = scanner->Cursor(); scanner->Advance(); - if (LikelyCardYearSelectField(scanner, log_manager)) { + if (LikelyCardYearSelectField(scanner, log_manager, page_language)) { expiration_year_ = scanner->Cursor(); scanner->Advance(); return true; @@ -500,11 +537,23 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, scanner->RewindTo(month_year_saved_cursor); 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); + if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kExpirationMonthRe), - kMatchCCType, &expiration_month_, + kMatchCCType, patterns_m, &expiration_month_, {log_manager_, "kExpirationMonthRe"}) && ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kExpirationYearRe), - kMatchCCType, &expiration_year_, + kMatchCCType, patterns_y, &expiration_year_, {log_manager_, "kExpirationYearRe"})) { return true; } @@ -512,9 +561,10 @@ 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, - &expiration_month_, {log_manager_, "^mm$"}) && + patterns_mm, &expiration_month_, + {log_manager_, "^mm$"}) && ParseFieldSpecifics(scanner, base::ASCIIToUTF16("^(yy|yyyy)$"), - kMatchCCType, &expiration_year_, + kMatchCCType, patterns_yy, &expiration_year_, {log_manager_, "^(yy|yyyy)$"})) { return true; } @@ -530,17 +580,23 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, return false; // Try to look for a 2-digit year expiration date. - if (ParseFieldSpecifics( - scanner, base::UTF8ToUTF16(kExpirationDate2DigitYearRe), kMatchCCType, - &expiration_date_, {log_manager_, "kExpirationDate2DigitYearRe"})) { + // 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"})) { 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); if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kExpirationDateRe), - kMatchCCType, &expiration_date_, + kMatchCCType, patterns_exp_d, &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 @@ -554,11 +610,14 @@ 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); if (FieldCanFitDataForFieldType(current_field_max_length, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) && - ParseFieldSpecifics( - scanner, base::UTF8ToUTF16(kExpirationDate4DigitYearRe), kMatchCCType, - &expiration_date_, {log_manager_, "kExpirationDate4DigitYearRe"})) { + ParseFieldSpecifics(scanner, + base::UTF8ToUTF16(kExpirationDate4DigitYearRe), + kMatchCCType, patterns_4dy, &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 974219ba87e..a3aa409c065 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 @@ -12,6 +12,7 @@ #include "base/macros.h" #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" namespace autofill { @@ -42,7 +43,8 @@ class CreditCardField : public FormField { // the next few years. |log_manager| is used to log any parsing details // to chrome://autofill-internals static bool LikelyCardYearSelectField(AutofillScanner* scanner, - LogManager* log_manager); + LogManager* log_manager, + const std::string& page_language); // Returns true if |scanner| points to a <select> field that contains credit // card type options. @@ -53,11 +55,14 @@ class CreditCardField : public FormField { // Prepaid debit cards do not count as gift cards, since they can be used like // a credit card. static bool IsGiftCardField(AutofillScanner* scanner, - LogManager* log_manager); + LogManager* log_manager, + const std::string& page_language); // Parses the expiration month/year/date fields. Returns true if it finds // something new. - bool ParseExpirationDate(AutofillScanner* scanner, LogManager* log_manager); + bool ParseExpirationDate(AutofillScanner* scanner, + LogManager* log_manager, + const std::string& 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 f5e0aab5013..9d9815c4269 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 @@ -13,6 +13,7 @@ #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/autofill_clock.h" #include "components/autofill/core/common/form_field_data.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,14 +24,11 @@ namespace autofill { class CreditCardFieldTestBase { public: - CreditCardFieldTestBase() {} - ~CreditCardFieldTestBase() {} + CreditCardFieldTestBase() = default; + CreditCardFieldTestBase(const CreditCardFieldTestBase&) = delete; + CreditCardFieldTestBase& operator=(const CreditCardFieldTestBase&) = delete; protected: - std::vector<std::unique_ptr<AutofillField>> list_; - std::unique_ptr<const CreditCardField> field_; - FieldCandidatesMap field_candidates_map_; - // Parses the contents of |list_| as a form, and stores the result into // |field_|. void Parse() { @@ -67,17 +65,20 @@ class CreditCardFieldTestBase { return field_->AddClassifications(&field_candidates_map_); } - private: - DISALLOW_COPY_AND_ASSIGN(CreditCardFieldTestBase); + std::vector<std::unique_ptr<AutofillField>> list_; + std::unique_ptr<const CreditCardField> field_; + FieldCandidatesMap field_candidates_map_; + + // RAII object to mock the the PatternProvider. + TestPatternProvider test_pattern_provider_; }; class CreditCardFieldTest : public CreditCardFieldTestBase, public testing::Test { public: - CreditCardFieldTest() {} - - private: - DISALLOW_COPY_AND_ASSIGN(CreditCardFieldTest); + CreditCardFieldTest() = default; + CreditCardFieldTest(const CreditCardFieldTest&) = delete; + CreditCardFieldTest& operator=(const CreditCardFieldTest&) = delete; }; TEST_F(CreditCardFieldTest, Empty) { 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 ad2b95f68b4..99d87e8fc17 100644 --- a/chromium/components/autofill/core/browser/form_parsing/email_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/email_field.cc @@ -5,8 +5,8 @@ #include "components/autofill/core/browser/form_parsing/email_field.h" #include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" -#include "components/autofill/core/common/autofill_regex_constants.h" namespace autofill { @@ -15,8 +15,10 @@ std::unique_ptr<FormField> EmailField::Parse(AutofillScanner* scanner, const std::string& page_language, LogManager* log_manager) { AutofillField* field; + auto& patterns = PatternProvider::GetInstance().GetMatchPatterns( + "EMAIL_ADDRESS", page_language); if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kEmailRe), - MATCH_DEFAULT | MATCH_EMAIL, &field, + MATCH_DEFAULT | MATCH_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 ffcf2dda7a9..3765457f766 100644 --- a/chromium/components/autofill/core/browser/form_parsing/email_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/email_field.h @@ -10,6 +10,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "components/autofill/core/browser/form_parsing/form_field.h" +#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" 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 10cbb1f9966..4b5f80e3ca8 100644 --- a/chromium/components/autofill/core/browser/form_parsing/form_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/form_field.cc @@ -17,6 +17,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/form_parsing/address_field.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" @@ -33,7 +34,6 @@ #include "components/autofill/core/common/autofill_features.h" #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_regexes.h" #include "components/autofill/core/common/autofill_util.h" namespace autofill { @@ -122,7 +122,7 @@ FieldCandidatesMap FormField::ParseFormFields( // For <form> tags, make an exception for email fields, which are commonly // the only recognized field on account registration sites. const bool accept_parsing = - fillable_fields >= MinRequiredFieldsForHeuristics() || + fillable_fields >= kMinRequiredFieldsForHeuristics || (is_form_tag && email_count > 0); if (!accept_parsing) { @@ -168,6 +168,22 @@ bool FormField::ParseField(AutofillScanner* scanner, return ParseFieldSpecifics(scanner, patterns, match, logging); } +bool FormField::ParseField(AutofillScanner* scanner, + const base::string16& pattern, + const std::vector<MatchingPattern>& patterns, + AutofillField** match, + const RegExLogging& logging) { + if (base::FeatureList::IsEnabled( + features::kAutofillUsePageLanguageToSelectFieldParsingPatterns) || + base::FeatureList::IsEnabled( + features:: + kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics)) { + return ParseField(scanner, patterns, match, logging); + } else { + return ParseField(scanner, pattern, match, logging); + } +} + bool FormField::ParseFieldSpecifics(AutofillScanner* scanner, const base::string16& pattern, int match_field_attributes, @@ -207,7 +223,9 @@ bool FormField::ParseFieldSpecifics( if (base::FeatureList::IsEnabled( features:: kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics)) { - if (FormField::Match(field, base::UTF8ToUTF16(pattern.negative_pattern), + if (pattern.negative_pattern.has_value() && + FormField::Match(field, + base::UTF8ToUTF16(pattern.negative_pattern.value()), pattern.match_field_attributes, pattern.match_field_input_types, logging)) { continue; @@ -220,7 +238,6 @@ bool FormField::ParseFieldSpecifics( return true; } } - return false; } @@ -237,6 +254,38 @@ bool FormField::ParseFieldSpecifics(AutofillScanner* scanner, match_field_types, match, logging); } +bool FormField::ParseFieldSpecifics( + AutofillScanner* scanner, + const base::string16& pattern, + int match_type, + const std::vector<MatchingPattern>& patterns, + AutofillField** match, + const RegExLogging& logging, + MatchFieldBitmasks match_field_bitmasks) { + if (base::FeatureList::IsEnabled( + features::kAutofillUsePageLanguageToSelectFieldParsingPatterns) || + base::FeatureList::IsEnabled( + features:: + kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics)) { + // 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) { + 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, patterns, match, logging); + } else { + return ParseFieldSpecifics(scanner, pattern, match_type, match, logging); + } +} + // static bool FormField::ParseEmptyLabel(AutofillScanner* scanner, AutofillField** match) { 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 f2c17a629c5..7eca61ab200 100644 --- a/chromium/components/autofill/core/browser/form_parsing/form_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/form_field.h @@ -72,6 +72,12 @@ class FormField { AutofillField** match, const RegExLogging& logging = {}); + static bool ParseField(AutofillScanner* scanner, + const base::string16& pattern, + const std::vector<MatchingPattern>& patterns, + AutofillField** match, + const RegExLogging& logging = {}); + // Parses the stream of fields in |scanner| with regular expression |pattern| // as specified in the |match_type| bit field (see |MatchType|). If |match| // is non-NULL and the pattern matches, |match| will be set to the matched @@ -96,6 +102,20 @@ class FormField { int match_field_input_types, AutofillField** match, const RegExLogging& logging = {}); + struct MatchFieldBitmasks { + int restrict_attributes = ~0; + int augment_types = 0; + }; + + static bool ParseFieldSpecifics(AutofillScanner* scanner, + const base::string16& pattern, + int match_type, + const std::vector<MatchingPattern>& patterns, + AutofillField** match, + const RegExLogging& logging, + MatchFieldBitmasks match_field_bitmasks = { + .restrict_attributes = ~0, + .augment_types = 0}); // Attempts to parse a field with an empty label. Returns true // on success and fills |match| with a pointer to the field. 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 771275d5ecf..ed5baa21072 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,10 +10,10 @@ #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/common/autofill_features.h" #include "testing/gtest/include/gtest/gtest.h" -using autofill::features::kAutofillEnforceMinRequiredFieldsForHeuristics; using autofill::features::kAutofillFixFillableFieldTypes; using base::ASCIIToUTF16; @@ -112,6 +112,8 @@ 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"; @@ -135,66 +137,27 @@ TEST(FormFieldTest, ParseFormFields) { std::make_unique<AutofillField>(field_data, field_data.label)); // Parse a single address line 1 field. - { - base::test::ScopedFeatureList enforce_min_fields; - enforce_min_fields.InitAndEnableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - // 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()); - } - { - base::test::ScopedFeatureList do_not_enforce_min_fields; - do_not_enforce_min_fields.InitAndDisableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - // 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); - ASSERT_EQ(1u, field_candidates_map.size()); - EXPECT_EQ(ADDRESS_HOME_LINE1, - field_candidates_map.find(ASCIIToUTF16("Address line1")) - ->second.BestHeuristicType()); - } + ASSERT_EQ( + 0u, + FormField::ParseFormFields(fields, /*page_language=*/"", 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)); - { - base::test::ScopedFeatureList enforce_min_fields; - enforce_min_fields.InitAndEnableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - // 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()); - } - { - base::test::ScopedFeatureList do_not_enforce_min_fields; - do_not_enforce_min_fields.InitAndDisableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - // 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); - ASSERT_EQ(2u, field_candidates_map.size()); - EXPECT_EQ(ADDRESS_HOME_LINE1, - field_candidates_map.find(ASCIIToUTF16("Address line1")) - ->second.BestHeuristicType()); - EXPECT_EQ(ADDRESS_HOME_LINE2, - field_candidates_map.find(ASCIIToUTF16("Address line2")) - ->second.BestHeuristicType()); - } + // 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()); } // 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"; @@ -208,16 +171,11 @@ TEST(FormFieldTest, ParseFormFieldEnforceMinFillableFields) { std::make_unique<AutofillField>(field_data, field_data.label)); // Don't parse forms with 2 fields. - { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - // 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()); - } + // 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()); field_data.label = ASCIIToUTF16("Search"); fields.push_back( @@ -227,10 +185,7 @@ TEST(FormFieldTest, ParseFormFieldEnforceMinFillableFields) { // now, although a search field is not fillable. { base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures( - /*enabled_features=*/ - {kAutofillEnforceMinRequiredFieldsForHeuristics}, - /*disabled_features=*/{kAutofillFixFillableFieldTypes}); + feature_list.InitAndDisableFeature(kAutofillFixFillableFieldTypes); // An empty page_language means the language is unknown and patterns of all // languages are used. EXPECT_EQ( @@ -242,11 +197,7 @@ TEST(FormFieldTest, ParseFormFieldEnforceMinFillableFields) { // fillable (therefore, the form has only 2 fillable fields). { base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures( - /*enabled_features=*/ - {kAutofillEnforceMinRequiredFieldsForHeuristics, - kAutofillFixFillableFieldTypes}, - /*disabled_features=*/{}); + feature_list.InitAndEnableFeature(kAutofillFixFillableFieldTypes); // An empty page_language means the language is unknown and patterns of all // languages are used. const FieldCandidatesMap field_candidates_map = 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 0db9bd926fc..0fd65aa87c9 100644 --- a/chromium/components/autofill/core/browser/form_parsing/name_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/name_field.cc @@ -10,10 +10,10 @@ #include "base/macros.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" #include "components/autofill/core/common/autofill_features.h" -#include "components/autofill/core/common/autofill_regex_constants.h" using base::UTF8ToUTF16; @@ -24,6 +24,7 @@ namespace { class FullNameField : public NameField { public: static std::unique_ptr<FullNameField> Parse(AutofillScanner* scanner, + const std::string& page_language, LogManager* log_manager); explicit FullNameField(AutofillField* field); @@ -42,9 +43,12 @@ class FirstTwoLastNamesField : public NameField { public: static std::unique_ptr<FirstTwoLastNamesField> ParseComponentNames( AutofillScanner* scanner, + const std::string& page_language, + LogManager* log_manager); + static std::unique_ptr<FirstTwoLastNamesField> Parse( + AutofillScanner* scanner, + const std::string& page_language, LogManager* log_manager); - static std::unique_ptr<FirstTwoLastNamesField> Parse(AutofillScanner* scanner, - LogManager* log_manager); protected: void AddClassifications(FieldCandidatesMap* field_candidates) const override; @@ -67,12 +71,16 @@ class FirstLastNameField : public NameField { public: static std::unique_ptr<FirstLastNameField> ParseSpecificName( AutofillScanner* scanner, + const std::string& page_language, LogManager* log_manager); static std::unique_ptr<FirstLastNameField> ParseComponentNames( AutofillScanner* scanner, + const std::string& page_language, + LogManager* log_manager); + static std::unique_ptr<FirstLastNameField> Parse( + AutofillScanner* scanner, + const std::string& page_language, LogManager* log_manager); - static std::unique_ptr<FirstLastNameField> Parse(AutofillScanner* scanner, - LogManager* log_manager); protected: void AddClassifications(FieldCandidatesMap* field_candidates) const override; @@ -103,11 +111,11 @@ std::unique_ptr<FormField> NameField::Parse(AutofillScanner* scanner, std::unique_ptr<FormField> field; if (!field && base::FeatureList::IsEnabled( features::kAutofillEnableSupportForMoreStructureInNames)) - field = FirstTwoLastNamesField::Parse(scanner, log_manager); + field = FirstTwoLastNamesField::Parse(scanner, page_language, log_manager); if (!field) - field = FirstLastNameField::Parse(scanner, log_manager); + field = FirstLastNameField::Parse(scanner, page_language, log_manager); if (!field) - field = FullNameField::Parse(scanner, log_manager); + field = FullNameField::Parse(scanner, page_language, log_manager); return field; } @@ -116,12 +124,17 @@ void NameField::AddClassifications(FieldCandidatesMap* field_candidates) const { } // static -std::unique_ptr<FullNameField> FullNameField::Parse(AutofillScanner* scanner, - LogManager* log_manager) { +std::unique_ptr<FullNameField> FullNameField::Parse( + AutofillScanner* scanner, + const std::string& page_language, + LogManager* log_manager) { // Exclude e.g. "username" or "nickname" fields. scanner->SaveCursor(); - bool should_ignore = ParseField(scanner, UTF8ToUTF16(kNameIgnoredRe), nullptr, - {log_manager, "kNameIgnoredRe"}); + auto& patterns_ni = PatternProvider::GetInstance().GetMatchPatterns( + "NAME_IGNORED", page_language); + bool should_ignore = + ParseField(scanner, UTF8ToUTF16(kNameIgnoredRe), patterns_ni, nullptr, + {log_manager, "kNameIgnoredRe"}); scanner->Rewind(); if (should_ignore) return nullptr; @@ -130,7 +143,10 @@ std::unique_ptr<FullNameField> FullNameField::Parse(AutofillScanner* scanner, // for example, Travelocity_Edit travel profile.html contains a field // "Travel Profile Name". AutofillField* field = nullptr; - if (ParseField(scanner, UTF8ToUTF16(kNameRe), &field, + // In JSON : FULL_NAME (closest vatiant) + auto& patterns_name = PatternProvider::GetInstance().GetMatchPatterns( + "FULL_NAME", page_language); + if (ParseField(scanner, UTF8ToUTF16(kNameRe), patterns_name, &field, {log_manager, "kNameRe"})) return std::make_unique<FullNameField>(field); @@ -149,17 +165,32 @@ FirstTwoLastNamesField::FirstTwoLastNamesField() = default; // static std::unique_ptr<FirstTwoLastNamesField> FirstTwoLastNamesField::Parse( AutofillScanner* scanner, + const std::string& page_language, LogManager* log_manager) { - return ParseComponentNames(scanner, log_manager); + return ParseComponentNames(scanner, page_language, log_manager); } // static std::unique_ptr<FirstTwoLastNamesField> FirstTwoLastNamesField::ParseComponentNames(AutofillScanner* scanner, + const std::string& 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); + // Allow name fields to appear in any order. while (!scanner->IsEnd()) { // Scan for the honorific prefix before checking for unrelated name fields @@ -168,7 +199,7 @@ FirstTwoLastNamesField::ParseComponentNames(AutofillScanner* scanner, // TODO(crbug.com/1098943): Remove check once feature is launched or // removed. if (!v->honorific_prefix_ && - ParseField(scanner, UTF8ToUTF16(kHonorificPrefixRe), + ParseField(scanner, UTF8ToUTF16(kHonorificPrefixRe), patterns_hp, &v->honorific_prefix_, {log_manager, "kHonorificPrefixRe"})) { continue; @@ -177,30 +208,31 @@ 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, - nullptr, {log_manager, "kNameIgnoredRe"})) { + patterns_ni, nullptr, + {log_manager, "kNameIgnoredRe"})) { continue; } if (!v->first_name_ && - ParseField(scanner, UTF8ToUTF16(kFirstNameRe), &v->first_name_, - {log_manager, "kFirstNameRe"})) { + ParseField(scanner, UTF8ToUTF16(kFirstNameRe), patterns_fn, + &v->first_name_, {log_manager, "kFirstNameRe"})) { continue; } if (!v->middle_name_ && - ParseField(scanner, UTF8ToUTF16(kMiddleNameRe), &v->middle_name_, - {log_manager, "kMiddleNameRe"})) { + ParseField(scanner, UTF8ToUTF16(kMiddleNameRe), patterns_mn, + &v->middle_name_, {log_manager, "kMiddleNameRe"})) { continue; } if (!v->first_last_name_ && - ParseField(scanner, UTF8ToUTF16(kNameLastFirstRe), &v->first_last_name_, - {log_manager, "kNameLastFirstRe"})) { + ParseField(scanner, UTF8ToUTF16(kNameLastFirstRe), patterns_ln1, + &v->first_last_name_, {log_manager, "kNameLastFirstRe"})) { continue; } if (!v->second_last_name_ && - ParseField(scanner, UTF8ToUTF16(kNameLastSecondRe), + ParseField(scanner, UTF8ToUTF16(kNameLastSecondRe), patterns_ln2, &v->second_last_name_, {log_manager, "kNameLastSecondtRe"})) { continue; @@ -235,6 +267,7 @@ void FirstTwoLastNamesField::AddClassifications( std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseSpecificName( AutofillScanner* scanner, + const std::string& 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. @@ -242,8 +275,11 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseSpecificName( scanner->SaveCursor(); AutofillField* next = nullptr; - if (ParseField(scanner, UTF8ToUTF16(kNameSpecificRe), &v->first_name_, - {log_manager, "kNameSpecificRe"}) && + auto& patterns_ns = PatternProvider::GetInstance().GetMatchPatterns( + "NAME_SPECIFIC", page_language); + + if (ParseField(scanner, UTF8ToUTF16(kNameSpecificRe), patterns_ns, + &v->first_name_, {log_manager, "kNameSpecificRe"}) && ParseEmptyLabel(scanner, &next)) { if (ParseEmptyLabel(scanner, &v->last_name_)) { // There are three name fields; assume that the middle one is a @@ -264,6 +300,7 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseSpecificName( // static std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames( AutofillScanner* scanner, + const std::string& page_language, LogManager* log_manager) { std::unique_ptr<FirstLastNameField> v(new FirstLastNameField); scanner->SaveCursor(); @@ -279,6 +316,20 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames( // The ".*last$" matches fields ending in "last" (example in sample8.html). // 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); + while (!scanner->IsEnd()) { // Scan for the honorific prefix before checking for unrelated fields // because a honorific prefix field is expected to have very specific labels @@ -288,7 +339,7 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames( if (base::FeatureList::IsEnabled( features::kAutofillEnableSupportForMoreStructureInNames)) { if (!v->honorific_prefix_ && - ParseField(scanner, UTF8ToUTF16(kHonorificPrefixRe), + ParseField(scanner, UTF8ToUTF16(kHonorificPrefixRe), patterns_hp, &v->honorific_prefix_, {log_manager, "kHonorificPrefixRe"})) { continue; @@ -298,13 +349,14 @@ 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, - nullptr, {log_manager, "kNameIgnoredRe"})) { + patterns_ni, nullptr, + {log_manager, "kNameIgnoredRe"})) { continue; } if (!v->first_name_ && - ParseField(scanner, UTF8ToUTF16(kFirstNameRe), &v->first_name_, - {log_manager, "kFirstNameRe"})) { + ParseField(scanner, UTF8ToUTF16(kFirstNameRe), patterns_fn, + &v->first_name_, {log_manager, "kFirstNameRe"})) { continue; } @@ -314,21 +366,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), &v->middle_name_, - {log_manager, "kMiddleInitialRe"})) { + ParseField(scanner, UTF8ToUTF16(kMiddleInitialRe), patterns_mi, + &v->middle_name_, {log_manager, "kMiddleInitialRe"})) { v->middle_initial_ = true; continue; } if (!v->middle_name_ && - ParseField(scanner, UTF8ToUTF16(kMiddleNameRe), &v->middle_name_, - {log_manager, "kMiddleNameRe"})) { + ParseField(scanner, UTF8ToUTF16(kMiddleNameRe), patterns_mn, + &v->middle_name_, {log_manager, "kMiddleNameRe"})) { continue; } if (!v->last_name_ && - ParseField(scanner, UTF8ToUTF16(kLastNameRe), &v->last_name_, - {log_manager, "kLastNameRe"})) { + ParseField(scanner, UTF8ToUTF16(kLastNameRe), patterns_ln, + &v->last_name_, {log_manager, "kLastNameRe"})) { continue; } @@ -347,11 +399,12 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames( // static std::unique_ptr<FirstLastNameField> FirstLastNameField::Parse( AutofillScanner* scanner, + const std::string& page_language, LogManager* log_manager) { std::unique_ptr<FirstLastNameField> field = - ParseSpecificName(scanner, log_manager); + ParseSpecificName(scanner, page_language, log_manager); if (!field) - field = ParseComponentNames(scanner, log_manager); + field = ParseComponentNames(scanner, page_language, log_manager); return field; } 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 26048b596bb..7298fbdb7d5 100644 --- a/chromium/components/autofill/core/browser/form_parsing/name_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/name_field.h @@ -13,6 +13,7 @@ #include "base/macros.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/pattern_provider.h" namespace autofill { 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 2931c9028d5..e83e8187b8c 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 @@ -12,10 +12,11 @@ #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/common/autofill_features.h" -#include "components/autofill/core/common/autofill_regex_constants.h" -#include "components/autofill/core/common/autofill_regexes.h" #include "components/autofill/core/common/form_field_data.h" #include "testing/gtest/include/gtest/gtest.h" @@ -25,13 +26,11 @@ namespace autofill { class NameFieldTest : public testing::Test { public: - NameFieldTest() {} + NameFieldTest() = default; + NameFieldTest(const NameFieldTest&) = delete; + NameFieldTest& operator=(const NameFieldTest&) = delete; protected: - std::vector<std::unique_ptr<AutofillField>> list_; - std::unique_ptr<NameField> field_; - FieldCandidatesMap field_candidates_map_; - // Downcast for tests. static std::unique_ptr<NameField> Parse(AutofillScanner* scanner) { // An empty page_language means the language is unknown and patterns of all @@ -41,8 +40,12 @@ class NameFieldTest : public testing::Test { return std::unique_ptr<NameField>(static_cast<NameField*>(field.release())); } - private: - DISALLOW_COPY_AND_ASSIGN(NameFieldTest); + 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) { 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 82d6294891a..b50388208be 100644 --- a/chromium/components/autofill/core/browser/form_parsing/phone_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/phone_field.cc @@ -16,10 +16,10 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.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/common/autofill_features.h" -#include "components/autofill/core/common/autofill_regex_constants.h" -#include "components/autofill/core/common/autofill_regexes.h" namespace autofill { namespace { @@ -246,7 +246,8 @@ std::unique_ptr<FormField> PhoneField::Parse(AutofillScanner* scanner, scanner, GetRegExp(kPhoneFieldGrammars[i].regex), &parsed_fields[kPhoneFieldGrammars[i].phone_part], {log_manager, GetRegExpName(kPhoneFieldGrammars[i].regex)}, - is_country_code_field)) + is_country_code_field, + GetJSONFieldType(kPhoneFieldGrammars[i].regex), page_language)) break; if (kPhoneFieldGrammars[i].max_size && (!parsed_fields[kPhoneFieldGrammars[i].phone_part]->max_length || @@ -291,11 +292,13 @@ std::unique_ptr<FormField> PhoneField::Parse(AutofillScanner* scanner, if (!ParsePhoneField(scanner, kPhoneSuffixRe, &phone_field->parsed_phone_fields_[FIELD_SUFFIX], {log_manager, "kPhoneSuffixRe"}, - /*is_country_code_field=*/false)) { + /*is_country_code_field=*/false, "PHONE_SUFFIX", + page_language)) { ParsePhoneField(scanner, kPhoneSuffixSeparatorRe, &phone_field->parsed_phone_fields_[FIELD_SUFFIX], {log_manager, "kPhoneSuffixSeparatorRe"}, - /*is_country_code_field=*/false); + /*is_country_code_field=*/false, "PHONE_SUFFIX_SEPARATOR", + page_language); } } @@ -305,7 +308,8 @@ std::unique_ptr<FormField> PhoneField::Parse(AutofillScanner* scanner, ParsePhoneField(scanner, kPhoneExtensionRe, &phone_field->parsed_phone_fields_[FIELD_EXTENSION], {log_manager, "kPhoneExtensionRe"}, - /*is_country_code_field=*/false); + /*is_country_code_field=*/false, "PHONE_EXTENSION", + page_language); return std::move(phone_field); } @@ -416,19 +420,52 @@ const char* PhoneField::GetRegExpName(RegexType regex_id) { return ""; } +// +std::string PhoneField::GetJSONFieldType(RegexType phonetype_id) { + switch (phonetype_id) { + case REGEX_COUNTRY: + return "PHONE_COUNTRY_CODE"; + case REGEX_AREA: + return "PHONE_AREA_CODE"; + case REGEX_AREA_NOTEXT: + return "PHONE_AREA_CODE_NO_TEXT"; + case REGEX_PHONE: + return "PHONE"; + case REGEX_PREFIX_SEPARATOR: + return "PHONE_PREFIX_SEPARATOR"; + case REGEX_PREFIX: + return "PHONE_PREFIX"; + case REGEX_SUFFIX_SEPARATOR: + return "PHONE_SUFFIX_SEPARATOR"; + case REGEX_SUFFIX: + return "PHONE_SUFFIX"; + case REGEX_EXTENSION: + return "PHONE_EXTENSION"; + default: + NOTREACHED(); + break; + } + return std::string(); +} + // static bool PhoneField::ParsePhoneField(AutofillScanner* scanner, const std::string& regex, AutofillField** field, const RegExLogging& logging, - const bool is_country_code_field) { + const bool is_country_code_field, + const std::string& json_field_type, + const std::string& 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); + return ParseFieldSpecifics(scanner, base::UTF8ToUTF16(regex), match_type, - field, logging); + patterns, field, logging); } } // namespace autofill 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 f0f39a3928f..6ac25f26693 100644 --- a/chromium/components/autofill/core/browser/form_parsing/phone_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/phone_field.h @@ -17,6 +17,7 @@ #include "components/autofill/core/browser/autofill_type.h" #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" namespace autofill { @@ -96,12 +97,18 @@ class PhoneField : public FormField { // This is useful for logging purposes. static const char* GetRegExpName(RegexType regex_id); + // Returns the name of field type which indicated in JSON corresponding to + // |regex_id|. + static std::string GetJSONFieldType(RegexType phonetype_id); + // Convenient wrapper for ParseFieldSpecifics(). static bool ParsePhoneField(AutofillScanner* scanner, const std::string& regex, AutofillField** field, const RegExLogging& logging, - const bool is_country_code_field); + const bool is_country_code_field, + const std::string& json_field_type, + const std::string& 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 72469564fd3..71e0cf605c4 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 @@ -16,6 +16,7 @@ #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" @@ -83,6 +84,9 @@ class PhoneFieldTest : public testing::Test { std::vector<std::unique_ptr<AutofillField>> list_; std::unique_ptr<PhoneField> field_; FieldCandidatesMap field_candidates_map_; + + // RAII object to mock the the PatternProvider. + TestPatternProvider test_pattern_provider_; }; TEST_F(PhoneFieldTest, Empty) { 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 58079a2800f..595b759ad00 100644 --- a/chromium/components/autofill/core/browser/form_parsing/price_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/price_field.cc @@ -6,8 +6,8 @@ #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" -#include "components/autofill/core/common/autofill_regex_constants.h" namespace autofill { @@ -16,10 +16,13 @@ std::unique_ptr<FormField> PriceField::Parse(AutofillScanner* scanner, const std::string& page_language, LogManager* log_manager) { AutofillField* field; + auto& patterns = + PatternProvider::GetInstance().GetMatchPatterns("PRICE", page_language); + if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kPriceRe), MATCH_DEFAULT | MATCH_NUMBER | MATCH_SELECT | MATCH_TEXT_AREA | MATCH_SEARCH, - &field, {log_manager, kPriceRe})) { + 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 b3023982b57..7d05f460dae 100644 --- a/chromium/components/autofill/core/browser/form_parsing/price_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/price_field.h @@ -10,6 +10,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "components/autofill/core/browser/form_parsing/form_field.h" +#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" namespace autofill { 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 9f6110de258..464830b2852 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 @@ -13,6 +13,7 @@ #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" @@ -22,13 +23,11 @@ namespace autofill { class PriceFieldTest : public testing::Test { public: - PriceFieldTest() {} + PriceFieldTest() = default; + PriceFieldTest(const PriceFieldTest&) = delete; + PriceFieldTest& operator=(const PriceFieldTest&) = delete; protected: - std::vector<std::unique_ptr<AutofillField>> list_; - std::unique_ptr<PriceField> field_; - FieldCandidatesMap field_candidates_map_; - // Downcast for tests. static std::unique_ptr<PriceField> Parse(AutofillScanner* scanner) { // An empty page_language means the language is unknown and patterns of all @@ -39,8 +38,12 @@ class PriceFieldTest : public testing::Test { static_cast<PriceField*>(field.release())); } - private: - DISALLOW_COPY_AND_ASSIGN(PriceFieldTest); + 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) { 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 a2065dc0c30..a7564839f06 100644 --- a/chromium/components/autofill/core/browser/form_parsing/search_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/search_field.cc @@ -6,8 +6,8 @@ #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" -#include "components/autofill/core/common/autofill_regex_constants.h" namespace autofill { @@ -16,9 +16,12 @@ std::unique_ptr<FormField> SearchField::Parse(AutofillScanner* scanner, const std::string& page_language, LogManager* log_manager) { AutofillField* field; + auto& patterns = PatternProvider::GetInstance().GetMatchPatterns( + SEARCH_TERM, page_language); + if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kSearchTermRe), MATCH_DEFAULT | MATCH_SEARCH | MATCH_TEXT_AREA, - &field, {log_manager, "kSearchTermRe"})) { + patterns, &field, {log_manager, "kSearchTermRe"})) { return std::make_unique<SearchField>(field); } 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 71b2f70bb48..99e70eb9a2c 100644 --- a/chromium/components/autofill/core/browser/form_parsing/search_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/search_field.h @@ -10,6 +10,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "components/autofill/core/browser/form_parsing/form_field.h" +#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" namespace autofill { 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 09148d51010..a6be80cc7e0 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 @@ -13,6 +13,7 @@ #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" @@ -22,13 +23,11 @@ namespace autofill { class SearchFieldTest : public testing::Test { public: - SearchFieldTest() {} + SearchFieldTest() = default; + SearchFieldTest(const SearchFieldTest&) = delete; + SearchFieldTest& operator=(const SearchFieldTest&) = delete; protected: - std::vector<std::unique_ptr<AutofillField>> list_; - std::unique_ptr<SearchField> field_; - FieldCandidatesMap field_candidates_map_; - // Downcast for tests. static std::unique_ptr<SearchField> Parse(AutofillScanner* scanner) { // An empty page_language means the language is unknown and patterns of all @@ -39,8 +38,12 @@ class SearchFieldTest : public testing::Test { static_cast<SearchField*>(field.release())); } - private: - DISALLOW_COPY_AND_ASSIGN(SearchFieldTest); + 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) { 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 cf90e229903..2e1dc92a836 100644 --- a/chromium/components/autofill/core/browser/form_parsing/travel_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/travel_field.cc @@ -8,7 +8,7 @@ #include <utility> #include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/common/autofill_regex_constants.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" namespace autofill { @@ -21,17 +21,25 @@ std::unique_ptr<FormField> TravelField::Parse(AutofillScanner* scanner, 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 = + PatternProvider::GetInstance().GetMatchPatterns("FLIGHT", page_language); auto travel_field = std::make_unique<TravelField>(); - if (ParseField(scanner, base::UTF8ToUTF16(kPassportRe), + if (ParseField(scanner, base::UTF8ToUTF16(kPassportRe), patternsP, &travel_field->passport_, {log_manager, "kPassportRe"}) || - ParseField(scanner, base::UTF8ToUTF16(kTravelOriginRe), + ParseField(scanner, base::UTF8ToUTF16(kTravelOriginRe), patternsTO, &travel_field->origin_, {log_manager, "kTravelOriginRe"}) || - ParseField(scanner, base::UTF8ToUTF16(kTravelDestinationRe), + ParseField(scanner, base::UTF8ToUTF16(kTravelDestinationRe), patternsTD, &travel_field->destination_, {log_manager, "kTravelDestinationRe"}) || - ParseField(scanner, base::UTF8ToUTF16(kFlightRe), &travel_field->flight_, - {log_manager, "kFlightRe"})) { + ParseField(scanner, base::UTF8ToUTF16(kFlightRe), patternsF, + &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 8ac09a00df3..9b2d2e5e891 100644 --- a/chromium/components/autofill/core/browser/form_parsing/travel_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/travel_field.h @@ -9,6 +9,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" namespace autofill { diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc index ff7993d82e2..9bd72190efa 100644 --- a/chromium/components/autofill/core/browser/form_structure.cc +++ b/chromium/components/autofill/core/browser/form_structure.cc @@ -32,6 +32,8 @@ #include "base/time/time.h" #include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/autofill_metrics.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" +#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_parsing/field_candidates.h" @@ -45,8 +47,6 @@ #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_regex_constants.h" -#include "components/autofill/core/common/autofill_regexes.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" @@ -56,6 +56,7 @@ #include "components/autofill/core/common/logging/log_buffer.h" #include "components/autofill/core/common/signatures.h" #include "components/security_state/core/security_state.h" +#include "components/version_info/version_info.h" #include "url/origin.h" namespace autofill { @@ -64,8 +65,6 @@ using mojom::SubmissionIndicatorEvent; namespace { -// Version of the client sent to the server. -constexpr char kClientVersion[] = "6.1.1715.1442/en (GGLL)"; constexpr char kBillingMode[] = "billing"; constexpr char kShippingMode[] = "shipping"; @@ -705,7 +704,8 @@ bool FormStructure::EncodeUploadRequest( encoded_signatures->clear(); upload->set_submission(observed_submission); - upload->set_client_version(kClientVersion); + upload->set_client_version( + version_info::GetProductNameAndVersionForUserAgent()); upload->set_form_signature(form_signature().value()); upload->set_autofill_used(form_was_autofilled); upload->set_data_present(EncodeFieldTypes(available_field_types)); @@ -763,7 +763,8 @@ bool FormStructure::EncodeQueryRequest( queried_form_signatures->clear(); queried_form_signatures->reserve(forms.size()); - query->set_client_version(kClientVersion); + query->set_client_version( + version_info::GetProductNameAndVersionForUserAgent()); // If a page contains repeated forms, detect that and encode only one form as // the returned data would be the same for all the repeated forms. @@ -889,7 +890,6 @@ void FormStructure::ProcessQueryResponse( form->UpdateAutofillCount(); form->RationalizeRepeatedFields(form_interactions_ukm_logger); form->RationalizeFieldTypePredictions(); - form->OverrideServerPredictionsWithHeuristics(); form->IdentifySections(false); } @@ -956,8 +956,8 @@ std::string FormStructure::FormSignatureAsStr() const { bool FormStructure::IsAutofillable() const { size_t min_required_fields = - std::min({MinRequiredFieldsForHeuristics(), MinRequiredFieldsForQuery(), - MinRequiredFieldsForUpload()}); + std::min({kMinRequiredFieldsForHeuristics, kMinRequiredFieldsForQuery, + kMinRequiredFieldsForUpload}); if (autofill_count() < min_required_fields) return false; @@ -999,8 +999,8 @@ bool FormStructure::ShouldBeParsed(LogManager* log_manager) const { } size_t min_required_fields = - std::min({MinRequiredFieldsForHeuristics(), MinRequiredFieldsForQuery(), - MinRequiredFieldsForUpload()}); + std::min({kMinRequiredFieldsForHeuristics, kMinRequiredFieldsForQuery, + kMinRequiredFieldsForUpload}); if (active_field_count() < min_required_fields && (!all_fields_are_passwords() || active_field_count() < kRequiredFieldsForFormsWithOnlyPasswordFields) && @@ -1040,7 +1040,7 @@ bool FormStructure::ShouldBeParsed(LogManager* log_manager) const { } bool FormStructure::ShouldRunHeuristics() const { - return active_field_count() >= MinRequiredFieldsForHeuristics() && + return active_field_count() >= kMinRequiredFieldsForHeuristics && HasAllowedScheme(source_url_) && (is_form_tag_ || is_formless_checkout_ || !base::FeatureList::IsEnabled( @@ -1049,12 +1049,12 @@ bool FormStructure::ShouldRunHeuristics() const { bool FormStructure::ShouldBeQueried() const { return (has_password_field_ || - active_field_count() >= MinRequiredFieldsForQuery()) && + active_field_count() >= kMinRequiredFieldsForQuery) && ShouldBeParsed(); } bool FormStructure::ShouldBeUploaded() const { - return active_field_count() >= MinRequiredFieldsForUpload() && + return active_field_count() >= kMinRequiredFieldsForUpload && ShouldBeParsed(); } @@ -1062,37 +1062,21 @@ void FormStructure::RetrieveFromCache( const FormStructure& cached_form, const bool should_keep_cached_value, const bool only_server_and_autofill_state) { - // TODO(crbug/1101631) Clean up once the experiment is over. - const bool kUseRendererIds = base::FeatureList::IsEnabled( - features::kAutofillRetrieveFromCacheWithRendererIds); - std::map<base::string16, const AutofillField*> cached_fields_by_name; std::map<FieldRendererId, const AutofillField*> cached_fields_by_id; for (size_t i = 0; i < cached_form.field_count(); ++i) { auto* const field = cached_form.field(i); - if (kUseRendererIds) - cached_fields_by_id[field->unique_renderer_id] = field; - else - cached_fields_by_name[field->unique_name()] = field; + cached_fields_by_id[field->unique_renderer_id] = field; } for (auto& field : *this) { const AutofillField* cached_field = nullptr; - if (kUseRendererIds) { - const auto& it = cached_fields_by_id.find(field->unique_renderer_id); - if (it != cached_fields_by_id.end()) - cached_field = it->second; - } else { - const auto& it = cached_fields_by_name.find(field->unique_name()); - if (it != cached_fields_by_name.end()) - cached_field = it->second; - } + const auto& it = cached_fields_by_id.find(field->unique_renderer_id); + if (it != cached_fields_by_id.end()) + cached_field = it->second; // If the unique renderer id (or the name) is not stable due to some Java // Script magic in the website, use the field signature as a fallback // solution to find the field in the cached form. - // TODO(crbug.com/1125624): Remove feature check once trial ended. - if (!cached_field && - base::FeatureList::IsEnabled( - features::kAutofillRetrieveFromCacheWithFieldSignatureAsFallback)) { + if (!cached_field) { // Iterates over the fields to find the field with the same form // signature. for (size_t i = 0; i < cached_form.field_count(); ++i) { @@ -1125,24 +1109,16 @@ void FormStructure::RetrieveFromCache( field->is_autofilled = cached_field->is_autofilled; } if (field->form_control_type != "select-one") { - bool is_credit_card_field = - AutofillType(cached_field->Type().GetStorableType()).group() == - CREDIT_CARD; - if (should_keep_cached_value && - (is_credit_card_field || - base::FeatureList::IsEnabled( - features::kAutofillKeepInitialFormValuesInCache))) { + if (should_keep_cached_value) { field->value = cached_field->value; value_from_dynamic_change_form_ = true; } else if (field->value == cached_field->value && - (!base::FeatureList::IsEnabled( - features:: - kAutofillImportPrefilledCountryAndStateValues) || - (field->server_type() != ADDRESS_HOME_COUNTRY && - field->server_type() != ADDRESS_HOME_STATE))) { - // TODO(crbug.com/1100231): Remove feature check once launched. + (field->server_type() != ADDRESS_HOME_COUNTRY && + field->server_type() != ADDRESS_HOME_STATE)) { // From the perspective of learning user data, text fields containing // default values are equivalent to empty fields. + // Since a website can prefill country and state values basedw on + // GeoIp, the mechanism is deactivated for state and country fields. field->value = base::string16(); } } @@ -1242,8 +1218,8 @@ void FormStructure::LogQualityMetrics( // submission event. if (observed_submission) { AutofillMetrics::AutofillFormSubmittedState state; - if (num_detected_field_types < MinRequiredFieldsForHeuristics() && - num_detected_field_types < MinRequiredFieldsForQuery()) { + if (num_detected_field_types < kMinRequiredFieldsForHeuristics && + num_detected_field_types < kMinRequiredFieldsForQuery) { state = AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA; } else { if (did_autofill_all_possible_fields) { @@ -1871,27 +1847,6 @@ void FormStructure::RationalizeAddressStateCountry( } } -void FormStructure::OverrideServerPredictionsWithHeuristics() { - if (!base::FeatureList::IsEnabled( - features::kAutofillEnableSupportForMoreStructureInNames) && - !base::FeatureList::IsEnabled( - features::kAutofillEnableSupportForMoreStructureInAddresses)) { - return; - } - for (const auto& field : fields_) { - switch (field->heuristic_type()) { - case NAME_LAST_SECOND: - case NAME_LAST_FIRST: - case ADDRESS_HOME_STREET_NAME: - case ADDRESS_HOME_HOUSE_NUMBER: - field->SetTypeTo(AutofillType(field->heuristic_type())); - break; - default: { - }; - } - } -} - void FormStructure::RationalizeRepeatedFields( AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) { // The type of every field whose index is in @@ -2090,9 +2045,21 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { 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(); + } + }; + if (!has_author_specified_sections || is_enabled_autofill_new_sectioning) { - // Name sections after the first field in the section. - base::string16 current_section = fields_.front()->unique_name(); + 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; @@ -2214,7 +2181,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { } // The end of a section, so start a new section. - current_section = field->unique_name(); + current_section = get_section_name(*field); if (is_enabled_autofill_new_sectioning) { // The section described in the autocomplete section attribute diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h index a18c4d9b581..58a8a8d48dc 100644 --- a/chromium/components/autofill/core/browser/form_structure.h +++ b/chromium/components/autofill/core/browser/form_structure.h @@ -25,7 +25,6 @@ #include "components/autofill/core/browser/form_types.h" #include "components/autofill/core/browser/proto/api_v1.pb.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" -#include "components/autofill/core/common/password_form.h" #include "components/autofill/core/common/renderer_id.h" #include "url/gurl.h" #include "url/origin.h" @@ -399,11 +398,12 @@ class FormStructure { private: friend class AutofillMergeTest; friend class FormStructureTestImpl; + friend class ParameterizedFormStructureTest; FRIEND_TEST_ALL_PREFIXES(AutofillDownloadTest, QueryAndUploadTest); FRIEND_TEST_ALL_PREFIXES(FormStructureTestImpl, FindLongestCommonPrefix); FRIEND_TEST_ALL_PREFIXES(FormStructureTestImpl, FindLongestCommonAffixLength); FRIEND_TEST_ALL_PREFIXES(FormStructureTestImpl, IsValidParseableName); - FRIEND_TEST_ALL_PREFIXES(FormStructureTestImpl, + FRIEND_TEST_ALL_PREFIXES(ParameterizedFormStructureTest, RationalizePhoneNumber_RunsOncePerSection); class SectionedFieldsIndexes { diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc index 6125363ecb2..4e894075277 100644 --- a/chromium/components/autofill/core/browser/form_structure_unittest.cc +++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc @@ -19,27 +19,26 @@ #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" #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/autofill/core/common/password_form.h" #include "components/autofill/core/common/signatures.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/prefs/testing_pref_service.h" +#include "components/version_info/version_info.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" using base::ASCIIToUTF16; +using version_info::GetProductNameAndVersionForUserAgent; namespace autofill { -using features::kAutofillEnforceMinRequiredFieldsForHeuristics; -using features::kAutofillEnforceMinRequiredFieldsForQuery; -using features::kAutofillEnforceMinRequiredFieldsForUpload; using features::kAutofillLabelAffixRemoval; using mojom::SubmissionIndicatorEvent; using mojom::SubmissionSource; @@ -90,74 +89,22 @@ class FormStructureTestImpl : public test::FormStructureTest { feature_list->InitAndDisableFeature(feature); } - // Single field forms are not parseable iff all of the minimum required field - // values are enforced. - void CheckFormShouldBeParsed(const char* trace_message, - const FormData form, - bool expected_if_all_enforced, - bool expected_if_not_all_enforced) { - SCOPED_TRACE(trace_message); - for (bool enforce_min_for_heuristics : {true, false}) { - base::test::ScopedFeatureList heuristics, query, upload; - InitFeature(&heuristics, kAutofillEnforceMinRequiredFieldsForHeuristics, - enforce_min_for_heuristics); - for (bool enforce_min_for_query : {true, false}) { - base::test::ScopedFeatureList heuristics, query, upload; - InitFeature(&query, kAutofillEnforceMinRequiredFieldsForQuery, - enforce_min_for_query); - for (bool enforce_min_for_upload : {true, false}) { - base::test::ScopedFeatureList heuristics, query, upload; - InitFeature(&upload, kAutofillEnforceMinRequiredFieldsForUpload, - enforce_min_for_upload); - bool all_enforced = enforce_min_for_heuristics && - enforce_min_for_query && enforce_min_for_upload; - FormStructure form_structure(form); - if (all_enforced) { - EXPECT_EQ(expected_if_all_enforced, - form_structure.ShouldBeParsed()); - } else { - EXPECT_EQ(expected_if_not_all_enforced, - form_structure.ShouldBeParsed()) - << "heuristics:" << enforce_min_for_heuristics << "; " - << "query:" << enforce_min_for_query << "; " - << "upload:" << enforce_min_for_upload; - } - } - } - } + bool FormShouldBeParsed(const FormData form) { + return FormStructure(form).ShouldBeParsed(); } - bool FormIsAutofillable(const FormData& form, bool enforce_min_fields) { - base::test::ScopedFeatureList feature_list; - InitFeature(&feature_list, kAutofillEnforceMinRequiredFieldsForHeuristics, - enforce_min_fields); + bool FormIsAutofillable(const FormData& form) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); return form_structure.IsAutofillable(); } - bool FormShouldRunHeuristics(const FormData& form, bool enforce_min_fields) { - base::test::ScopedFeatureList feature_list; - InitFeature(&feature_list, kAutofillEnforceMinRequiredFieldsForHeuristics, - enforce_min_fields); - FormStructure form_structure(form); - return form_structure.ShouldRunHeuristics(); + bool FormShouldRunHeuristics(const FormData& form) { + return FormStructure(form).ShouldRunHeuristics(); } - bool FormShouldBeQueried(const FormData& form, bool enforce_min_fields) { - base::test::ScopedFeatureList feature_list; - InitFeature(&feature_list, kAutofillEnforceMinRequiredFieldsForQuery, - enforce_min_fields); - FormStructure form_structure(form); - return form_structure.ShouldBeQueried(); - } - - bool FormShouldBeUploaded(const FormData& form, bool enforce_min_fields) { - base::test::ScopedFeatureList feature_list; - InitFeature(&feature_list, kAutofillEnforceMinRequiredFieldsForUpload, - enforce_min_fields); - FormStructure form_structure(form); - return form_structure.ShouldBeUploaded(); + bool FormShouldBeQueried(const FormData& form) { + return FormStructure(form).ShouldBeQueried(); } void DisableAutofillMetadataFieldTrial() { @@ -175,6 +122,13 @@ class FormStructureTestImpl : public test::FormStructureTest { {}); } + FieldRendererId MakeFieldRendererId() { + return FieldRendererId(++id_counter_); + } + + protected: + TestPatternProvider test_pattern_provider_; + private: void EnableAutofillMetadataFieldTrial() { scoped_feature_list_.Reset(); @@ -184,6 +138,7 @@ class FormStructureTestImpl : public test::FormStructureTest { field_trial_->group(); } + uint32_t id_counter_ = 10; base::test::ScopedFeatureList scoped_feature_list_; scoped_refptr<base::FieldTrial> field_trial_; }; @@ -277,62 +232,60 @@ TEST_F(FormStructureTestImpl, IsAutofillable) { field.label = ASCIIToUTF16("username"); field.name = ASCIIToUTF16("username"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // With min required fields enabled. - EXPECT_FALSE(FormIsAutofillable(form, true)); // Min enforced. - EXPECT_FALSE(FormIsAutofillable(form, false)); // Min not enforced. + EXPECT_FALSE(FormIsAutofillable(form)); // Add a password field. The form should be picked up by the password but // not by autofill. field.label = ASCIIToUTF16("password"); field.name = ASCIIToUTF16("password"); field.form_control_type = "password"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - EXPECT_FALSE(FormIsAutofillable(form, true)); // Min enforced. - EXPECT_FALSE(FormIsAutofillable(form, false)); // Min not enforced. + EXPECT_FALSE(FormIsAutofillable(form)); // Add an auto-fillable fields. With just one auto-fillable field, this should // be picked up by autofill only if there is no minimum field enforcement. field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullname"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - EXPECT_FALSE(FormIsAutofillable(form, true)); // Min enforced. - EXPECT_TRUE(FormIsAutofillable(form, false)); // Min not enforced. + EXPECT_FALSE(FormIsAutofillable(form)); // Add an auto-fillable fields. With just one auto-fillable field, this should // be picked up by autofill only if there is no minimum field enforcement. field.label = ASCIIToUTF16("Address Line 1"); field.name = ASCIIToUTF16("address1"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - EXPECT_FALSE(FormIsAutofillable(form, true)); // Min enforced. - EXPECT_TRUE(FormIsAutofillable(form, false)); // Min not enforced. + EXPECT_FALSE(FormIsAutofillable(form)); // We now have three auto-fillable fields. It's always autofillable. field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); field.form_control_type = "email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - EXPECT_TRUE(FormIsAutofillable(form, true)); // Min enforced. - EXPECT_TRUE(FormIsAutofillable(form, false)); // Min not enforced. + EXPECT_TRUE(FormIsAutofillable(form)); // The target cannot include http(s)://*/search... form.action = GURL("http://google.com/search?q=hello"); - EXPECT_FALSE(FormIsAutofillable(form, true)); // Min enforced. - EXPECT_FALSE(FormIsAutofillable(form, false)); // Min not enforced. + EXPECT_FALSE(FormIsAutofillable(form)); // But search can be in the URL. form.action = GURL("http://search.com/?q=hello"); - EXPECT_TRUE(FormIsAutofillable(form, true)); // Min enforced. - EXPECT_TRUE(FormIsAutofillable(form, false)); // Min not enforced. + EXPECT_TRUE(FormIsAutofillable(form)); } TEST_F(FormStructureTestImpl, ShouldBeParsed) { @@ -345,51 +298,56 @@ TEST_F(FormStructureTestImpl, ShouldBeParsed) { FormFieldData::CheckStatus::kCheckableButUnchecked; checkable_field.name = ASCIIToUTF16("radiobtn"); checkable_field.form_control_type = "radio"; + checkable_field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(checkable_field); // A form with a single checkable field isn't interesting. - CheckFormShouldBeParsed("one checkable", form, false, false); + EXPECT_FALSE(FormShouldBeParsed(form)) << "one checkable"; // Add a second checkable field. checkable_field.name = ASCIIToUTF16("checkbox"); checkable_field.form_control_type = "checkbox"; + checkable_field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(checkable_field); // A form with a only checkable fields isn't interesting. - CheckFormShouldBeParsed("two checkable", form, false, false); + EXPECT_FALSE(FormShouldBeParsed(form)) << "two checkable"; // Add a text field. 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); // Single text field forms shouldn't be parsed if all of the minimums are // enforced but should be parsed if ANY of the minimums is not enforced. - CheckFormShouldBeParsed("username", form, false, true); + EXPECT_TRUE(FormShouldBeParsed(form)) << "username"; // We now have three text fields, though only two are auto-fillable. field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstname"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Three text field forms should always be parsed. - CheckFormShouldBeParsed("three field", form, true, true); + EXPECT_TRUE(FormShouldBeParsed(form)) << "three field"; // The target cannot include http(s)://*/search... form.action = GURL("http://google.com/search?q=hello"); - CheckFormShouldBeParsed("search path", form, false, false); + EXPECT_FALSE(FormShouldBeParsed(form)) << "search path"; // But search can be in the URL. form.action = GURL("http://search.com/?q=hello"); - CheckFormShouldBeParsed("search domain", form, true, true); + EXPECT_TRUE(FormShouldBeParsed(form)) << "search domain"; // The form need only have three fields, but at least one must be a text // field. @@ -398,38 +356,43 @@ TEST_F(FormStructureTestImpl, ShouldBeParsed) { field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); field.form_control_type = "email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); field.form_control_type = "select-one"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); field.form_control_type = "select-one"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - CheckFormShouldBeParsed("text + selects", form, true, true); + EXPECT_TRUE(FormShouldBeParsed(form)) << "text + selects"; // Now, no text fields. form.fields[0].form_control_type = "select-one"; - CheckFormShouldBeParsed("only selects", form, false, false); + EXPECT_FALSE(FormShouldBeParsed(form)) << "only selects"; // We have only one field, which is password. form.fields.clear(); field.label = ASCIIToUTF16("Password"); field.name = ASCIIToUTF16("pw"); field.form_control_type = "password"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - CheckFormShouldBeParsed("password", form, false, true); + EXPECT_TRUE(FormShouldBeParsed(form)) << "password"; // We have two fields, which are passwords, should be parsed. field.label = ASCIIToUTF16("New password"); field.name = ASCIIToUTF16("new_pw"); field.form_control_type = "password"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - CheckFormShouldBeParsed("new password", form, true, true); + EXPECT_TRUE(FormShouldBeParsed(form)) << "new password"; } TEST_F(FormStructureTestImpl, ShouldBeParsed_BadScheme) { @@ -441,18 +404,21 @@ TEST_F(FormStructureTestImpl, ShouldBeParsed_BadScheme) { field.name = ASCIIToUTF16("name"); field.form_control_type = "text"; field.autocomplete_attribute = "name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); field.form_control_type = "text"; field.autocomplete_attribute = "email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.form_control_type = "text"; field.autocomplete_attribute = "address-line1"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Baseline, HTTP should work. @@ -522,12 +488,14 @@ TEST_F(FormStructureTestImpl, ShouldBeParsed_TwoFields_HasAutocomplete) { field.name = ASCIIToUTF16("name"); field.form_control_type = "name"; field.autocomplete_attribute = "name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("Address"); field.form_control_type = "select-one"; field.autocomplete_attribute = ""; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -1115,39 +1083,20 @@ TEST_F(FormStructureTestImpl, FormFieldData field; field.form_control_type = "text"; + field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - EXPECT_FALSE(FormShouldRunHeuristics(form, true)); // Min enforced. - EXPECT_TRUE(FormShouldRunHeuristics(form, false)); // Min not enforced. + EXPECT_FALSE(FormShouldRunHeuristics(form)); - EXPECT_FALSE(FormShouldBeQueried(form, true)); // Min enforced. - EXPECT_TRUE(FormShouldBeQueried(form, false)); // Min not enforced. - - // Status Quo (Q3/2017) - Small forms not supported. - { - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures( - // Enabled. - {kAutofillEnforceMinRequiredFieldsForHeuristics, - kAutofillEnforceMinRequiredFieldsForQuery, - kAutofillEnforceMinRequiredFieldsForUpload}, - // Disabled. - {}); - FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); - ASSERT_EQ(2U, form_structure.field_count()); - ASSERT_EQ(0U, form_structure.autofill_count()); - EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type()); - EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(1)->heuristic_type()); - EXPECT_EQ(NO_SERVER_DATA, form_structure.field(0)->server_type()); - EXPECT_EQ(NO_SERVER_DATA, form_structure.field(1)->server_type()); - EXPECT_FALSE(form_structure.IsAutofillable()); - } + EXPECT_TRUE(FormShouldBeQueried(form)); // Default configuration. { @@ -1161,22 +1110,6 @@ TEST_F(FormStructureTestImpl, EXPECT_EQ(NO_SERVER_DATA, form_structure.field(1)->server_type()); EXPECT_FALSE(form_structure.IsAutofillable()); } - - // Enable small form heuristics. - { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); - ASSERT_EQ(2U, form_structure.field_count()); - ASSERT_EQ(2U, form_structure.autofill_count()); - EXPECT_EQ(NAME_FIRST, form_structure.field(0)->heuristic_type()); - EXPECT_EQ(NAME_LAST, form_structure.field(1)->heuristic_type()); - EXPECT_EQ(NO_SERVER_DATA, form_structure.field(0)->server_type()); - EXPECT_EQ(NO_SERVER_DATA, form_structure.field(1)->server_type()); - EXPECT_TRUE(form_structure.IsAutofillable()); - } } // Tests the heuristics and server predictions are not run for forms with less @@ -1194,65 +1127,23 @@ TEST_F(FormStructureTestImpl, field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstname"); field.autocomplete_attribute = "given-name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); field.autocomplete_attribute = ""; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - EXPECT_FALSE(FormShouldRunHeuristics(form, true)); // Min enforced. - EXPECT_TRUE(FormShouldRunHeuristics(form, false)); // Min not enforced. + EXPECT_FALSE(FormShouldRunHeuristics(form)); - EXPECT_FALSE(FormShouldBeQueried(form, true)); // Min enforced. - EXPECT_TRUE(FormShouldBeQueried(form, false)); // Min not enforced. - - // Status Quo (Q3/2017) - Small forms not supported. - { - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures( - // Enabled. - {kAutofillEnforceMinRequiredFieldsForHeuristics, - kAutofillEnforceMinRequiredFieldsForQuery, - kAutofillEnforceMinRequiredFieldsForUpload}, - // Disabled. - {}); - FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); - ASSERT_EQ(2U, form_structure.field_count()); - ASSERT_EQ(1U, form_structure.autofill_count()); - EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type()); - EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(1)->heuristic_type()); - EXPECT_EQ(NO_SERVER_DATA, form_structure.field(0)->server_type()); - EXPECT_EQ(NO_SERVER_DATA, form_structure.field(1)->server_type()); - EXPECT_FALSE(form_structure.IsAutofillable()); - } - - // Enable small form heuristics. - { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature( - kAutofillEnforceMinRequiredFieldsForHeuristics); - FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); - ASSERT_EQ(2U, form_structure.field_count()); - ASSERT_EQ(2U, form_structure.autofill_count()); - EXPECT_EQ(NAME_FIRST, form_structure.field(0)->heuristic_type()); - EXPECT_EQ(NAME_LAST, form_structure.field(1)->heuristic_type()); - EXPECT_EQ(NO_SERVER_DATA, form_structure.field(0)->server_type()); - EXPECT_EQ(NO_SERVER_DATA, form_structure.field(1)->server_type()); - EXPECT_EQ(NAME_FIRST, form_structure.field(0)->Type().GetStorableType()); - EXPECT_EQ(NAME_LAST, form_structure.field(1)->Type().GetStorableType()); - EXPECT_TRUE(form_structure.IsAutofillable()); - } + EXPECT_TRUE(FormShouldBeQueried(form)); // As a side effect of parsing small forms (if any of the heuristics, query, // or upload minimmums are disabled, we'll autofill fields with an // autocomplete attribute, even if its the only field in the form. { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature( - kAutofillEnforceMinRequiredFieldsForUpload); FormData form_copy = form; form_copy.fields.pop_back(); FormStructure form_structure(form_copy); @@ -1280,20 +1171,24 @@ TEST_F(FormStructureTestImpl, PasswordFormShouldBeQueried) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); field.autocomplete_attribute = "username"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Password"); field.name = ASCIIToUTF16("Password"); field.form_control_type = "password"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -1315,28 +1210,37 @@ TEST_F(FormStructureTestImpl, HeuristicsAutocompleteAttributeWithSections) { // Some fields will have no section specified. These fall into the default // section. field.autocomplete_attribute = "email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // We allow arbitrary section names. field.autocomplete_attribute = "section-foo email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // "shipping" and "billing" are special section tokens that don't require the // "section-" prefix. field.autocomplete_attribute = "shipping email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.autocomplete_attribute = "billing email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // "shipping" and "billing" can be combined with other section names. field.autocomplete_attribute = "section-foo shipping email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.autocomplete_attribute = "section-foo billing email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // We don't do anything clever to try to coalesce sections; it's up to site // authors to avoid typos. field.autocomplete_attribute = "section--foo email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // "shipping email" and "section--shipping" email should be parsed as @@ -1344,10 +1248,12 @@ TEST_F(FormStructureTestImpl, HeuristicsAutocompleteAttributeWithSections) { // implement implicit section names from attributes like "shipping email"; see // the implementation for more details. field.autocomplete_attribute = "section--shipping email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Credit card fields are implicitly in a separate section from other fields. field.autocomplete_attribute = "section-foo cc-number"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -1380,20 +1286,29 @@ TEST_F(FormStructureTestImpl, // Some fields will have no section specified. These fall into the default // section. field.autocomplete_attribute = "email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Specifying "section-" is equivalent to not specifying a section. field.autocomplete_attribute = "section- email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Invalid tokens should prevent us from setting a section name. field.autocomplete_attribute = "garbage section-foo email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.autocomplete_attribute = "garbage section-bar email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.autocomplete_attribute = "garbage shipping email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.autocomplete_attribute = "garbage billing email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -1423,8 +1338,11 @@ TEST_F(FormStructureTestImpl, field.form_control_type = "text"; field.autocomplete_attribute = "section-foo email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.autocomplete_attribute = "section-foo address-line1"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -1455,15 +1373,22 @@ TEST_F(FormStructureTestImpl, field.name = ASCIIToUTF16("one"); field.autocomplete_attribute = "address-line1"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.name = base::string16(); field.autocomplete_attribute = "section-foo email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.name = base::string16(); field.autocomplete_attribute = "name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.name = ASCIIToUTF16("two"); field.autocomplete_attribute = "address-line1"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -1491,43 +1416,53 @@ TEST_F(FormStructureTestImpl, HeuristicsSample8) { field.label = ASCIIToUTF16("Your First Name:"); field.name = ASCIIToUTF16("bill.first"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Your Last Name:"); field.name = ASCIIToUTF16("bill.last"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Street Address Line 1:"); field.name = ASCIIToUTF16("bill.street1"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Street Address Line 2:"); field.name = ASCIIToUTF16("bill.street2"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("bill.city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State (U.S.):"); field.name = ASCIIToUTF16("bill.state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Zip/Postal Code:"); field.name = ASCIIToUTF16("BillTo.PostalCode"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country:"); field.name = ASCIIToUTF16("bill.country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone Number:"); field.name = ASCIIToUTF16("BillTo.Phone"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = base::string16(); field.name = ASCIIToUTF16("Submit"); field.form_control_type = "submit"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -1569,32 +1504,39 @@ TEST_F(FormStructureTestImpl, HeuristicsSample6) { field.label = ASCIIToUTF16("E-mail address"); field.name = ASCIIToUTF16("email"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full name"); field.name = ASCIIToUTF16("name"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Company"); field.name = ASCIIToUTF16("company"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Zip Code"); field.name = ASCIIToUTF16("Home.PostalCode"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = base::string16(); field.name = ASCIIToUTF16("Submit"); field.value = ASCIIToUTF16("continue"); field.form_control_type = "submit"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -1632,35 +1574,43 @@ TEST_F(FormStructureTestImpl, HeuristicsLabelsOnly) { field.label = ASCIIToUTF16("First Name"); field.name = base::string16(); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); field.name = base::string16(); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Email"); field.name = base::string16(); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); field.name = base::string16(); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = base::string16(); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = base::string16(); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Zip code"); field.name = base::string16(); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = base::string16(); field.name = ASCIIToUTF16("Submit"); field.form_control_type = "submit"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -1698,27 +1648,33 @@ TEST_F(FormStructureTestImpl, HeuristicsCreditCardInfo) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Exp Month"); field.name = ASCIIToUTF16("ccmonth"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Exp Year"); field.name = ASCIIToUTF16("ccyear"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Verification"); field.name = ASCIIToUTF16("verification"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = base::string16(); field.name = ASCIIToUTF16("Submit"); field.form_control_type = "submit"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -1753,33 +1709,40 @@ TEST_F(FormStructureTestImpl, HeuristicsCreditCardInfoWithUnknownCardField) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // This is not a field we know how to process. But we should skip over it // and process the other fields in the card block. field.label = ASCIIToUTF16("Card image"); field.name = ASCIIToUTF16("card_image"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Exp Month"); field.name = ASCIIToUTF16("ccmonth"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Exp Year"); field.name = ASCIIToUTF16("ccyear"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Verification"); field.name = ASCIIToUTF16("verification"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = base::string16(); field.name = ASCIIToUTF16("Submit"); field.form_control_type = "submit"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -1816,18 +1779,22 @@ TEST_F(FormStructureTestImpl, ThreeAddressLines) { field.label = ASCIIToUTF16("Address Line1"); field.name = ASCIIToUTF16("Address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address Line2"); field.name = ASCIIToUTF16("Address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address Line3"); field.name = ASCIIToUTF16("Address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -1857,18 +1824,22 @@ TEST_F(FormStructureTestImpl, SurplusAddressLinesIgnored) { field.label = ASCIIToUTF16("Address Line1"); field.name = ASCIIToUTF16("shipping.address.addressLine1"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address Line2"); field.name = ASCIIToUTF16("shipping.address.addressLine2"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address Line3"); field.name = ASCIIToUTF16("billing.address.addressLine3"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address Line4"); field.name = ASCIIToUTF16("billing.address.addressLine4"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -1901,18 +1872,22 @@ TEST_F(FormStructureTestImpl, ThreeAddressLinesExpedia) { field.label = ASCIIToUTF16("Street:"); field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_ads1"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Suite or Apt:"); field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_adap"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Street address second line"); field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_ads2"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City:"); field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_adct"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -1944,14 +1919,17 @@ TEST_F(FormStructureTestImpl, TwoAddressLinesEbay) { field.label = ASCIIToUTF16("Address Line1"); field.name = ASCIIToUTF16("address1"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Floor number, suite number, etc"); field.name = ASCIIToUTF16("address2"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City:"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -1978,14 +1956,17 @@ TEST_F(FormStructureTestImpl, HeuristicsStateWithProvince) { field.label = ASCIIToUTF16("Address Line1"); field.name = ASCIIToUTF16("Address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address Line2"); field.name = ASCIIToUTF16("Address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State/Province/Region"); field.name = ASCIIToUTF16("State"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -2013,46 +1994,57 @@ TEST_F(FormStructureTestImpl, HeuristicsWithBilling) { field.label = ASCIIToUTF16("First Name*:"); field.name = ASCIIToUTF16("editBillingAddress$firstNameBox"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name*:"); field.name = ASCIIToUTF16("editBillingAddress$lastNameBox"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Company Name:"); field.name = ASCIIToUTF16("editBillingAddress$companyBox"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address*:"); field.name = ASCIIToUTF16("editBillingAddress$addressLine1Box"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Apt/Suite :"); field.name = ASCIIToUTF16("editBillingAddress$addressLine2Box"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City*:"); field.name = ASCIIToUTF16("editBillingAddress$cityBox"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State/Province*:"); field.name = ASCIIToUTF16("editBillingAddress$stateDropDown"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country*:"); field.name = ASCIIToUTF16("editBillingAddress$countryDropDown"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Postal Code*:"); field.name = ASCIIToUTF16("editBillingAddress$zipCodeBox"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone*:"); field.name = ASCIIToUTF16("editBillingAddress$phoneBox"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Email Address*:"); field.name = ASCIIToUTF16("email$emailBox"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -2086,11 +2078,13 @@ TEST_F(FormStructureTestImpl, ThreePartPhoneNumber) { field.label = ASCIIToUTF16("Phone:"); field.name = ASCIIToUTF16("dayphone1"); field.max_length = 0; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("-"); field.name = ASCIIToUTF16("dayphone2"); field.max_length = 3; // Size of prefix is 3. + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("-"); @@ -2098,11 +2092,13 @@ TEST_F(FormStructureTestImpl, ThreePartPhoneNumber) { field.max_length = 4; // Size of suffix is 4. If unlimited size is // passed, phone will be parsed as // <country code> - <area code> - <phone>. + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("ext.:"); field.name = ASCIIToUTF16("dayphone4"); field.max_length = 0; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -2131,22 +2127,27 @@ TEST_F(FormStructureTestImpl, HeuristicsInfernoCC) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("billing_address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Expiration Date"); field.name = ASCIIToUTF16("expiration_month"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Expiration Year"); field.name = ASCIIToUTF16("expiration_year"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -2182,26 +2183,32 @@ TEST_F(FormStructureTestImpl, HeuristicsInferCCNames_NamesNotFirst) { field.label = ASCIIToUTF16("Card number"); field.name = ASCIIToUTF16("ccnumber"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("First name"); field.name = ASCIIToUTF16("first_name"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last name"); field.name = ASCIIToUTF16("last_name"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Expiration date"); field.name = ASCIIToUTF16("ccexpiresmonth"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = base::string16(); field.name = ASCIIToUTF16("ccexpiresyear"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("cvc number"); field.name = ASCIIToUTF16("csc"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -2241,26 +2248,32 @@ TEST_F(FormStructureTestImpl, HeuristicsInferCCNames_NamesFirst) { field.label = ASCIIToUTF16("Cardholder Name"); field.name = ASCIIToUTF16("cc_first_name"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last name"); field.name = ASCIIToUTF16("last_name"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Card number"); field.name = ASCIIToUTF16("ccnumber"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Expiration date"); field.name = ASCIIToUTF16("ccexpiresmonth"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = base::string16(); field.name = ASCIIToUTF16("ccexpiresyear"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("cvc number"); field.name = ASCIIToUTF16("csc"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -2296,22 +2309,27 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("billing_address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Expiration Date"); field.name = ASCIIToUTF16("expiration_month"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Expiration Year"); field.name = ASCIIToUTF16("expiration_year"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Add checkable field. @@ -2320,7 +2338,9 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest) { FormFieldData::CheckStatus::kCheckableButUnchecked; checkable_field.label = ASCIIToUTF16("Checkable1"); checkable_field.name = ASCIIToUTF16("Checkable1"); + checkable_field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(checkable_field); + FormStructure form_structure(form); std::vector<FormStructure*> forms; @@ -2331,7 +2351,7 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest) { // Prepare the expected proto string. AutofillPageQueryRequest query; - query.set_client_version("6.1.1715.1442/en (GGLL)"); + query.set_client_version(GetProductNameAndVersionForUserAgent()); AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); @@ -2379,6 +2399,7 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest) { for (size_t i = 0; i < 5; ++i) { field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); } @@ -2441,6 +2462,7 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest) { for (size_t i = 0; i < 300; ++i) { field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); malformed_form.fields.push_back(field); } @@ -2568,6 +2590,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}, {AutofillProfile::UNVALIDATED}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); @@ -2575,6 +2598,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}, {AutofillProfile::UNVALIDATED}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Email"); @@ -2583,6 +2607,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}, {AutofillProfile::INVALID}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); @@ -2591,6 +2616,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {PHONE_HOME_WHOLE_NUMBER}, {AutofillProfile::EMPTY}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); @@ -2599,6 +2625,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {ADDRESS_HOME_COUNTRY}, {AutofillProfile::VALID}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Add checkable field. @@ -2610,6 +2637,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {ADDRESS_HOME_COUNTRY}, {AutofillProfile::VALID}); + checkable_field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(checkable_field); form_structure = std::make_unique<FormStructure>(form); @@ -2640,7 +2668,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(false); upload.set_data_present("144200030e"); @@ -2699,6 +2727,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, @@ -2767,6 +2796,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}, {AutofillProfile::UNVALIDATED}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); @@ -2774,6 +2804,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}, {AutofillProfile::UNVALIDATED}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Email"); @@ -2782,6 +2813,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}, {AutofillProfile::INVALID}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); @@ -2790,6 +2822,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {PHONE_HOME_WHOLE_NUMBER}, {AutofillProfile::EMPTY}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); @@ -2798,6 +2831,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {ADDRESS_HOME_COUNTRY}, {AutofillProfile::VALID}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Add checkable field. @@ -2809,6 +2843,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {ADDRESS_HOME_COUNTRY}, {AutofillProfile::VALID}); + checkable_field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(checkable_field); form_structure = std::make_unique<FormStructure>(form); @@ -2839,7 +2874,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(false); upload.set_data_present("144200030e"); @@ -2899,6 +2934,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}, {AutofillProfile::UNVALIDATED, AutofillProfile::VALID}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); @@ -2906,6 +2942,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}, {AutofillProfile::UNVALIDATED, AutofillProfile::VALID}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Email"); @@ -2914,6 +2951,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}, {AutofillProfile::INVALID, AutofillProfile::VALID}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); @@ -2923,6 +2961,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { possible_field_types, possible_field_types_validities, {PHONE_HOME_WHOLE_NUMBER}, {AutofillProfile::EMPTY, AutofillProfile::VALID}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); @@ -2931,6 +2970,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {ADDRESS_HOME_COUNTRY}, {AutofillProfile::VALID, AutofillProfile::VALID}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Add checkable field. @@ -2942,6 +2982,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {ADDRESS_HOME_COUNTRY}, {AutofillProfile::VALID, AutofillProfile::VALID}); + checkable_field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(checkable_field); form_structure = std::make_unique<FormStructure>(form); @@ -2972,7 +3013,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(false); upload.set_data_present("144200030e"); @@ -3030,12 +3071,14 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { field.name = ASCIIToUTF16("firstname"); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Email"); @@ -3043,6 +3086,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { field.form_control_type = "email"; test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); @@ -3051,6 +3095,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { test::InitializePossibleTypesAndValidities(possible_field_types, possible_field_types_validities, {PHONE_HOME_WHOLE_NUMBER}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); @@ -3059,6 +3104,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { test::InitializePossibleTypesAndValidities(possible_field_types, possible_field_types_validities, {ADDRESS_HOME_COUNTRY}); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Add checkable field. @@ -3070,6 +3116,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { test::InitializePossibleTypesAndValidities(possible_field_types, possible_field_types_validities, {ADDRESS_HOME_COUNTRY}); + checkable_field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(checkable_field); form_structure = std::make_unique<FormStructure>(form); @@ -3106,7 +3153,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { AutofillUploadContents upload; upload.set_submission(true); upload.set_submission_event(AutofillUploadContents::HTML_FORM_SUBMISSION); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(false); upload.set_data_present("144200030e"); @@ -3159,6 +3206,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, @@ -3216,6 +3264,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, @@ -3254,35 +3303,45 @@ TEST_F(FormStructureTestImpl, field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstname"); field.autocomplete_attribute = "given-name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); + field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); field.autocomplete_attribute = "family-name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); + field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); field.form_control_type = "email"; field.autocomplete_attribute = "email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); + field.label = ASCIIToUTF16("username"); field.name = ASCIIToUTF16("username"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {USERNAME}); + field.label = ASCIIToUTF16("password"); field.name = ASCIIToUTF16("password"); field.form_control_type = "password"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities(possible_field_types, possible_field_types_validities, {ACCOUNT_CREATION_PASSWORD}); + form_structure = std::make_unique<FormStructure>(form); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -3316,7 +3375,7 @@ TEST_F(FormStructureTestImpl, // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(true); upload.set_data_present("1440000000000000000802"); @@ -3383,22 +3442,28 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithAutocomplete) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstname"); field.autocomplete_attribute = "given-name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); + field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); field.autocomplete_attribute = "family-name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); + field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); field.form_control_type = "email"; field.autocomplete_attribute = "email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); + form_structure = std::make_unique<FormStructure>(form); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -3419,7 +3484,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithAutocomplete) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(true); upload.set_data_present("1440"); @@ -3473,9 +3538,11 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestWithPropertiesMask) { field.autocomplete_attribute = "given-name"; field.css_classes = ASCIIToUTF16("class1 class2"); field.properties_mask = FieldPropertiesFlags::kHadFocus; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); + field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); field.name_attribute = field.name; @@ -3484,9 +3551,11 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestWithPropertiesMask) { field.css_classes = ASCIIToUTF16("class1 class2"); field.properties_mask = FieldPropertiesFlags::kHadFocus | FieldPropertiesFlags::kUserTyped; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); + field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); field.name_attribute = field.name; @@ -3496,9 +3565,11 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestWithPropertiesMask) { field.css_classes = ASCIIToUTF16("class1 class2"); field.properties_mask = FieldPropertiesFlags::kHadFocus | FieldPropertiesFlags::kUserTyped; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); + form_structure = std::make_unique<FormStructure>(form); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -3519,7 +3590,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestWithPropertiesMask) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(true); upload.set_data_present("1440"); @@ -3571,22 +3642,28 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_ObservedSubmissionFalse) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstname"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); + field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); + field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); field.name_attribute = field.name; field.form_control_type = "email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); + form_structure = std::make_unique<FormStructure>(form); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -3607,7 +3684,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_ObservedSubmissionFalse) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(false); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(true); upload.set_data_present("1440"); @@ -3653,17 +3730,23 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithLabels) { field.form_control_type = "text"; // No label for the first field. + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); + field.label = ASCIIToUTF16("Last Name"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); + field.label = ASCIIToUTF16("Email"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); + form_structure = std::make_unique<FormStructure>(form); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -3684,7 +3767,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithLabels) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(true); upload.set_data_present("1440"); @@ -3725,19 +3808,25 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithCssClassesAndIds) { FormFieldData field; field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); + field.css_classes = ASCIIToUTF16("last_name_field"); field.id_attribute = ASCIIToUTF16("lastname_id"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); + field.css_classes = ASCIIToUTF16("email_field required_field"); field.id_attribute = ASCIIToUTF16("email_id"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); + std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -3758,7 +3847,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithCssClassesAndIds) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(true); upload.set_data_present("1440"); @@ -3814,12 +3903,17 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithFormName) { FormFieldData field; field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); + + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); + + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); @@ -3845,7 +3939,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithFormName) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(true); upload.set_data_present("1440"); @@ -3894,22 +3988,28 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestPartialMetadata) { // Some fields don't have "name" or "autocomplete" attributes, and some have // neither. // No label. + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); + field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); field.name_attribute = field.name; field.autocomplete_attribute = "family-name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); + field.label = ASCIIToUTF16("Email"); field.form_control_type = "email"; field.autocomplete_attribute = "email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); + form_structure = std::make_unique<FormStructure>(form); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -3930,7 +4030,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestPartialMetadata) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(true); upload.set_data_present("1440"); @@ -3984,18 +4084,22 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_DisabledMetadataTrial) { field.id_attribute = ASCIIToUTF16("first_name"); field.autocomplete_attribute = "given-name"; field.css_classes = ASCIIToUTF16("class1 class2"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); + field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); field.name_attribute = field.name; field.id_attribute = ASCIIToUTF16("last_name"); field.autocomplete_attribute = "family-name"; field.css_classes = ASCIIToUTF16("class1 class2"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); + field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); field.name_attribute = field.name; @@ -4003,9 +4107,11 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_DisabledMetadataTrial) { field.form_control_type = "email"; field.autocomplete_attribute = "email"; field.css_classes = ASCIIToUTF16("class1 class2"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); + form_structure = std::make_unique<FormStructure>(form); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -4026,7 +4132,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_DisabledMetadataTrial) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(true); upload.set_data_present("1440"); @@ -4069,16 +4175,19 @@ TEST_F(FormStructureTestImpl, CheckDataPresence) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("first"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("last"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -4103,7 +4212,7 @@ TEST_F(FormStructureTestImpl, CheckDataPresence) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure.form_signature().value()); upload.set_autofill_used(false); upload.set_data_present(""); @@ -4355,6 +4464,7 @@ TEST_F(FormStructureTestImpl, CheckMultipleTypes) { field.label = ASCIIToUTF16("email"); field.name = ASCIIToUTF16("email"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); @@ -4362,6 +4472,7 @@ TEST_F(FormStructureTestImpl, CheckMultipleTypes) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("first"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_FIRST}); @@ -4369,6 +4480,7 @@ TEST_F(FormStructureTestImpl, CheckMultipleTypes) { field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("last"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, {NAME_LAST}); @@ -4376,6 +4488,7 @@ TEST_F(FormStructureTestImpl, CheckMultipleTypes) { field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); test::InitializePossibleTypesAndValidities(possible_field_types, possible_field_types_validities, @@ -4392,7 +4505,7 @@ TEST_F(FormStructureTestImpl, CheckMultipleTypes) { // Prepare the expected proto string. AutofillUploadContents upload; upload.set_submission(true); - upload.set_client_version("6.1.1715.1442/en (GGLL)"); + upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(false); upload.set_data_present("1440000360000008"); @@ -4500,14 +4613,17 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_PasswordsRevealed) { FormFieldData field; field.name = ASCIIToUTF16("email"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.name = ASCIIToUTF16("first"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.name = ASCIIToUTF16("last"); field.name_attribute = field.name; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -4529,6 +4645,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_IsFormTag) { form.url = GURL("http://www.foo.com/"); FormFieldData field; field.name = ASCIIToUTF16("email"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form.is_form_tag = is_form_tag; @@ -4581,6 +4698,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_RichMetadata) { field.aria_label = ASCIIToUTF16(f.aria_label); field.aria_description = ASCIIToUTF16(f.aria_description); field.css_classes = ASCIIToUTF16(f.css_classes); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); } RandomizedEncoder encoder("seed for testing", @@ -4722,6 +4840,7 @@ TEST_F(FormStructureTestImpl, Metadata_OnlySendFullUrlWithUserConsent) { field.form_control_type = "text"; field.label = ASCIIToUTF16("email"); field.name = ASCIIToUTF16("email"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); TestingPrefServiceSimple prefs; @@ -4755,10 +4874,12 @@ TEST_F(FormStructureTestImpl, CheckFormSignature) { field.label = ASCIIToUTF16("email"); field.name = ASCIIToUTF16("email"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("first"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Checkable fields shouldn't affect the signature. @@ -4766,6 +4887,7 @@ TEST_F(FormStructureTestImpl, CheckFormSignature) { field.name = ASCIIToUTF16("Select"); field.form_control_type = "checkbox"; field.check_status = FormFieldData::CheckStatus::kCheckableButUnchecked; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); form_structure = std::make_unique<FormStructure>(form); @@ -4796,16 +4918,24 @@ TEST_F(FormStructureTestImpl, CheckFormSignature) { field.label = ASCIIToUTF16("Random Field label"); field.name = ASCIIToUTF16("random1234"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.label = ASCIIToUTF16("Random Field label2"); field.name = ASCIIToUTF16("random12345"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.label = ASCIIToUTF16("Random Field label3"); field.name = ASCIIToUTF16("1ran12dom12345678"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.label = ASCIIToUTF16("Random Field label3"); field.name = ASCIIToUTF16("12345ran123456dom123"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + form_structure = std::make_unique<FormStructure>(form); EXPECT_EQ(FormStructureTestImpl::Hash64Bit( std::string("https://login.facebook.com&login_form&email&first&" @@ -4823,16 +4953,19 @@ TEST_F(FormStructureTestImpl, ToFormData) { 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 = ASCIIToUTF16("password"); field.name = ASCIIToUTF16("password"); field.form_control_type = "password"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = base::string16(); field.name = ASCIIToUTF16("Submit"); field.form_control_type = "submit"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); EXPECT_TRUE(form.SameFormAs(FormStructure(form).ToFormData())); @@ -4848,18 +4981,21 @@ TEST_F(FormStructureTestImpl, SkipFieldTest) { 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 = ASCIIToUTF16("select"); field.name = ASCIIToUTF16("select"); field.form_control_type = "checkbox"; field.check_status = FormFieldData::CheckStatus::kCheckableButUnchecked; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = base::string16(); field.name = ASCIIToUTF16("email"); 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); @@ -4870,7 +5006,7 @@ TEST_F(FormStructureTestImpl, SkipFieldTest) { // Create the expected query and serialize it to a string. AutofillPageQueryRequest query; - query.set_client_version("6.1.1715.1442/en (GGLL)"); + query.set_client_version(GetProductNameAndVersionForUserAgent()); AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); @@ -4903,16 +5039,19 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_WithLabels) { // No label on the first field. field.name = ASCIIToUTF16("username"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Enter your Email address"); field.name = ASCIIToUTF16("email"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Enter your Password"); field.name = ASCIIToUTF16("password"); field.form_control_type = "password"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); std::vector<FormStructure*> forms; @@ -4923,7 +5062,7 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_WithLabels) { // Create the expected query and serialize it to a string. AutofillPageQueryRequest query; - query.set_client_version("6.1.1715.1442/en (GGLL)"); + query.set_client_version(GetProductNameAndVersionForUserAgent()); AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); @@ -4954,6 +5093,7 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_WithLongLabels) { // No label on the first field. field.name = ASCIIToUTF16("username"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // This label will be truncated in the XML request. @@ -4964,11 +5104,13 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_WithLongLabels) { "Exceeding A Certain Number Of Characters..."); field.name = ASCIIToUTF16("email"); field.form_control_type = "text"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Enter your Password"); field.name = ASCIIToUTF16("password"); field.form_control_type = "password"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -4979,7 +5121,7 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_WithLongLabels) { // Create the expected query and serialize it to a string. AutofillPageQueryRequest query; - query.set_client_version("6.1.1715.1442/en (GGLL)"); + query.set_client_version(GetProductNameAndVersionForUserAgent()); AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); @@ -5011,6 +5153,7 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_MissingNames) { 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(); @@ -5018,6 +5161,7 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_MissingNames) { field.name = ASCIIToUTF16(""); 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); @@ -5029,7 +5173,7 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_MissingNames) { // Create the expected query and serialize it to a string. AutofillPageQueryRequest query; - query.set_client_version("6.1.1715.1442/en (GGLL)"); + query.set_client_version(GetProductNameAndVersionForUserAgent()); AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); @@ -5065,12 +5209,14 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_DisabledMetadataTrial) { 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); @@ -5081,7 +5227,7 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_DisabledMetadataTrial) { // Create the expected query and serialize it to a string. AutofillPageQueryRequest query; - query.set_client_version("6.1.1715.1442/en (GGLL)"); + query.set_client_version(GetProductNameAndVersionForUserAgent()); AutofillPageQueryRequest::Form* query_form = query.add_forms(); query_form->set_signature(form_structure.form_signature().value()); @@ -5115,7 +5261,9 @@ TEST_F(FormStructureTestImpl, PossibleValues) { field.option_values.push_back(ASCIIToUTF16("")); field.option_contents.push_back(ASCIIToUTF16("Germany")); field.option_values.push_back(ASCIIToUTF16("GRMNY")); + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(field); + FormStructure form_structure(form_data); form_structure.ParseFieldTypesFromAutocompleteAttributes(); @@ -5138,7 +5286,9 @@ TEST_F(FormStructureTestImpl, PossibleValues) { // A freeform input (<input>) allows any value (overriding other <select>s). FormFieldData freeform_field; freeform_field.autocomplete_attribute = "billing country"; + field.unique_renderer_id = MakeFieldRendererId(); form_data.fields.push_back(freeform_field); + FormStructure form_structure2(form_data); form_structure2.ParseFieldTypesFromAutocompleteAttributes(); EXPECT_EQ(0U, form_structure2.PossibleValues(ADDRESS_BILLING_COUNTRY).size()); @@ -5395,15 +5545,18 @@ TEST_F(FormStructureTestImpl, ParseQueryResponse_UnknownType) { 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); @@ -5454,10 +5607,12 @@ TEST_F(FormStructureTestImpl, ParseApiQueryResponse) { field.label = ASCIIToUTF16("fullname"); field.name = ASCIIToUTF16("fullname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Checkable fields should be ignored in parsing @@ -5466,6 +5621,7 @@ TEST_F(FormStructureTestImpl, ParseApiQueryResponse) { checkable_field.form_control_type = "radio"; checkable_field.check_status = FormFieldData::CheckStatus::kCheckableButUnchecked; + checkable_field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(checkable_field); FormStructure form_structure(form); @@ -5476,11 +5632,13 @@ TEST_F(FormStructureTestImpl, ParseApiQueryResponse) { FormData form2; field.label = ASCIIToUTF16("email"); field.name = ASCIIToUTF16("email"); + field.unique_renderer_id = MakeFieldRendererId(); form2.fields.push_back(field); field.label = ASCIIToUTF16("password"); field.name = ASCIIToUTF16("password"); field.form_control_type = "password"; + field.unique_renderer_id = MakeFieldRendererId(); form2.fields.push_back(field); FormStructure form_structure2(form2); @@ -5545,6 +5703,7 @@ TEST_F(FormStructureTestImpl, field.form_control_type = "email"; field.label = ASCIIToUTF16("emailaddress"); field.name = ASCIIToUTF16("emailaddress"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Add form to the vector needed by the response parsing function. @@ -5574,6 +5733,7 @@ TEST_F(FormStructureTestImpl, ParseApiQueryResponseWhenPayloadNotBase64) { field.form_control_type = "email"; field.label = ASCIIToUTF16("emailaddress"); field.name = ASCIIToUTF16("emailaddress"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Add form to the vector needed by the response parsing function. @@ -5615,12 +5775,14 @@ TEST_F(FormStructureTestImpl, ParseQueryResponse_AuthorDefinedTypes) { field.name = ASCIIToUTF16("email"); field.form_control_type = "text"; field.autocomplete_attribute = "email"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("password"); field.name = ASCIIToUTF16("password"); field.form_control_type = "password"; field.autocomplete_attribute = "new-password"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -5656,18 +5818,22 @@ TEST_F(FormStructureTestImpl, ParseQueryResponse_RationalizeLoneField) { field.label = ASCIIToUTF16("fullname"); field.name = ASCIIToUTF16("fullname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("height"); field.name = ASCIIToUTF16("height"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("email"); field.name = ASCIIToUTF16("email"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -5703,14 +5869,17 @@ TEST_F(FormStructureTestImpl, ParseQueryResponse_RationalizeCCName) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("fname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("email"); field.name = ASCIIToUTF16("email"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -5745,22 +5914,27 @@ TEST_F(FormStructureTestImpl, ParseQueryResponse_RationalizeMultiMonth_1) { field.label = ASCIIToUTF16("Cardholder"); field.name = ASCIIToUTF16("fullname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Month)"); field.name = ASCIIToUTF16("expiry_month"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Year"); field.name = ASCIIToUTF16("expiry_year"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Quantity"); field.name = ASCIIToUTF16("quantity"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -5804,18 +5978,22 @@ TEST_F(FormStructureTestImpl, ParseQueryResponse_RationalizeMultiMonth_2) { field.label = ASCIIToUTF16("Cardholder"); field.name = ASCIIToUTF16("fullname"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Expiry Date (MMYY)"); field.name = ASCIIToUTF16("expiry"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Quantity"); field.name = ASCIIToUTF16("quantity"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -5981,7 +6159,16 @@ TEST_F(FormStructureTestImpl, FindLongestCommonPrefix) { EXPECT_EQ(ASCIIToUTF16(""), prefix); } -TEST_F(FormStructureTestImpl, RationalizePhoneNumber_RunsOncePerSection) { +TEST_P(ParameterizedFormStructureTest, + RationalizePhoneNumber_RunsOncePerSection) { + bool section_with_renderer_ids = GetParam(); + base::test::ScopedFeatureList scoped_features; + std::vector<base::Feature> enabled; + std::vector<base::Feature> disabled; + (section_with_renderer_ids ? &enabled : &disabled) + ->push_back(features::kAutofillNameSectionsWithRendererIds); + scoped_features.InitWithFeatures(enabled, disabled); + FormData form; form.url = GURL("http://foo.com"); FormFieldData field; @@ -5990,18 +6177,22 @@ TEST_F(FormStructureTestImpl, RationalizePhoneNumber_RunsOncePerSection) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Home Phone"); field.name = ASCIIToUTF16("homePhoneNumber"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Cell Phone"); field.name = ASCIIToUTF16("cellPhoneNumber"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -6022,9 +6213,15 @@ TEST_F(FormStructureTestImpl, RationalizePhoneNumber_RunsOncePerSection) { FormStructure::ParseApiQueryResponse( response_string, forms, test::GetEncodedSignatures(forms), nullptr); - EXPECT_FALSE(form_structure.phone_rationalized_["fullName_1-default"]); - form_structure.RationalizePhoneNumbersInSection("fullName_1-default"); - EXPECT_TRUE(form_structure.phone_rationalized_["fullName_1-default"]); + if (section_with_renderer_ids) { + EXPECT_FALSE(form_structure.phone_rationalized_["fullName_11-default"]); + form_structure.RationalizePhoneNumbersInSection("fullName_11-default"); + EXPECT_TRUE(form_structure.phone_rationalized_["fullName_11-default"]); + } else { + EXPECT_FALSE(form_structure.phone_rationalized_["fullName_1-default"]); + form_structure.RationalizePhoneNumbersInSection("fullName_1-default"); + EXPECT_TRUE(form_structure.phone_rationalized_["fullName_1-default"]); + } ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); EXPECT_EQ(NAME_FULL, forms[0]->field(0)->server_type()); @@ -6048,14 +6245,17 @@ TEST_F(FormStructureTestImpl, RationalizeRepeatedFields_OneAddress) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -6095,18 +6295,22 @@ TEST_F(FormStructureTestImpl, RationalizeRepreatedFields_TwoAddresses) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -6148,22 +6352,27 @@ TEST_F(FormStructureTestImpl, RationalizeRepreatedFields_ThreeAddresses) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -6209,26 +6418,32 @@ TEST_F(FormStructureTestImpl, RationalizeRepreatedFields_FourAddresses) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -6281,31 +6496,37 @@ TEST_F(FormStructureTestImpl, field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.section = "Billing"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Billing"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); field.section = "Billing"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.section = "Shipping"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Shipping"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); field.section = "Shipping"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -6361,78 +6582,93 @@ TEST_F( field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.section = "Shipping"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Shipping"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Shipping"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); field.section = "Shipping"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Billing field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.section = "Billing"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Billing"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Billing"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Billing"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); field.section = "Billing"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Work address (not realistic) field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.section = "Work"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Work"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Work"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Work"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); field.section = "Work"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); field.section = "Work"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -6513,26 +6749,32 @@ TEST_F(FormStructureTestImpl, field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -6588,39 +6830,48 @@ TEST_F( // Shipping field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Billing field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -6677,45 +6928,55 @@ TEST_F(FormStructureTestImpl, // First Section field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Second Section field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Third Section field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Fourth Section field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -6779,18 +7040,22 @@ TEST_F(FormStructureTestImpl, field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.section = "billing"; @@ -6799,54 +7064,64 @@ TEST_F(FormStructureTestImpl, field.name = ASCIIToUTF16("country2"); field.form_control_type = "select-one"; field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.is_focusable = true; // visible field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country2"); field.form_control_type = "select-one"; field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country2"); field.form_control_type = "select-one"; field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country2"); field.form_control_type = "select-one"; field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.is_focusable = true; // visible field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.section = "billing-2"; field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -6912,74 +7187,88 @@ TEST_F(FormStructureTestImpl, // First Section field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); field.form_control_type = "select-one"; field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.is_focusable = true; // visible field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country2"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("city"); field.name = ASCIIToUTF16("City"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state2"); field.form_control_type = "select-one"; field.role = FormFieldData::RoleAttribute::kPresentation; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.role = FormFieldData::RoleAttribute::kOther; // visible field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Second Section field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("city"); field.name = ASCIIToUTF16("City"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Third Section field.label = ASCIIToUTF16("city"); field.name = ASCIIToUTF16("City"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state2"); field.form_control_type = "select-one"; field.role = FormFieldData::RoleAttribute::kPresentation; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.role = FormFieldData::RoleAttribute::kOther; // visible field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country2"); field.form_control_type = "select-one"; field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -7057,28 +7346,33 @@ TEST_F(FormStructureTestImpl, field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country2"); field.form_control_type = "select-one"; field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country3"); field.form_control_type = "select-one"; field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.is_focusable = true; // visible field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -7121,34 +7415,40 @@ TEST_F(FormStructureTestImpl, field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country2"); field.form_control_type = "select-one"; field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country3"); field.form_control_type = "select-one"; field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.is_focusable = true; // visible field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state2"); field.is_focusable = true; // visible + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -7208,22 +7508,26 @@ TEST_P(ParameterizedFormStructureTest, // Autocomplete Off, with server data. field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Autocomplete Off, without server data. field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Autocomplete On, with server data. field.should_autocomplete = true; field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); // Autocomplete On, without server data. field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -7277,18 +7581,22 @@ TEST_P(ParameterizedFormStructureTest, NoServerDataCCFields_CVC_NoOverwrite) { // All fields with autocomplete off and no server data. field.label = ASCIIToUTF16("Cardholder Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Credit Card Number"); field.name = ASCIIToUTF16("cc-number"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Expiration Date"); field.name = ASCIIToUTF16("exp-date"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("CVC"); field.name = ASCIIToUTF16("cvc"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -7352,18 +7660,22 @@ TEST_P(ParameterizedFormStructureTest, WithServerDataCCFields_CVC_NoOverwrite) { // All fields with autocomplete off and no server data. field.label = ASCIIToUTF16("Cardholder Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Credit Card Number"); field.name = ASCIIToUTF16("cc-number"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Expiration Date"); field.name = ASCIIToUTF16("exp-date"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("CVC"); field.name = ASCIIToUTF16("cvc"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -7443,16 +7755,22 @@ TEST_P(RationalizationFieldTypeFilterTest, Rationalization_Rules_Filter_Out) { // Just adding >=3 random fields to trigger rationalization. field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Something under test"); field.name = ASCIIToUTF16("tested-thing"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -7503,17 +7821,22 @@ TEST_P(RationalizationFieldTypeRelationshipsTest, // Just adding >=3 random fields to trigger rationalization. field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Some field with required type"); field.name = ASCIIToUTF16("some-name"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Something under test"); field.name = ASCIIToUTF16("tested-thing"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); AutofillQueryResponse response; @@ -7560,6 +7883,7 @@ TEST_F(FormStructureTestImpl, AllowBigForms) { for (size_t i = 0; i < 250; ++i) { field.form_control_type = "text"; field.name = ASCIIToUTF16("text") + base::NumberToString16(i); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); } @@ -7578,14 +7902,11 @@ TEST_F(FormStructureTestImpl, AllowBigForms) { // Tests that an Autofill upload for password form with 1 field should not be // uploaded. TEST_F(FormStructureTestImpl, OneFieldPasswordFormShouldNotBeUpload) { - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures( - /* enabled features */ {kAutofillEnforceMinRequiredFieldsForUpload}, - /* disabled features */ {kAutofillEnforceMinRequiredFieldsForQuery}); FormData form; FormFieldData field; field.name = ASCIIToUTF16("Password"); field.form_control_type = "password"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); EXPECT_FALSE(FormStructure(form).ShouldBeUploaded()); @@ -7611,9 +7932,15 @@ TEST_F(FormStructureTestImpl, CreateForPasswordManagerUpload) { // Tests if a new logical form is started with the second appearance of a field // of type |NAME|. -TEST_F(FormStructureTestImpl, NoAutocompleteSectionNames) { - base::test::ScopedFeatureList enabled; - enabled.InitAndEnableFeature(features::kAutofillUseNewSectioningMethod); +TEST_P(ParameterizedFormStructureTest, NoAutocompleteSectionNames) { + bool section_with_renderer_ids = GetParam(); + base::test::ScopedFeatureList scoped_features; + std::vector<base::Feature> enabled; + std::vector<base::Feature> disabled; + enabled.push_back(features::kAutofillUseNewSectioningMethod); + (section_with_renderer_ids ? &enabled : &disabled) + ->push_back(features::kAutofillNameSectionsWithRendererIds); + scoped_features.InitWithFeatures(enabled, disabled); FormData form; form.url = GURL("http://foo.com"); @@ -7623,26 +7950,32 @@ TEST_F(FormStructureTestImpl, NoAutocompleteSectionNames) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); field.name = ASCIIToUTF16("phone"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); field.name = ASCIIToUTF16("phone"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -7661,12 +7994,21 @@ TEST_F(FormStructureTestImpl, NoAutocompleteSectionNames) { // Assert the correct number of fields. ASSERT_EQ(6U, form_structure.field_count()); - EXPECT_EQ("fullName_1-default", form_structure.field(0)->section); - EXPECT_EQ("fullName_1-default", form_structure.field(1)->section); - EXPECT_EQ("fullName_1-default", form_structure.field(2)->section); - EXPECT_EQ("fullName_2-default", form_structure.field(3)->section); - EXPECT_EQ("fullName_2-default", form_structure.field(4)->section); - EXPECT_EQ("fullName_2-default", form_structure.field(5)->section); + if (section_with_renderer_ids) { + EXPECT_EQ("fullName_11-default", form_structure.field(0)->section); + EXPECT_EQ("fullName_11-default", form_structure.field(1)->section); + EXPECT_EQ("fullName_11-default", form_structure.field(2)->section); + EXPECT_EQ("fullName_14-default", form_structure.field(3)->section); + EXPECT_EQ("fullName_14-default", form_structure.field(4)->section); + EXPECT_EQ("fullName_14-default", form_structure.field(5)->section); + } else { + EXPECT_EQ("fullName_1-default", form_structure.field(0)->section); + EXPECT_EQ("fullName_1-default", form_structure.field(1)->section); + EXPECT_EQ("fullName_1-default", form_structure.field(2)->section); + EXPECT_EQ("fullName_2-default", form_structure.field(3)->section); + EXPECT_EQ("fullName_2-default", form_structure.field(4)->section); + EXPECT_EQ("fullName_2-default", form_structure.field(5)->section); + } } // Tests that the immediate recurrence of the |PHONE_HOME_NUMBER| type does not @@ -7683,33 +8025,40 @@ TEST_F(FormStructureTestImpl, NoSplitByRecurringPhoneFieldType) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); field.name = ASCIIToUTF16("phone"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Mobile Number"); field.name = ASCIIToUTF16("mobileNumber"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.autocomplete_attribute = "section-blue billing name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); field.name = ASCIIToUTF16("phone"); field.autocomplete_attribute = "section-blue billing tel"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Mobile Number"); field.name = ASCIIToUTF16("mobileNumber"); field.autocomplete_attribute = "section-blue billing tel"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -7740,9 +8089,15 @@ TEST_F(FormStructureTestImpl, NoSplitByRecurringPhoneFieldType) { // Tests if a new logical form is started with the second appearance of a field // of type |ADDRESS_HOME_COUNTRY|. -TEST_F(FormStructureTestImpl, SplitByRecurringFieldType) { - base::test::ScopedFeatureList enabled; - enabled.InitAndEnableFeature(features::kAutofillUseNewSectioningMethod); +TEST_P(ParameterizedFormStructureTest, SplitByRecurringFieldType) { + bool section_with_renderer_ids = GetParam(); + base::test::ScopedFeatureList scoped_features; + std::vector<base::Feature> enabled; + std::vector<base::Feature> disabled; + enabled.push_back(features::kAutofillUseNewSectioningMethod); + (section_with_renderer_ids ? &enabled : &disabled) + ->push_back(features::kAutofillNameSectionsWithRendererIds); + scoped_features.InitWithFeatures(enabled, disabled); FormData form; form.url = GURL("http://foo.com"); @@ -7753,21 +8108,25 @@ TEST_F(FormStructureTestImpl, SplitByRecurringFieldType) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.autocomplete_attribute = "section-blue shipping name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); field.autocomplete_attribute = "section-blue shipping country"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.autocomplete_attribute = "section-blue shipping name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); field.autocomplete_attribute = ""; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -7787,16 +8146,26 @@ TEST_F(FormStructureTestImpl, SplitByRecurringFieldType) { EXPECT_EQ("blue-shipping-default", form_structure.field(0)->section); EXPECT_EQ("blue-shipping-default", form_structure.field(1)->section); EXPECT_EQ("blue-shipping-default", form_structure.field(2)->section); - EXPECT_EQ("country_2-default", form_structure.field(3)->section); + if (section_with_renderer_ids) { + EXPECT_EQ("country_14-default", form_structure.field(3)->section); + } else { + EXPECT_EQ("country_2-default", form_structure.field(3)->section); + } } // Tests if a new logical form is started with the second appearance of a field // of type |NAME_FULL| and another with the second appearance of a field of // type |ADDRESS_HOME_COUNTRY|. -TEST_F(FormStructureTestImpl, +TEST_P(ParameterizedFormStructureTest, SplitByNewAutocompleteSectionNameAndRecurringType) { - base::test::ScopedFeatureList enabled; - enabled.InitAndEnableFeature(features::kAutofillUseNewSectioningMethod); + bool section_with_renderer_ids = GetParam(); + base::test::ScopedFeatureList scoped_features; + std::vector<base::Feature> enabled; + std::vector<base::Feature> disabled; + enabled.push_back(features::kAutofillUseNewSectioningMethod); + (section_with_renderer_ids ? &enabled : &disabled) + ->push_back(features::kAutofillNameSectionsWithRendererIds); + scoped_features.InitWithFeatures(enabled, disabled); FormData form; form.url = GURL("http://foo.com"); @@ -7807,21 +8176,25 @@ TEST_F(FormStructureTestImpl, field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.autocomplete_attribute = "section-blue shipping name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); field.autocomplete_attribute = "section-blue billing country"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.autocomplete_attribute = ""; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); field.autocomplete_attribute = ""; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -7842,7 +8215,11 @@ TEST_F(FormStructureTestImpl, EXPECT_EQ("blue-shipping-default", form_structure.field(0)->section); EXPECT_EQ("blue-billing-default", form_structure.field(1)->section); EXPECT_EQ("blue-billing-default", form_structure.field(2)->section); - EXPECT_EQ("country_2-default", form_structure.field(3)->section); + if (section_with_renderer_ids) { + EXPECT_EQ("country_14-default", form_structure.field(3)->section); + } else { + EXPECT_EQ("country_2-default", form_structure.field(3)->section); + } } // Tests if a new logical form is started with the second appearance of a field @@ -7860,21 +8237,25 @@ TEST_F(FormStructureTestImpl, SplitByNewAutocompleteSectionName) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.autocomplete_attribute = "section-blue shipping name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); field.autocomplete_attribute = ""; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.autocomplete_attribute = "section-blue billing name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); field.autocomplete_attribute = ""; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -7914,21 +8295,25 @@ TEST_F( field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); field.autocomplete_attribute = "section-blue shipping country"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.autocomplete_attribute = "section-blue billing name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); field.autocomplete_attribute = ""; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -7966,11 +8351,13 @@ TEST_F(FormStructureTestImpl, FromEmptyAutocompleteSectionToDefinedOne) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); field.autocomplete_attribute = "section-blue shipping country"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -8005,17 +8392,20 @@ TEST_F(FormStructureTestImpl, field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Phone"); field.name = ASCIIToUTF16("phone"); field.is_focusable = false; // hidden + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("FullName"); field.name = ASCIIToUTF16("fullName"); field.is_focusable = true; // visible field.autocomplete_attribute = "shipping name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); @@ -8052,11 +8442,13 @@ TEST_F(FormStructureTestImpl, IgnoreAribtraryAutocompleteSectionName) { field.label = ASCIIToUTF16("Full Name"); field.name = ASCIIToUTF16("fullName"); field.autocomplete_attribute = "section-red ship name"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); field.autocomplete_attribute = "section-blue shipping country"; + field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); FormStructure form_structure(form); 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 new file mode 100644 index 00000000000..1e5ed929936 --- /dev/null +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map.cc @@ -0,0 +1,156 @@ +// 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/geo/alternative_state_name_map.h" + +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" + +namespace autofill { + +namespace { + +// Assuming a user can have maximum 500 profiles each containing a different +// state string in the worst case scenario. +constexpr int kMaxMapSize = 500; + +// The characters to be removed from the state strings before the comparison. +constexpr char kCharsToStrip[] = ".- "; + +} // namespace + +// static +AlternativeStateNameMap* AlternativeStateNameMap::GetInstance() { + static base::NoDestructor<AlternativeStateNameMap> + g_alternative_state_name_map; + return g_alternative_state_name_map.get(); +} + +AlternativeStateNameMap::AlternativeStateNameMap() = default; + +// static +AlternativeStateNameMap::StateName AlternativeStateNameMap::NormalizeStateName( + const StateName& text) { + base::string16 normalized_text; + base::RemoveChars(text.value(), base::ASCIIToUTF16(kCharsToStrip), + &normalized_text); + return StateName(normalized_text); +} + +base::Optional<AlternativeStateNameMap::CanonicalStateName> +AlternativeStateNameMap::GetCanonicalStateName( + const CountryCode& country_code, + const StateName& state_name, + bool is_state_name_normalized) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(alternative_state_name_map_sequence_checker_); + // Example: + // Entries in |localized_state_names_map_| are: + // ("DE", "Bavaria") -> { + // "canonical_name": "Bayern", + // "abbreviations": "BY", + // "alternative_names": "Bavaria" + // } + // Entries in |localized_state_names_reverse_lookup_map_| are: + // ("DE", "Bayern") -> "Bayern" + // ("DE", "BY") -> "Bayern" + // ("DE", "Bavaria") -> "Bayern" + // then, AlternativeStateNameMap::GetCanonicalStateName("DE", "Bayern") = + // AlternativeStateNameMap::GetCanonicalStateName("DE", "BY") = + // AlternativeStateNameMap::GetCanonicalStateName("DE", "Bavaria") = + // CanonicalStateName("Bayern") + StateName normalized_state_name = state_name; + if (!is_state_name_normalized) + normalized_state_name = NormalizeStateName(state_name); + + auto it = localized_state_names_reverse_lookup_map_.find( + {country_code, normalized_state_name}); + if (it != localized_state_names_reverse_lookup_map_.end()) + return it->second; + + return base::nullopt; +} + +base::Optional<StateEntry> AlternativeStateNameMap::GetEntry( + const CountryCode& country_code, + const StateName& state_string_from_profile) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(alternative_state_name_map_sequence_checker_); + + StateName normalized_state_string_from_profile = + NormalizeStateName(state_string_from_profile); + base::Optional<CanonicalStateName> canonical_state_name = + 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()); + } + + 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; +} + +void AlternativeStateNameMap::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) { + DCHECK_CALLED_ON_VALID_SEQUENCE(alternative_state_name_map_sequence_checker_); + + // Example: + // AddEntry("DE", "Bavaria", { + // "canonical_name": "Bayern", + // "abbreviations": "BY", + // "alternative_names": "Bavaria" + // }, {"Bavaria", "BY", "Bayern"}, "Bayern") + // Then entry added to |localized_state_names_map_| is: + // ("DE", "Bayern") -> { + // "canonical_name": "Bayern", + // "abbreviations": "BY", + // "alternative_names": "Bavaria" + // } + // Entries added to |localized_state_names_reverse_lookup_map_| are: + // ("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; + } +} + +bool AlternativeStateNameMap::IsLocalisedStateNamesMapEmpty() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(alternative_state_name_map_sequence_checker_); + return localized_state_names_map_.empty(); +} + +void AlternativeStateNameMap::ClearAlternativeStateNameMap() { + DCHECK_CALLED_ON_VALID_SEQUENCE(alternative_state_name_map_sequence_checker_); + localized_state_names_map_.clear(); + localized_state_names_reverse_lookup_map_.clear(); +} + +} // namespace autofill 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 new file mode 100644 index 00000000000..1ed11eaa6c6 --- /dev/null +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map.h @@ -0,0 +1,181 @@ +// 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_GEO_ALTERNATIVE_STATE_NAME_MAP_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_ALTERNATIVE_STATE_NAME_MAP_H_ + +#include "components/autofill/core/browser/proto/states.pb.h" + +#include "base/i18n/case_conversion.h" +#include "base/no_destructor.h" +#include "base/optional.h" +#include "base/sequence_checker.h" +#include "base/strings/string16.h" +#include "base/util/type_safety/strong_alias.h" + +namespace autofill { +// AlternativeStateNameMap encapsulates mappings from state names in the +// profiles to their localized and the abbreviated names. +// +// AlternativeStateNameMap is used for the filling of state fields, comparison +// of profiles, determining mergeability of the address profiles and required +// |ADDRESS_HOME_STATE| votes to be sent to the server. +// +// AlternativeStateNameMap can provide the following data for the states: +// 1. The state string stored in the address profile denoted by +// state_string_from_profile in this class. +// 2. The canonical state name (StateEntry::canonical_name) which acts as the +// unique identifier representing the state (unique within a country). +// 3. The abbreviations of the state (StateEntry::abbreviations). +// 4. The alternative names of the state (StateEntry::alternative_names). +// +// StateEntry holds the information about the abbreviations and the +// alternative names of the state which is determined after comparison with the +// state values saved in the address profiles if they match. +// +// The main map |localized_state_names_map_| maps the tuple +// (country_code, canonical state name) as the key to the corresponding +// StateEntry object (with the information about the abbreviations and the +// alternative names) as the value. +// +// The |localized_state_names_reverse_lookup_map_| takes in the +// country_code and StateEntry::name, StateEntry::abbreviations or +// ::alternative_names as the key and the canonical state name as the value. +// +// Example: Considering "California" as the state_string_from_profile and +// the corresponding StateEntry object: +// { +// 'canonical_name': 'California', +// 'abbreviations': ['CA'], +// 'alternate_names': ['The Golden State'] +// } +// +// 1. StateEntry::canonical_name (i.e "California" in this case) acts +// as the canonical state name. +// 2. ("US", "California") acts as the key and the above StateEntry +// object is added as the value in the +// |localized_state_names_map_|. +// 3. Entries added to |localized_state_names_reverse_lookup_map_| +// are: +// a. ("US", "California") -> "California" +// 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 +class AlternativeStateNameMap { + public: + // Represents ISO 3166-1 alpha-2 codes (always uppercase ASCII). + using CountryCode = util::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>; + + // 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>; + + static AlternativeStateNameMap* GetInstance(); + + ~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|). + base::Optional<CanonicalStateName> GetCanonicalStateName( + const CountryCode& country_code, + const StateName& state_name, + bool is_state_name_normalized = false) const; + + // Returns the value present in |localized_state_names_map_| corresponding + // to (|country_code|, |state_string_from_profile|). In case, the entry does + // not exist in the map, base::nullopt is returned. + base::Optional<StateEntry> GetEntry( + const CountryCode& country_code, + const StateName& state_string_from_profile) const; + + // 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 + // |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); + + // Returns true if the |localized_state_names_map_| is empty. + bool IsLocalisedStateNamesMapEmpty() const; + +#if defined(UNIT_TEST) + // Clears the map for testing purposes. + void ClearAlternativeStateNameMapForTesting() { + ClearAlternativeStateNameMap(); + } +#endif + + private: + AlternativeStateNameMap(); + + // Clears the |localized_state_names_map_| and + // |localized_state_names_reverse_lookup_map_|. + // Used only for testing purposes. + void ClearAlternativeStateNameMap(); + + // A custom comparator for the + // |localized_state_names_reverse_lookup_map_| that ignores the case + // of the string on comparisons. + struct CaseInsensitiveLessComparator { + bool operator()(const std::pair<CountryCode, StateName>& lhs, + const std::pair<CountryCode, StateName>& rhs) const { + // Compares the country codes that are always uppercase ASCII. + if (lhs.first != rhs.first) + return lhs.first.value() < rhs.first.value(); + + return base::i18n::ToLower(lhs.second.value()) < + base::i18n::ToLower(rhs.second.value()); + } + }; + + // Since the constructor is private, |base::NoDestructor| must be friend to be + // allowed to construct the class. + friend class base::NoDestructor<AlternativeStateNameMap>; + + // A map that stores the alternative state names. The map is keyed + // by the country_code and the canonical state name (or + // normalized_state_value_from_profile in case no canonical state name is + // known) while the value is the StateEntry object. + std::map<std::pair<CountryCode, CanonicalStateName>, StateEntry> + localized_state_names_map_; + + // The map is keyed by the country_code and the abbreviation or + // canonical name or the alternative name of the state. + std::map<std::pair<CountryCode, StateName>, + CanonicalStateName, + CaseInsensitiveLessComparator> + localized_state_names_reverse_lookup_map_; + + SEQUENCE_CHECKER(alternative_state_name_map_sequence_checker_); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_ALTERNATIVE_STATE_NAME_MAP_H_ 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 new file mode 100644 index 00000000000..9435c583dbf --- /dev/null +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_test_utils.cc @@ -0,0 +1,69 @@ +// 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/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 { + +namespace test { + +void PopulateStateEntry(const TestStateEntry& test_state_entry, + StateEntry* state_entry) { + state_entry->set_canonical_name(test_state_entry.canonical_name); + for (const auto& abbr : test_state_entry.abbreviations) + state_entry->add_abbreviations(abbr); + for (const auto& alternative_name : test_state_entry.alternative_names) + state_entry->add_alternative_names(alternative_name); +} + +void ClearAlternativeStateNameMapForTesting() { + AlternativeStateNameMap::GetInstance() + ->ClearAlternativeStateNameMapForTesting(); +} + +void PopulateAlternativeStateNameMapForTesting( + const std::string& country_code, + const std::string& key, + const std::vector<TestStateEntry>& test_state_entries) { + for (const auto& test_state_entry : test_state_entries) { + 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())); + 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))); + + AlternativeStateNameMap::GetInstance()->AddEntry( + AlternativeStateNameMap::CountryCode(country_code), + AlternativeStateNameMap::StateName(base::ASCIIToUTF16(key)), + state_entry, alternatives, &canonical_state_name); + } +} + +std::string CreateStatesProtoAsString(const std::string& country_code, + const TestStateEntry& test_state_entry) { + StatesInCountry states_data; + states_data.set_country_code(std::move(country_code)); + StateEntry* entry = states_data.add_states(); + PopulateStateEntry(test_state_entry, entry); + + std::string serialized_output; + bool proto_is_serialized = states_data.SerializeToString(&serialized_output); + DCHECK(proto_is_serialized); + return serialized_output; +} + +} // namespace test +} // namespace autofill 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 new file mode 100644 index 00000000000..0c86be629a8 --- /dev/null +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h @@ -0,0 +1,47 @@ +// 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_GEO_ALTERNATIVE_STATE_NAME_MAP_TEST_UTILS_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_ALTERNATIVE_STATE_NAME_MAP_TEST_UTILS_H_ + +#include "base/optional.h" +#include "components/autofill/core/browser/proto/states.pb.h" + +namespace autofill { + +namespace test { + +namespace internal { +template <typename = void> +struct TestStateEntry { + std::string canonical_name = "Bavaria"; + std::vector<std::string> abbreviations = {"BY"}; + std::vector<std::string> alternative_names = {"Bayern"}; +}; +} // namespace internal + +using TestStateEntry = internal::TestStateEntry<>; + +// Populates |state_entry| with the data in |test_state_entry|. +void PopulateStateEntry(const TestStateEntry& test_state_entry, + StateEntry* state_entry); + +// Clears the map for testing purposes. +void ClearAlternativeStateNameMapForTesting(); + +// Inserts a StateEntry instance into AlternativeStateNameMap for testing. +void PopulateAlternativeStateNameMapForTesting( + const std::string& country_code = "DE", + const std::string& key = "Bavaria", + const std::vector<TestStateEntry>& test_state_entries = {TestStateEntry()}); + +// Returns a StateEntry instance serialized as string. +std::string CreateStatesProtoAsString( + const std::string& country_code = "DE", + const TestStateEntry& test_state_entry = TestStateEntry()); + +} // namespace test +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_ALTERNATIVE_STATE_NAME_MAP_TEST_UTILS_H_ 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 new file mode 100644 index 00000000000..268b41cec02 --- /dev/null +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_unittest.cc @@ -0,0 +1,119 @@ +// 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/geo/alternative_state_name_map.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { +namespace test { + +// Tests that map is not empty when an entry has been added to it. +TEST(AlternativeStateNameMapTest, IsEntryAddedToMap) { + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting(); + EXPECT_FALSE( + AlternativeStateNameMap::GetInstance()->IsLocalisedStateNamesMapEmpty()); +} + +// Tests that the state canonical name is present when an entry is added to +// the map. +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))), + 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(""))), + base::nullopt); + EXPECT_EQ(alternative_state_name_map->GetCanonicalStateName( + AlternativeStateNameMap::CountryCode(""), + AlternativeStateNameMap::StateName(base::ASCIIToUTF16(""))), + base::nullopt); +} + +// Tests that the separate entries are created in the map for the different +// country codes. +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); +} + +// Tests that |AlternativeStateNameMap::NormalizeStateName()| removes "-", " " +// and "." from the text. +TEST(AlternativeStateNameMapTest, StripText) { + struct { + const char* test_string; + const char* expected; + } test_cases[] = {{"B.Y", "BY"}, + {"The Golden Sun", "TheGoldenSun"}, + {"Bavaria - BY", "BavariaBY"}}; + for (const auto& test_case : test_cases) { + SCOPED_TRACE(testing::Message() << "test_string: " << test_case.test_string + << " | expected: " << test_case.expected); + AlternativeStateNameMap::StateName text = + AlternativeStateNameMap::StateName( + base::ASCIIToUTF16(test_case.test_string)); + EXPECT_EQ(AlternativeStateNameMap::NormalizeStateName(text).value(), + base::ASCIIToUTF16(test_case.expected)); + } +} + +// Tests that the correct entries are returned when the maps in +// AlternativeStateNameMap are queried. +TEST(AlternativeStateNameMapTest, GetEntry) { + test::ClearAlternativeStateNameMapForTesting(); + test::PopulateAlternativeStateNameMapForTesting(); + AlternativeStateNameMap* alternative_state_name_map = + AlternativeStateNameMap::GetInstance(); + EXPECT_EQ( + alternative_state_name_map->GetEntry( + AlternativeStateNameMap::CountryCode("DE"), + AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Random"))), + base::nullopt); + auto entry = alternative_state_name_map->GetEntry( + AlternativeStateNameMap::CountryCode("DE"), + AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bavaria"))); + EXPECT_NE(entry, base::nullopt); + ASSERT_TRUE(entry->has_canonical_name()); + EXPECT_EQ(entry->canonical_name(), "Bavaria"); + EXPECT_THAT(entry->abbreviations(), + testing::UnorderedElementsAreArray({"BY"})); + EXPECT_THAT(entry->alternative_names(), + testing::UnorderedElementsAreArray({"Bayern"})); +} + +} // namespace test +} // namespace autofill 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 new file mode 100644 index 00000000000..3c85158b61e --- /dev/null +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.cc @@ -0,0 +1,226 @@ +// 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/geo/alternative_state_name_map_updater.h" + +#include <memory> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/ranges/algorithm.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/task/post_task.h" +#include "base/task/task_traits.h" +#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/common/autofill_l10n_util.h" +#include "components/autofill/core/common/autofill_prefs.h" +#include "components/prefs/pref_service.h" + +namespace autofill { + +namespace { + +// Returns data read from the file specified in |file|. +std::string LoadDataFromFile(const base::FilePath& file) { + DCHECK(!file.empty()); + + std::string data; + if (!base::PathExists(file)) { + VLOG(1) << "File does not exist: " << file; + return std::string(); + } + + if (!base::ReadFileToString(file, &data)) { + VLOG(1) << "Failed reading from file: " << file; + return std::string(); + } + + return data; +} + +} // namespace + +AlternativeStateNameMapUpdater::AlternativeStateNameMapUpdater() + : task_runner_(base::ThreadPool::CreateSequencedTaskRunner( + {base::MayBlock(), base::TaskPriority::BEST_EFFORT})) {} + +AlternativeStateNameMapUpdater::~AlternativeStateNameMapUpdater() = default; + +// static +bool AlternativeStateNameMapUpdater::ContainsState( + const std::vector<AlternativeStateNameMap::StateName>& + stripped_alternative_state_names, + const AlternativeStateNameMap::StateName& + stripped_state_values_from_profile) { + l10n::CaseInsensitiveCompare compare; + + // Returns true if |str1| is same as |str2| in a case-insensitive comparison. + return base::ranges::any_of( + stripped_alternative_state_names, + [&](const AlternativeStateNameMap::StateName& text) { + return compare.StringsEqual(text.value(), + stripped_state_values_from_profile.value()); + }); +} + +void AlternativeStateNameMapUpdater::LoadStatesData( + const 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); + + // If the installed directory path is empty, it means that the component is + // not ready for use yet. + if (data_download_path.empty()) { + std::move(done_callback).Run(); + return; + } + + const std::vector<std::string>& country_codes = + CountryDataMap::GetInstance()->country_codes(); + + // 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) { + const AlternativeStateNameMap::CountryCode& country_code = entry.first; + const std::vector<AlternativeStateNameMap::StateName>& states = + entry.second; + + // This is a security check to ensure that we only attempt to read files + // that match to known countries. + 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), + base::BindOnce( + &AlternativeStateNameMapUpdater::ProcessLoadedStateFileContent, + weak_ptr_factory_.GetWeakPtr(), states)); + } +} + +void AlternativeStateNameMapUpdater::ProcessLoadedStateFileContent( + const std::vector<AlternativeStateNameMap::StateName>& + stripped_state_values_from_profiles, + const std::string& data) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + DCHECK_GT(number_pending_init_tasks_, 0); + --number_pending_init_tasks_; + + StatesInCountry states_data; + + if (!data.empty() && states_data.ParseFromString(data)) { + DCHECK(states_data.has_country_code()); + AlternativeStateNameMap::CountryCode country_code = + AlternativeStateNameMap::CountryCode(states_data.country_code()); + + // Boolean flags that denote in |match_found[i]| whether the match has been + // found for |stripped_state_values_from_profiles[i]|. + std::vector<bool> match_found(stripped_state_values_from_profiles.size(), + false); + + // 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 + // the above created states list (if a match is not found for v yet). If the + // 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); + + 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 + // the state under consideration, add it to the AlternativeStateNameMap. + if (ContainsState(state_names, + stripped_state_values_from_profiles[i])) { + AlternativeStateNameMap::GetInstance()->AddEntry( + country_code, stripped_state_values_from_profiles[i], state_entry, + state_names, &state_canonical_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) { + for (auto& callback : std::exchange(pending_init_done_callbacks_, {})) + std::move(callback).Run(); + } +} + +std::vector<AlternativeStateNameMap::StateName> +AlternativeStateNameMapUpdater::ExtractAllStateNames( + const StateEntry& state_entry) { + DCHECK(state_entry.has_canonical_name()); + + std::vector<AlternativeStateNameMap::StateName> state_names; + state_names.reserve(1u + state_entry.abbreviations_size() + + state_entry.alternative_names_size()); + + state_names.emplace_back(AlternativeStateNameMap::NormalizeStateName( + AlternativeStateNameMap::StateName( + base::UTF8ToUTF16(state_entry.canonical_name())))); + for (const auto& abbr : state_entry.abbreviations()) { + state_names.emplace_back(AlternativeStateNameMap::NormalizeStateName( + AlternativeStateNameMap::StateName(base::UTF8ToUTF16(abbr)))); + } + for (const auto& alternative_name : state_entry.alternative_names()) { + state_names.emplace_back(AlternativeStateNameMap::NormalizeStateName( + AlternativeStateNameMap::StateName( + base::UTF8ToUTF16(alternative_name)))); + } + + return state_names; +} + +} // 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 new file mode 100644 index 00000000000..ab1caabaf66 --- /dev/null +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater.h @@ -0,0 +1,113 @@ +// 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_GEO_ALTERNATIVE_STATE_NAME_MAP_UPDATER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_ALTERNATIVE_STATE_NAME_MAP_UPDATER_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map.h" + +class PrefService; + +namespace autofill { + +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 { + public: + AlternativeStateNameMapUpdater(); + ~AlternativeStateNameMapUpdater(); + 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); + +#if defined(UNIT_TEST) + // A wrapper around |ProcessLoadedStateFileContent| used for testing purposes. + void ProcessLoadedStateFileContentForTesting( + const std::vector<AlternativeStateNameMap::StateName>& + state_values_from_profiles, + const std::string& data, + base::OnceClosure callback) { + ++number_pending_init_tasks_; + pending_init_done_callbacks_.push_back(std::move(callback)); + ProcessLoadedStateFileContent(state_values_from_profiles, data); + } + + // A wrapper around |ContainsState| used for testing purposes. + static bool ContainsStateForTesting( + const std::vector<AlternativeStateNameMap::StateName>& + stripped_alternative_state_names, + const AlternativeStateNameMap::StateName& + stripped_state_values_from_profile) { + return ContainsState(stripped_alternative_state_names, + stripped_state_values_from_profile); + } +#endif + + private: + // Compares |stripped_state_value_from_profile| with the entries in + // |stripped_state_alternative_names| and returns true if a match is found. + static bool ContainsState( + const std::vector<AlternativeStateNameMap::StateName>& + stripped_alternative_state_names, + const AlternativeStateNameMap::StateName& + stripped_state_values_from_profile); + + // Each entry in |state_values_from_profiles| is compared with the states + // |data| read from the files and then inserted into the + // AlternativeStateNameMap. + void ProcessLoadedStateFileContent( + const std::vector<AlternativeStateNameMap::StateName>& + state_values_from_profiles, + const std::string& data); + + // Builds and returns a list of all the names of the state (including its + // abbreviations) from the |state_entry| into |state_names|. + std::vector<AlternativeStateNameMap::StateName> ExtractAllStateNames( + const StateEntry& state_entry); + + // TaskRunner for reading files from disk. + scoped_refptr<base::SequencedTaskRunner> task_runner_; + + // 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; + + SEQUENCE_CHECKER(sequence_checker_); + + // base::WeakPtr ensures that the callback bound to the object is canceled + // when that object is destroyed. + base::WeakPtrFactory<AlternativeStateNameMapUpdater> weak_ptr_factory_{this}; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_GEO_ALTERNATIVE_STATE_NAME_MAP_UPDATER_H_ 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 new file mode 100644 index 00000000000..97a6eb1038a --- /dev/null +++ b/chromium/components/autofill/core/browser/geo/alternative_state_name_map_updater_unittest.cc @@ -0,0 +1,159 @@ +// 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/geo/alternative_state_name_map_updater.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/optional.h" +#include "base/strings/utf_string_conversions.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/common/autofill_prefs.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::ASCIIToUTF16; +using base::UTF8ToUTF16; + +namespace autofill { + +class AlternativeStateNameMapUpdaterTest : public ::testing::Test { + public: + AlternativeStateNameMapUpdaterTest() + : pref_service_(test::PrefServiceForTesting()) {} + + void SetUp() override { + ASSERT_TRUE(data_install_dir_.CreateUniqueTempDir()); + } + + 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); + } + + protected: + base::test::TaskEnvironment task_environment_; + AlternativeStateNameMapUpdater alternative_state_name_map_updater; + std::unique_ptr<PrefService> pref_service_; + base::ScopedTempDir data_install_dir_; +}; + +// Tests that the states data is added to AlternativeStateNameMap. +TEST_F(AlternativeStateNameMapUpdaterTest, EntryAddedToStateMap) { + test::ClearAlternativeStateNameMapForTesting(); + std::string states_data = test::CreateStatesProtoAsString(); + std::vector<AlternativeStateNameMap::StateName> test_strings = { + AlternativeStateNameMap::StateName(ASCIIToUTF16("Bavaria")), + AlternativeStateNameMap::StateName(ASCIIToUTF16("Bayern")), + AlternativeStateNameMap::StateName(ASCIIToUTF16("B.Y")), + AlternativeStateNameMap::StateName(ASCIIToUTF16("Bav-aria")), + AlternativeStateNameMap::StateName(UTF8ToUTF16("amapá")), + AlternativeStateNameMap::StateName(ASCIIToUTF16("Broen")), + AlternativeStateNameMap::StateName(ASCIIToUTF16("Bavaria is in Germany")), + AlternativeStateNameMap::StateName(ASCIIToUTF16("BA is in Germany"))}; + std::vector<bool> state_data_present = {true, true, true, true, + false, false, false, false}; + + 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, + state_data_present[i]); + } +} + +// Tests that the AlternativeStateNameMap is populated when +// |StateNameMapUpdater::LoadStatesData()| is called. +TEST_F(AlternativeStateNameMapUpdaterTest, TestLoadStatesData) { + test::ClearAlternativeStateNameMapForTesting(); + + base::WriteFile(GetPath().AppendASCII("DE"), + test::CreateStatesProtoAsString()); + WritePathToPref(GetPath()); + + base::RunLoop run_loop; + alternative_state_name_map_updater.LoadStatesData( + {{AlternativeStateNameMap::CountryCode("DE"), + {AlternativeStateNameMap::StateName(ASCIIToUTF16("Bavaria"))}}}, + pref_service_.get(), run_loop.QuitClosure()); + run_loop.Run(); + + EXPECT_NE( + AlternativeStateNameMap::GetInstance()->GetCanonicalStateName( + AlternativeStateNameMap::CountryCode("DE"), + AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bavaria"))), + base::nullopt); +} + +// Tests that the AlternativeStateNameMap is populated when +// |StateNameMapUpdater::LoadStatesData()| is called and there are UTF8 strings. +TEST_F(AlternativeStateNameMapUpdaterTest, TestLoadStatesDataUTF8) { + test::ClearAlternativeStateNameMapForTesting(); + + base::WriteFile( + GetPath().AppendASCII("ES"), + test::CreateStatesProtoAsString( + "ES", {.canonical_name = "Paraná", + .abbreviations = {"PR"}, + .alternative_names = {"Parana", "State of Parana"}})); + WritePathToPref(GetPath()); + + base::RunLoop run_loop; + alternative_state_name_map_updater.LoadStatesData( + {{AlternativeStateNameMap::CountryCode("ES"), + {AlternativeStateNameMap::StateName(ASCIIToUTF16("Parana"))}}}, + pref_service_.get(), run_loop.QuitClosure()); + run_loop.Run(); + + base::Optional<StateEntry> entry1 = + AlternativeStateNameMap::GetInstance()->GetEntry( + AlternativeStateNameMap::CountryCode("ES"), + AlternativeStateNameMap::StateName(base::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("ES"), + AlternativeStateNameMap::StateName(base::UTF8ToUTF16("Parana"))); + EXPECT_NE(entry2, base::nullopt); + EXPECT_EQ(entry2->canonical_name(), "Paraná"); + EXPECT_THAT(entry2->abbreviations(), + testing::UnorderedElementsAreArray({"PR"})); + EXPECT_THAT(entry2->alternative_names(), testing::UnorderedElementsAreArray( + {"Parana", "State of Parana"})); +} + +// 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")))); + EXPECT_FALSE(AlternativeStateNameMapUpdater::ContainsStateForTesting( + {AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bavaria")), + AlternativeStateNameMap::StateName(base::ASCIIToUTF16("Bayern")), + AlternativeStateNameMap::StateName(base::ASCIIToUTF16("BY"))}, + AlternativeStateNameMap::StateName(base::ASCIIToUTF16("California")))); +} + +} // 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 43a02de277f..121868e521a 100644 --- a/chromium/components/autofill/core/browser/geo/autofill_country.cc +++ b/chromium/components/autofill/core/browser/geo/autofill_country.cc @@ -37,20 +37,16 @@ AutofillCountry::AutofillCountry(const std::string& country_code, // If there is no entry in the |CountryDataMap| for the // |country_code_for_country_data| use the country code derived from the // locale. This reverts to US. - country_data_map->HasCountryData(country_code_) + country_data_map->HasRequiredFieldsForAddressImport(country_code_) ? country_code_ : CountryCodeForLocale(locale); // Acquire the country address data. - const CountryData& data = country_data_map->GetCountryData(country_code_); + required_fields_for_address_import_ = + country_data_map->GetRequiredFieldsForAddressImport(country_code_); // Translate the country name by the supplied local. name_ = l10n_util::GetDisplayNameForCountry(country_code_, locale); - - // Get the localized strings associate with the address fields. - postal_code_label_ = l10n_util::GetStringUTF16(data.postal_code_label_id); - state_label_ = l10n_util::GetStringUTF16(data.state_label_id); - address_required_fields_ = data.address_required_fields; } AutofillCountry::~AutofillCountry() {} @@ -82,10 +78,7 @@ AutofillCountry::AutofillCountry(const std::string& country_code, const base::string16& name, const base::string16& postal_code_label, const base::string16& state_label) - : country_code_(country_code), - name_(name), - postal_code_label_(postal_code_label), - state_label_(state_label) {} + : country_code_(country_code), name_(name) {} // Prints a formatted log of a |AutofillCountry| to a |LogBuffer|. LogBuffer& operator<<(LogBuffer& buffer, const AutofillCountry& country) { @@ -97,8 +90,6 @@ LogBuffer& operator<<(LogBuffer& buffer, const AutofillCountry& country) { buffer << Tr{} << "State required:" << country.requires_state(); buffer << Tr{} << "Zip required:" << country.requires_zip(); buffer << Tr{} << "City required:" << country.requires_city(); - buffer << Tr{} << "State label:" << country.state_label(); - buffer << Tr{} << "Postal code label:" << country.postal_code_label(); buffer << CTag{"table"}; buffer << CTag{"div"}; buffer << CTag{}; diff --git a/chromium/components/autofill/core/browser/geo/autofill_country.h b/chromium/components/autofill/core/browser/geo/autofill_country.h index fc16ef008d3..c1d3a3eb1b7 100644 --- a/chromium/components/autofill/core/browser/geo/autofill_country.h +++ b/chromium/components/autofill/core/browser/geo/autofill_country.h @@ -31,33 +31,32 @@ class AutofillCountry { const std::string& country_code() const { return country_code_; } const base::string16& name() const { return name_; } - const base::string16& postal_code_label() const { return postal_code_label_; } - const base::string16& state_label() const { return state_label_; } // City is expected in a complete address for this country. bool requires_city() const { - return (address_required_fields_ & ADDRESS_REQUIRES_CITY) != 0; + return (required_fields_for_address_import_ & ADDRESS_REQUIRES_CITY) != 0; } // State is expected in a complete address for this country. bool requires_state() const { - return (address_required_fields_ & ADDRESS_REQUIRES_STATE) != 0; + return (required_fields_for_address_import_ & ADDRESS_REQUIRES_STATE) != 0; } // Zip is expected in a complete address for this country. bool requires_zip() const { - return (address_required_fields_ & ADDRESS_REQUIRES_ZIP) != 0; + return (required_fields_for_address_import_ & ADDRESS_REQUIRES_ZIP) != 0; } // An address line1 is expected in a complete address for this country. bool requires_line1() const { - return (address_required_fields_ & ADDRESS_REQUIRES_LINE1) != 0; + return (required_fields_for_address_import_ & ADDRESS_REQUIRES_LINE1) != 0; } // True if a complete address is expected to either contain a state or a ZIP // code. Not true if the address explicitly needs both. bool requires_zip_or_state() const { - return (address_required_fields_ & ADDRESS_REQUIRES_ZIP_OR_STATE) != 0; + return (required_fields_for_address_import_ & + ADDRESS_REQUIRES_ZIP_OR_STATE) != 0; } private: @@ -72,14 +71,8 @@ class AutofillCountry { // The country's name, localized to the app locale. base::string16 name_; - // The localized label for the postal code (or zip code) field. - base::string16 postal_code_label_; - - // The localized label for the state (or province, district, etc.) field. - base::string16 state_label_; - - // Address requirement field codes for the country. - AddressRequiredFields address_required_fields_; + // Required fields for an address import for the country. + RequiredFieldsForAddressImport required_fields_for_address_import_; DISALLOW_COPY_AND_ASSIGN(AutofillCountry); }; 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 655c9e1777a..487d46e1990 100644 --- a/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc +++ b/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc @@ -24,8 +24,6 @@ TEST(AutofillCountryTest, AutofillCountry) { AutofillCountry united_states_en("US", "en_US"); EXPECT_EQ("US", united_states_en.country_code()); EXPECT_EQ(ASCIIToUTF16("United States"), united_states_en.name()); - EXPECT_EQ(ASCIIToUTF16("ZIP code"), united_states_en.postal_code_label()); - EXPECT_EQ(ASCIIToUTF16("State"), united_states_en.state_label()); AutofillCountry united_states_es("US", "es"); EXPECT_EQ("US", united_states_es.country_code()); @@ -39,8 +37,6 @@ TEST(AutofillCountryTest, AutofillCountry) { AutofillCountry canada_en("CA", "en_US"); EXPECT_EQ("CA", canada_en.country_code()); EXPECT_EQ(ASCIIToUTF16("Canada"), canada_en.name()); - EXPECT_EQ(ASCIIToUTF16("Postal code"), canada_en.postal_code_label()); - EXPECT_EQ(ASCIIToUTF16("Province"), canada_en.state_label()); AutofillCountry canada_hu("CA", "hu"); EXPECT_EQ("CA", canada_hu.country_code()); @@ -124,7 +120,7 @@ TEST(AutofillCountryTest, AliasMappingsForCountryData) { CountryDataMap* country_data_map = CountryDataMap::GetInstance(); // There should be country data for the "GB". - EXPECT_TRUE(country_data_map->HasCountryData("GB")); + EXPECT_TRUE(country_data_map->HasRequiredFieldsForAddressImport("GB")); // Check the correctness of the alias definitions. EXPECT_TRUE(country_data_map->HasCountryCodeAlias("UK")); diff --git a/chromium/components/autofill/core/browser/geo/country_data.cc b/chromium/components/autofill/core/browser/geo/country_data.cc index a85fba5a745..545179c09d8 100644 --- a/chromium/components/autofill/core/browser/geo/country_data.cc +++ b/chromium/components/autofill/core/browser/geo/country_data.cc @@ -14,9 +14,9 @@ namespace autofill { namespace { -struct StaticCountryData { +struct StaticCountryAddressImportRequirementsData { char country_code[3]; - CountryData country_data; + RequiredFieldsForAddressImport address_import_field_requirements; }; // Alias definitions record for CountryData requests. A request for @@ -30,783 +30,284 @@ struct StaticCountryCodeAliasData { // Alias definitions. const StaticCountryCodeAliasData kCountryCodeAliases[] = {{"UK", "GB"}}; -// Maps country codes to localized label string identifiers. Keep this sorted +// Maps country codes to address import requirements. Keep this sorted // by country code. // This list is comprized of countries appearing in both // //third_party/icu/source/data/region/en.txt and // //third_party/libaddressinput/src/cpp/src/region_data_constants.cc. -const StaticCountryData kCountryData[] = { - // clang-format off - {"AC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"AD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PARISH, - ADDRESS_REQUIRES_LINE1 } }, - {"AE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_EMIRATE, - ADDRESS_REQUIRES_LINE1_STATE } }, - {"AF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"AG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1 } }, - {"AI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"AL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"AM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"AO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"AQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"AR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"AS", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"AT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"AU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"AW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"AX", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"AZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"BA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"BB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PARISH, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"BD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"BE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"BF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"BG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"BH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"BI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"BJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"BL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"BM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"BN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"BO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"BQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"BR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"BS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_ISLAND, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"BT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"BV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"BW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"BY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"BZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"CA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"CC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"CD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"CF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"CG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"CH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"CI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"CK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"CL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"CM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"CN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"CO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"CR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"CS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1 } }, - {"CV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_ISLAND, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"CW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"CX", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"CY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"CZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"DE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"DJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"DK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"DM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"DO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"DZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"EC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"EE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"EG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"EH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"ER", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"ES", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"ET", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"FI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"FJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"FK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"FM", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"FO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"FR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"GB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_COUNTY, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"GE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"GI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1 } }, - {"GL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"GN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"GR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GU", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"GY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"HK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_AREA, - ADDRESS_REQUIRES_LINE1_STATE } }, - {"HM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"HN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"HR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"HT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"HU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"ID", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"IE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_COUNTY, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"IL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"IM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"IN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"IO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"IQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"IR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"IS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"IT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"JE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"JM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PARISH, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"JO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"JP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PREFECTURE, - ADDRESS_REQUIRES_LINE1_STATE_ZIP } }, - {"KE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"KG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"KH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"KI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_ISLAND, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"KM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"KN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_ISLAND, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"KP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"KR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"KW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"KY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_ISLAND, - ADDRESS_REQUIRES_LINE1_STATE } }, - {"KZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"LA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"LB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"LC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"LI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"LK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"LR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"LS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"LT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"LU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"LV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"LY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"MA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"ME", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MH", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"MK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"ML", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"MM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"MO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1 } }, - {"MP", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"MQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"MS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"MT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"MX", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"MZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"NA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"NC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"NE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"NF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"NG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"NI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_DEPARTMENT, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"NL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"NO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"NP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"NR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_DISTRICT, - ADDRESS_REQUIRES_LINE1_STATE } }, - {"NU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"NZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"OM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"PA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"PE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"PF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_ISLAND, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"PG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"PH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"PK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"PL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"PM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"PN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"PR", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"PS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"PT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"PW", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"PY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"QA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"RE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"RO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"RS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"RU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"RW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"SA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"SB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"SC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_ISLAND, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"SE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"SG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_ZIP } }, - {"SH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"SI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"SJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"SK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"SL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"SM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_ZIP } }, - {"SN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"SO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"SR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"SS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"ST", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"SV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"SX", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"SZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"TA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"TC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"TD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"TF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"TG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"TH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"TJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"TK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"TL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"TM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"TN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"TO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"TR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"TT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"TV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_ISLAND, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"TW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_COUNTY, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"TZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"UA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"UG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"UM", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"US", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"UY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"UZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"VA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"VC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"VE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_STATE } }, - {"VG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1 } }, - {"VI", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE, - IDS_AUTOFILL_FIELD_LABEL_STATE, - ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP } }, - {"VN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"VU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"WF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"WS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"XK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"YE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"YT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"ZA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY_ZIP } }, - {"ZM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - {"ZW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE, - ADDRESS_REQUIRES_LINE1_CITY } }, - // clang-format on +const StaticCountryAddressImportRequirementsData + kCountryAddressImportRequirementsData[] = { + {"AC", ADDRESS_REQUIRES_LINE1_CITY}, + {"AD", ADDRESS_REQUIRES_LINE1}, + {"AE", ADDRESS_REQUIRES_LINE1_STATE}, + {"AF", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"AG", ADDRESS_REQUIRES_LINE1}, + {"AI", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"AL", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"AM", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"AO", ADDRESS_REQUIRES_LINE1_CITY}, + {"AQ", ADDRESS_REQUIRES_LINE1_CITY}, + {"AR", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"AS", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"AT", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"AU", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"AW", ADDRESS_REQUIRES_LINE1_CITY}, + {"AX", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"AZ", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"BA", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"BB", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"BD", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"BE", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"BF", ADDRESS_REQUIRES_LINE1_CITY}, + {"BG", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"BH", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"BI", ADDRESS_REQUIRES_LINE1_CITY}, + {"BJ", ADDRESS_REQUIRES_LINE1_CITY}, + {"BL", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"BM", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"BN", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"BO", ADDRESS_REQUIRES_LINE1_CITY}, + {"BQ", ADDRESS_REQUIRES_LINE1_CITY}, + {"BR", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"BS", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"BT", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"BV", ADDRESS_REQUIRES_LINE1_CITY}, + {"BW", ADDRESS_REQUIRES_LINE1_CITY}, + {"BY", ADDRESS_REQUIRES_LINE1_CITY}, + {"BZ", ADDRESS_REQUIRES_LINE1_CITY}, + {"CA", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"CC", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"CD", ADDRESS_REQUIRES_LINE1_CITY}, + {"CF", ADDRESS_REQUIRES_LINE1_CITY}, + {"CG", ADDRESS_REQUIRES_LINE1_CITY}, + {"CH", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"CI", ADDRESS_REQUIRES_LINE1_CITY}, + {"CK", ADDRESS_REQUIRES_LINE1_CITY}, + {"CL", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"CM", ADDRESS_REQUIRES_LINE1_CITY}, + {"CN", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"CO", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"CR", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"CS", ADDRESS_REQUIRES_LINE1}, + {"CV", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"CW", ADDRESS_REQUIRES_LINE1_CITY}, + {"CX", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"CY", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"CZ", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"DE", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"DJ", ADDRESS_REQUIRES_LINE1_CITY}, + {"DK", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"DM", ADDRESS_REQUIRES_LINE1_CITY}, + {"DO", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"DZ", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"EC", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"EE", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"EG", ADDRESS_REQUIRES_LINE1_CITY}, + {"EH", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"ER", ADDRESS_REQUIRES_LINE1_CITY}, + {"ES", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"ET", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"FI", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"FJ", ADDRESS_REQUIRES_LINE1_CITY}, + {"FK", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"FM", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"FO", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"FR", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GA", ADDRESS_REQUIRES_LINE1_CITY}, + {"GB", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GD", ADDRESS_REQUIRES_LINE1_CITY}, + {"GE", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GF", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GG", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GH", ADDRESS_REQUIRES_LINE1_CITY}, + {"GI", ADDRESS_REQUIRES_LINE1}, + {"GL", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GM", ADDRESS_REQUIRES_LINE1_CITY}, + {"GN", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GP", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GQ", ADDRESS_REQUIRES_LINE1_CITY}, + {"GR", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GS", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GT", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GU", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GW", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"GY", ADDRESS_REQUIRES_LINE1_CITY}, + {"HK", ADDRESS_REQUIRES_LINE1_STATE}, + {"HM", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"HN", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"HR", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"HT", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"HU", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"ID", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"IE", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"IL", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"IM", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"IN", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"IO", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"IQ", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"IR", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"IS", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"IT", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"JE", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"JM", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"JO", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"JP", ADDRESS_REQUIRES_LINE1_STATE_ZIP}, + {"KE", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"KG", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"KH", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"KI", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"KM", ADDRESS_REQUIRES_LINE1_CITY}, + {"KN", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"KP", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"KR", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"KW", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"KY", ADDRESS_REQUIRES_LINE1_STATE}, + {"KZ", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"LA", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"LB", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"LC", ADDRESS_REQUIRES_LINE1_CITY}, + {"LI", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"LK", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"LR", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"LS", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"LT", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"LU", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"LV", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"LY", ADDRESS_REQUIRES_LINE1_CITY}, + {"MA", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MC", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MD", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"ME", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MF", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MG", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MH", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"MK", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"ML", ADDRESS_REQUIRES_LINE1_CITY}, + {"MM", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MN", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"MO", ADDRESS_REQUIRES_LINE1}, + {"MP", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"MQ", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MR", ADDRESS_REQUIRES_LINE1_CITY}, + {"MS", ADDRESS_REQUIRES_LINE1_CITY}, + {"MT", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MU", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MV", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MW", ADDRESS_REQUIRES_LINE1_CITY}, + {"MX", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MY", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"MZ", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"NA", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"NC", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"NE", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"NF", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"NG", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"NI", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"NL", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"NO", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"NP", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"NR", ADDRESS_REQUIRES_LINE1_STATE}, + {"NU", ADDRESS_REQUIRES_LINE1_CITY}, + {"NZ", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"OM", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"PA", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"PE", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"PF", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"PG", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"PH", ADDRESS_REQUIRES_LINE1_CITY}, + {"PK", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"PL", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"PM", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"PN", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"PR", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"PS", ADDRESS_REQUIRES_LINE1_CITY}, + {"PT", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"PW", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"PY", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"QA", ADDRESS_REQUIRES_LINE1_CITY}, + {"RE", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"RO", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"RS", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"RU", ADDRESS_REQUIRES_LINE1_CITY}, + {"RW", ADDRESS_REQUIRES_LINE1_CITY}, + {"SA", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"SB", ADDRESS_REQUIRES_LINE1_CITY}, + {"SC", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"SE", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"SG", ADDRESS_REQUIRES_LINE1_ZIP}, + {"SH", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"SI", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"SJ", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"SK", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"SL", ADDRESS_REQUIRES_LINE1_CITY}, + {"SM", ADDRESS_REQUIRES_LINE1_ZIP}, + {"SN", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"SO", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"SR", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"SS", ADDRESS_REQUIRES_LINE1_CITY}, + {"ST", ADDRESS_REQUIRES_LINE1_CITY}, + {"SV", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"SX", ADDRESS_REQUIRES_LINE1_CITY}, + {"SZ", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"TA", ADDRESS_REQUIRES_LINE1_CITY}, + {"TC", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"TD", ADDRESS_REQUIRES_LINE1_CITY}, + {"TF", ADDRESS_REQUIRES_LINE1_CITY}, + {"TG", ADDRESS_REQUIRES_LINE1_CITY}, + {"TH", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"TJ", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"TK", ADDRESS_REQUIRES_LINE1_CITY}, + {"TL", ADDRESS_REQUIRES_LINE1_CITY}, + {"TM", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"TN", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"TO", ADDRESS_REQUIRES_LINE1_CITY}, + {"TR", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"TT", ADDRESS_REQUIRES_LINE1_CITY}, + {"TV", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"TW", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"TZ", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"UA", ADDRESS_REQUIRES_LINE1_CITY}, + {"UG", ADDRESS_REQUIRES_LINE1_CITY}, + {"UM", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"US", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"UY", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"UZ", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"VA", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"VC", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"VE", ADDRESS_REQUIRES_LINE1_CITY_STATE}, + {"VG", ADDRESS_REQUIRES_LINE1}, + {"VI", ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP}, + {"VN", ADDRESS_REQUIRES_LINE1_CITY}, + {"VU", ADDRESS_REQUIRES_LINE1_CITY}, + {"WF", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"WS", ADDRESS_REQUIRES_LINE1_CITY}, + {"XK", ADDRESS_REQUIRES_LINE1_CITY}, + {"YE", ADDRESS_REQUIRES_LINE1_CITY}, + {"YT", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"ZA", ADDRESS_REQUIRES_LINE1_CITY_ZIP}, + {"ZM", ADDRESS_REQUIRES_LINE1_CITY}, + {"ZW", ADDRESS_REQUIRES_LINE1_CITY}, }; // GetCountryCodes and GetCountryData compute the data for CountryDataMap -// based on |kCountryData|. +// based on |kCountryAddressImportRequirementsData|. std::vector<std::string> GetCountryCodes() { std::vector<std::string> country_codes; - country_codes.reserve(base::size(kCountryData)); - for (const auto& static_data : kCountryData) { + country_codes.reserve(base::size(kCountryAddressImportRequirementsData)); + for (const auto& static_data : kCountryAddressImportRequirementsData) { country_codes.push_back(static_data.country_code); } return country_codes; } -std::map<std::string, CountryData> GetCountryDataMap() { - std::map<std::string, CountryData> country_data; +std::map<std::string, RequiredFieldsForAddressImport> GetCountryDataMap() { + std::map<std::string, RequiredFieldsForAddressImport> import_requirements; // Add all the countries we have explicit data for. - for (const auto& static_data : kCountryData) { - country_data.insert( - std::make_pair(static_data.country_code, static_data.country_data)); + for (const auto& static_data : kCountryAddressImportRequirementsData) { + import_requirements.insert( + import_requirements.end(), + std::make_pair(static_data.country_code, + static_data.address_import_field_requirements)); } // Add any other countries that ICU knows about, falling back to default data @@ -814,14 +315,13 @@ std::map<std::string, CountryData> GetCountryDataMap() { for (const char* const* country_pointer = icu::Locale::getISOCountries(); *country_pointer; ++country_pointer) { std::string country_code = *country_pointer; - if (!country_data.count(country_code)) { - CountryData data = {IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE, - IDS_AUTOFILL_FIELD_LABEL_PROVINCE}; - country_data.insert( - std::make_pair(std::move(country_code), std::move(data))); + if (!import_requirements.count(country_code)) { + import_requirements.insert(std::make_pair( + std::move(country_code), + RequiredFieldsForAddressImport::ADDRESS_REQUIREMENTS_UNKNOWN)); } } - return country_data; + return import_requirements; } std::map<std::string, std::string> GetCountryCodeAliasMap() { @@ -844,23 +344,25 @@ CountryDataMap* CountryDataMap::GetInstance() { } CountryDataMap::CountryDataMap() - : country_data_(GetCountryDataMap()), + : required_fields_for_address_import_map_(GetCountryDataMap()), country_code_aliases_(GetCountryCodeAliasMap()), country_codes_(GetCountryCodes()) {} CountryDataMap::~CountryDataMap() = default; -bool CountryDataMap::HasCountryData(const std::string& country_code) const { - return country_data_.count(country_code) > 0; +bool CountryDataMap::HasRequiredFieldsForAddressImport( + const std::string& country_code) const { + return required_fields_for_address_import_map_.count(country_code) > 0; } -const CountryData& CountryDataMap::GetCountryData( +RequiredFieldsForAddressImport +CountryDataMap::GetRequiredFieldsForAddressImport( const std::string& country_code) const { - auto lookup = country_data_.find(country_code); - if (lookup != country_data_.end()) + auto lookup = required_fields_for_address_import_map_.find(country_code); + if (lookup != required_fields_for_address_import_map_.end()) return lookup->second; // If there is no entry for country_code return the entry for the US. - return country_data_.find("US")->second; + return required_fields_for_address_import_map_.find("US")->second; } bool CountryDataMap::HasCountryCodeAlias( @@ -872,7 +374,7 @@ const std::string CountryDataMap::GetCountryCodeForAlias( const std::string& country_code_alias) const { auto lookup = country_code_aliases_.find(country_code_alias); if (lookup != country_code_aliases_.end()) { - DCHECK(HasCountryData(lookup->second)); + DCHECK(HasRequiredFieldsForAddressImport(lookup->second)); return lookup->second; } return std::string(); diff --git a/chromium/components/autofill/core/browser/geo/country_data.h b/chromium/components/autofill/core/browser/geo/country_data.h index d30783a6276..12ae5f8cf17 100644 --- a/chromium/components/autofill/core/browser/geo/country_data.h +++ b/chromium/components/autofill/core/browser/geo/country_data.h @@ -20,7 +20,7 @@ namespace autofill { // The minimal required fields for an address to be complete for a given // country. -enum AddressRequiredFields { +enum RequiredFieldsForAddressImport { ADDRESS_REQUIRES_CITY = 1 << 0, ADDRESS_REQUIRES_STATE = 1 << 1, ADDRESS_REQUIRES_ZIP = 1 << 2, @@ -52,19 +52,6 @@ enum AddressRequiredFields { ADDRESS_REQUIREMENTS_UNKNOWN = ADDRESS_REQUIRES_LINE1_CITY_STATE_ZIP, }; -// This struct describes the address format typical for a particular country. -struct CountryData { - // Resource identifier for the string used to denote postal codes. - int postal_code_label_id; - - // Resource identifier for the string used to denote the major subdivision - // below the "country" level. - int state_label_id; - - // The required parts of the address. - AddressRequiredFields address_required_fields; -}; - // A singleton class that encapsulates a map from country codes to country data. class CountryDataMap { public: @@ -72,7 +59,7 @@ class CountryDataMap { // Returns true if a |CountryData| entry for the supplied |country_code| // exists. - bool HasCountryData(const std::string& country_code) const; + bool HasRequiredFieldsForAddressImport(const std::string& country_code) const; // Returns true if there is a country code alias for |country_code|. bool HasCountryCodeAlias(const std::string& country_code_alias) const; @@ -82,9 +69,10 @@ class CountryDataMap { const std::string GetCountryCodeForAlias( const std::string& country_code_alias) const; - // Lookup the |CountryData| for the supplied |country_code|. If no entry - // exists, return the data for the US as a best guess. - const CountryData& GetCountryData(const std::string& country_code) const; + // Lookup the |RequiredFieldForAddressImport| for the supplied |country_code|. + // If no entry exists, return requirements for the US as a best guess. + RequiredFieldsForAddressImport GetRequiredFieldsForAddressImport( + const std::string& country_code) const; // Return a constant reference to a vector of all country codes. const std::vector<std::string>& country_codes() { return country_codes_; } @@ -94,7 +82,8 @@ class CountryDataMap { ~CountryDataMap(); friend struct base::DefaultSingletonTraits<CountryDataMap>; - const std::map<std::string, CountryData> country_data_; + const std::map<std::string, RequiredFieldsForAddressImport> + required_fields_for_address_import_map_; const std::map<std::string, std::string> country_code_aliases_; const std::vector<std::string> country_codes_; diff --git a/chromium/components/autofill/core/browser/geo/subkey_requester.cc b/chromium/components/autofill/core/browser/geo/subkey_requester.cc index 5bbc0dda70f..0eb5da274bc 100644 --- a/chromium/components/autofill/core/browser/geo/subkey_requester.cc +++ b/chromium/components/autofill/core/browser/geo/subkey_requester.cc @@ -8,7 +8,7 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/cancelable_callback.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/sequenced_task_runner_handle.h" diff --git a/chromium/components/autofill/core/browser/logging/log_buffer_submitter_unittest.cc b/chromium/components/autofill/core/browser/logging/log_buffer_submitter_unittest.cc index b167d01018f..506fadb82f0 100644 --- a/chromium/components/autofill/core/browser/logging/log_buffer_submitter_unittest.cc +++ b/chromium/components/autofill/core/browser/logging/log_buffer_submitter_unittest.cc @@ -4,8 +4,8 @@ #include "components/autofill/core/browser/logging/log_buffer_submitter.h" -#include "base/bind_helpers.h" #include "base/callback.h" +#include "base/callback_helpers.h" #include "base/values.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/browser/logging/log_receiver.h" diff --git a/chromium/components/autofill/core/browser/logging/log_manager_unittest.cc b/chromium/components/autofill/core/browser/logging/log_manager_unittest.cc index e9c2ab752f4..19806be5c00 100644 --- a/chromium/components/autofill/core/browser/logging/log_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/logging/log_manager_unittest.cc @@ -5,7 +5,7 @@ #include "components/autofill/core/browser/logging/log_manager.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/macros.h" #include "components/autofill/core/browser/logging/log_receiver.h" #include "components/autofill/core/browser/logging/log_router.h" diff --git a/chromium/components/autofill/core/browser/metrics/credit_card_form_event_logger.cc b/chromium/components/autofill/core/browser/metrics/credit_card_form_event_logger.cc index 95e479a4740..ecae251fd12 100644 --- a/chromium/components/autofill/core/browser/metrics/credit_card_form_event_logger.cc +++ b/chromium/components/autofill/core/browser/metrics/credit_card_form_event_logger.cc @@ -13,7 +13,6 @@ #include "components/autofill/core/browser/form_data_importer.h" #include "components/autofill/core/browser/payments/credit_card_access_manager.h" #include "components/autofill/core/browser/validation.h" -#include "components/autofill/core/common/autofill_tick_clock.h" namespace autofill { @@ -50,19 +49,6 @@ void CreditCardFormEventLogger::OnDidSelectCardSuggestion( AutofillSyncSigninState sync_state) { sync_state_ = sync_state; - // When server nicknames are available, if any card is selected, log the - // selection duration. - if (has_server_nickname_ && !has_logged_suggestion_selected_timestamp_) { - has_logged_suggestion_selected_timestamp_ = true; - base::TimeTicks now = AutofillTickClock::NowTicks(); - // Suggestion selection should always chronologically follow suggestion - // shown. - DCHECK(now > first_suggestion_shown_timestamp_); - base::UmaHistogramMediumTimes( - "Autofill.FormEvents.CreditCard.WithServerNickname.SelectionDuration", - now - first_suggestion_shown_timestamp_); - } - if (has_eligible_offer_) { card_selected_has_offer_ = DoesCardHaveOffer(credit_card); base::UmaHistogramBoolean("Autofill.Offer.SelectedCardHasOffer", @@ -189,8 +175,6 @@ void CreditCardFormEventLogger::LogUkmInteractedWithForm( } void CreditCardFormEventLogger::OnSuggestionsShownOnce() { - // Record the timestamp of the first suggestion shown. - first_suggestion_shown_timestamp_ = AutofillTickClock::NowTicks(); base::UmaHistogramBoolean("Autofill.Offer.SuggestedCardsHaveOffer", has_eligible_offer_); } @@ -214,14 +198,6 @@ void CreditCardFormEventLogger::OnLog(const std::string& name, NUM_FORM_EVENTS); } - // Log a different histogram for credit card forms with server nickname - // available so that selection rate with server nickname can be compared on - // their own. - if (has_server_nickname_) { - base::UmaHistogramEnumeration(name + ".WithServerNickname", event, - NUM_FORM_EVENTS); - } - // Log a different histogram for credit card forms with credit card offers // available so that selection rate with offers and rewards can be compared on // their own. diff --git a/chromium/components/autofill/core/browser/metrics/credit_card_form_event_logger.h b/chromium/components/autofill/core/browser/metrics/credit_card_form_event_logger.h index a84566a1b3f..4fb93962afc 100644 --- a/chromium/components/autofill/core/browser/metrics/credit_card_form_event_logger.h +++ b/chromium/components/autofill/core/browser/metrics/credit_card_form_event_logger.h @@ -46,10 +46,6 @@ class CreditCardFormEventLogger : public FormEventLoggerBase { is_context_secure_ = is_context_secure; } - void set_has_server_nickname(bool has_server_nickname) { - has_server_nickname_ = has_server_nickname; - } - void set_suggestions(std::vector<Suggestion> suggestions); void OnDidSelectCardSuggestion(const CreditCard& credit_card, @@ -97,18 +93,11 @@ class CreditCardFormEventLogger : public FormEventLoggerBase { bool is_context_secure_ = false; UnmaskAuthFlowType current_authentication_flow_; bool has_logged_masked_server_card_suggestion_selected_ = false; - bool has_logged_suggestion_selected_timestamp_ = false; bool logged_suggestion_filled_was_masked_server_card_ = false; - base::TimeTicks first_suggestion_shown_timestamp_; std::vector<Suggestion> suggestions_; bool has_eligible_offer_ = false; bool card_selected_has_offer_ = false; - // True when ANY of the masked server cards has a nickname. Note that, - // depending on the experimental setup, the user may not be shown the - // nickname. - bool has_server_nickname_ = false; - // Weak references. PersonalDataManager* personal_data_manager_; AutofillClient* client_; diff --git a/chromium/components/autofill/core/browser/pattern_provider/DEPS b/chromium/components/autofill/core/browser/pattern_provider/DEPS new file mode 100644 index 00000000000..89e4cd8553f --- /dev/null +++ b/chromium/components/autofill/core/browser/pattern_provider/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+components/grit/components_resources.h", + "+services/data_decoder/public/cpp:cpp", + "+services/data_decoder/public", +] 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 new file mode 100644 index 00000000000..3aaeb3d97a9 --- /dev/null +++ b/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc @@ -0,0 +1,228 @@ +// 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/pattern_configuration_parser.h" + +#include "base/bind.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/grit/components_resources.h" +#include "ui/base/resource/resource_bundle.h" + +namespace autofill { + +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"; +const char kMatchFieldAttributesKey[] = "match_field_attributes"; +const char kMatchFieldInputTypesKey[] = "match_field_input_types"; +const char kVersionKey[] = "version"; + +bool ParseMatchingPattern(PatternProvider::Map& patterns, + const std::string& field_type, + const std::string& 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 = + value.FindStringKey(kNegativePatternKey); + base::Optional<double> positive_score = + value.FindDoubleKey(kPositiveScoreKey); + base::Optional<int> match_field_attributes = + value.FindIntKey(kMatchFieldAttributesKey); + base::Optional<int> match_field_input_types = + value.FindIntKey(kMatchFieldInputTypesKey); + + if (!pattern_identifier || !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.match_field_attributes = match_field_attributes.value(); + new_pattern.match_field_input_types = match_field_input_types.value(); + new_pattern.language = language; + + // Shift to the right to match the MatchFieldTypes enum, which temporarily + // starts at 1<<2 instead of 1<<0. + new_pattern.match_field_input_types <<= 2; + + 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 << "|."; + + 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 +// 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. + if (!result.value) { + DVLOG(1) << "Failed to parse PatternProvider configuration JSON string."; + std::move(done_callback).Run(); + return; + } + + base::Version version = ExtractVersionFromJsonObject(result.value.value()); + base::Optional<PatternProvider::Map> patterns = + GetConfigurationFromJsonObject(result.value.value()); + + 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); + } else { + DVLOG(1) << "Failed to parse PatternProvider configuration JSON object."; + } + + std::move(done_callback).Run(); +} + +} // namespace + +base::Optional<PatternProvider::Map> GetConfigurationFromJsonObject( + const base::Value& root) { + PatternProvider::Map patterns; + + if (!root.is_dict()) { + DVLOG(1) << "JSON object is not a dictionary."; + return base::nullopt; + } + + for (const auto& kv : root.DictItems()) { + const std::string& field_type = kv.first; + const base::Value* field_type_dict = &kv.second; + + if (!field_type_dict->is_dict()) { + DVLOG(1) << "|" << field_type << "| does not contain a dictionary."; + return base::nullopt; + } + + for (const auto& value : field_type_dict->DictItems()) { + const std::string& language = value.first; + const base::Value* inner_list = &value.second; + + if (!inner_list->is_list()) { + DVLOG(1) << "Language |" << language << "| in |" << field_type + << "| does not contain a list."; + return base::nullopt; + } + + for (const auto& matchingPatternObj : inner_list->GetList()) { + bool success = ParseMatchingPattern(patterns, field_type, language, + matchingPatternObj); + if (!success) { + DVLOG(1) << "Found incorrect |MatchingPattern| object in list |" + << field_type << "|, language |" << language << "|."; + return base::nullopt; + } + } + } + } + + return base::make_optional(patterns); +} + +base::Version ExtractVersionFromJsonObject(base::Value& root) { + if (!root.is_dict()) + return base::Version("0"); + + base::Optional<base::Value> version_str = root.ExtractKey(kVersionKey); + if (!version_str || !version_str.value().is_string()) + return base::Version("0"); + + base::Version version = base::Version(version_str.value().GetString()); + if (!version.IsValid()) + return base::Version("0"); + + return version; +} + +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))); +} + +base::Optional<PatternProvider::Map> +GetPatternsFromResourceBundleSynchronously() { + if (!ui::ResourceBundle::HasSharedInstance()) { + VLOG(1) << "Resource Bundle unavailable to load Autofill Matching Pattern " + "definitions."; + return base::nullopt; + } + + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + std::string resource_string = + bundle.LoadDataResourceString(IDR_AUTOFILL_REGEX_JSON); + base::Optional<base::Value> json_object = + base::JSONReader::Read(resource_string); + + // Discard version, since this is the only getter used in unit tests. + base::Version version = ExtractVersionFromJsonObject(json_object.value()); + base::Optional<PatternProvider::Map> configuration_map = + GetConfigurationFromJsonObject(json_object.value()); + + return configuration_map; +} + +} // namespace field_type_parsing + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h b/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h new file mode 100644 index 00000000000..8f72b18bf31 --- /dev/null +++ b/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h @@ -0,0 +1,60 @@ +// 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_PATTERN_CONFIGURATION_PARSER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_CONFIGURATION_PARSER_H_ + +#include <string> + +#include "base/json/json_reader.h" +#include "base/version.h" +#include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h" +#include "components/autofill/core/browser/pattern_provider/pattern_provider.h" +#include "services/data_decoder/public/cpp/data_decoder.h" + +namespace autofill { + +namespace field_type_parsing { + +// Tries to extract the configuration version from the JSON base::Value tree. +// This removes the key if found, so that validation is easier later on. +// If not found, default to version 0. +base::Version ExtractVersionFromJsonObject(base::Value& root); + +// Transforms the parsed JSON base::Value tree into the map used in +// |PatternProvider|. Requires the version key to already be extracted. +// The root is expected to be a dictionary with keys corresponding to +// strings representing |ServerFieldType|. Then there should be +// second level dictionaries with keys describing the language. These +// should point to a list of objects representing |MatchingPattern|. +// { +// "FIELD_NAME": { +// "language":[ +// {MatchingPatternFields} +// ] +// } +// } +// An example can be found in the relative resources folder. +base::Optional<PatternProvider::Map> GetConfigurationFromJsonObject( + const base::Value& root); + +// Tries to get and parse the default configuration in the resource bundle +// into a valid map used in |PatternProvider| and swap it in for further use. +// The callback is used as a signal for testing. +void PopulateFromResourceBundle( + base::OnceClosure done_callback = base::DoNothing::Once()); + +// Tries to parse the given JSON string into a valid map used in the +// |PatternProvider| and swap it in for further use. +void PopulateFromJsonString(std::string json_string); + +// Synchronous getter used to set up a test fixture. +base::Optional<PatternProvider::Map> +GetPatternsFromResourceBundleSynchronously(); + +} // namespace field_type_parsing + +} // namespace autofill + +#endif 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 new file mode 100644 index 00000000000..a9cd570a644 --- /dev/null +++ b/chromium/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc @@ -0,0 +1,196 @@ +// 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/pattern_configuration_parser.h" + +#include <stddef.h> + +#include "base/json/json_reader.h" +#include "base/test/gtest_util.h" +#include "base/version.h" +#include "components/grit/components_resources.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/resource/resource_bundle.h" + +namespace autofill { + +namespace field_type_parsing { + +// Test that the |base::Value| object of the configuration is +// parsed to the map structure used by |PatternProvider| as +// expected, given the input is valid. +TEST(PatternConfigurationParserTest, WellFormedParsedCorrectly) { + std::string JSON_message = R"( + { + "version": "1.0", + "FULL_NAME": { + "en_us": [ + { + "pattern_identifier": "Name_en", + "positive_pattern": "name|full name", + "positive_score": 2.0, + "negative_pattern": "company", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + ], + "fr": [ + { + "pattern_identifier": "Name_fr", + "positive_pattern": "nom|prenom", + "positive_score": 2.0, + "negative_pattern": "compagne", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + ] + }, + "ADDRESS": { + "en_us": [ + { + "pattern_identifier": "Address", + "positive_pattern": "address", + "positive_score": 2.0, + "negative_pattern": "email", + "match_field_attributes": 4, + "match_field_input_types": 3 + } + ] + } + })"; + base::Optional<base::Value> JSON_object = + base::JSONReader::Read(JSON_message); + + ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + + base::Version version = ExtractVersionFromJsonObject(JSON_object.value()); + base::Optional<PatternProvider::Map> optional_patterns = + GetConfigurationFromJsonObject(JSON_object.value()); + + ASSERT_TRUE(version.IsValid()); + ASSERT_TRUE(optional_patterns); + + ASSERT_EQ(base::Version("1.0"), version); + + PatternProvider::Map patterns = optional_patterns.value(); + + 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.count("ADDRESS")); + ASSERT_EQ(1U, patterns["ADDRESS"].size()); + ASSERT_TRUE(patterns["ADDRESS"].count("en_us")); + + // Test one |MatchingPattern| to check that they are parsed correctly. + MatchingPattern* pattern = &patterns["FULL_NAME"]["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_NEAR(2.0, pattern->positive_score, 1e-6); + ASSERT_EQ(2, pattern->match_field_attributes); + ASSERT_EQ(3 << 2, pattern->match_field_input_types); +} + +// Test that the parser does not return anything if some |MatchingPattern| +// object is missing a property. +TEST(PatternConfigurationParserTest, MalformedMissingProperty) { + std::string JSON_message = R"( + { + "version": "1.0", + "FULL_NAME": { + "en_us": [ + { + "pattern_identifier": "Name_en", + "positive_pattern": "name|full name", + "positive_score": 2.0, + "negative_pattern": "company", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + ], + "fr": [ + { + "pattern_identifier": "Name_fr", + "positive_pattern": "nom|prenom", + "negative_pattern": "compagne", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + ] + } + })"; + base::Optional<base::Value> JSON_object = + base::JSONReader::Read(JSON_message); + + ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + + base::Optional<PatternProvider::Map> optional_patterns = + GetConfigurationFromJsonObject(JSON_object.value()); + + ASSERT_FALSE(optional_patterns); +} + +// 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"( + { + "FULL_NAME": { + "en_us": [ + { + "pattern_identifier": "Name_en", + "positive_pattern": "name|full name", + "positive_score": 2.0, + "negative_pattern": "company", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + ] + } + })"; + base::Optional<base::Value> JSON_object = + base::JSONReader::Read(JSON_message); + + ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + + base::Version version = ExtractVersionFromJsonObject(JSON_object.value()); + + ASSERT_EQ(base::Version("0"), version); +} + +// 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"( + { + "FULL_NAME": { + "en_us": { + "pattern_identifier": "Name_en", + "positive_pattern": "name|full name", + "positive_score": 2.0, + "negative_pattern": "company", + "match_field_attributes": 2, + "match_field_input_types": 3 + } + } + })"; + base::Optional<base::Value> JSON_object = + base::JSONReader::Read(JSON_message); + + ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string."; + + base::Optional<PatternProvider::Map> optional_patterns = + GetConfigurationFromJsonObject(JSON_object.value()); + + ASSERT_FALSE(optional_patterns); +} + +} // namespace field_type_parsing + +} // namespace autofill 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 04a2d48bac1..349c0d8c39a 100644 --- a/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.cc +++ b/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.cc @@ -8,42 +8,171 @@ #include <iostream> #include <string> +#include "base/bind.h" +#include "base/feature_list.h" +#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/pattern_configuration_parser.h" +#include "components/autofill/core/common/autofill_features.h" namespace autofill { -PatternProvider::PatternProvider() { - auto& company_patterns = patterns_[AutofillType(COMPANY_NAME).ToString()]; - company_patterns["EN"].push_back(GetCompanyPatternEn()); - company_patterns["DE"].push_back(GetCompanyPatternDe()); + +namespace { +const char* kSourceCodeLanguage = "en"; + +// Adds the English patterns, restricted to MatchFieldType MATCH_NAME, to +// every other language. +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 = + p.second; + + auto it = lang_to_patterns.find(kSourceCodeLanguage); + if (it == lang_to_patterns.end()) + continue; + std::vector<MatchingPattern> en_patterns = it->second; + for (MatchingPattern& en_pattern : en_patterns) { + en_pattern.match_field_attributes = MATCH_NAME; + } + + for (auto& q : lang_to_patterns) { + const std::string& page_language = q.first; + std::vector<MatchingPattern>& patterns = q.second; + + if (page_language != kSourceCodeLanguage) { + patterns.insert(patterns.end(), en_patterns.begin(), en_patterns.end()); + } + } + } } -PatternProvider::~PatternProvider() { - patterns_.clear(); +// 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 = + p.second; + for (auto& q : lang_to_patterns) { + std::vector<MatchingPattern>& patterns = q.second; + std::sort(patterns.begin(), patterns.end(), + [](const MatchingPattern& mp1, const MatchingPattern& mp2) { + return mp1.positive_score > mp2.positive_score; + }); + } + } +} +} + +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(); + } + } + return *g_pattern_provider; } -void PatternProvider::SetPatterns( - const std::map<std::string, - std::map<std::string, std::vector<MatchingPattern>>>& - patterns) { - patterns_ = patterns; +// static +void PatternProvider::ResetPatternProvider() { + g_pattern_provider = nullptr; } -const std::vector<MatchingPattern>& PatternProvider::GetMatchPatterns( +PatternProvider::PatternProvider() = default; +PatternProvider::~PatternProvider() = default; + +void PatternProvider::SetPatterns(PatternProvider::Map patterns, + const base::Version version, + const bool overwrite_equal_version) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!pattern_version_.IsValid() || pattern_version_ < version || + (overwrite_equal_version && pattern_version_ == version)) { + patterns_ = patterns; + pattern_version_ = version; + EnrichPatternsWithEnVersion(&patterns_); + SortPatternsByScore(&patterns_); + } +} + +const std::vector<MatchingPattern> PatternProvider::GetMatchPatterns( const std::string& pattern_name, - const std::string& page_language) { - return patterns_[pattern_name][page_language]; + const std::string& 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)) { + auto outer_it = patterns_.find(pattern_name); + if (outer_it != patterns_.end()) { + const std::map<std::string, 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()) { + const std::vector<MatchingPattern>& patterns = inner_it->second; + if (!patterns.empty()) { + return patterns; + } + } + } + return GetAllPatternsByType(pattern_name); + } else if ( + base::FeatureList::IsEnabled( + features:: + kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics)) { + return GetAllPatternsByType(pattern_name); + } else { + return {}; + } } -const std::vector<MatchingPattern>& PatternProvider::GetMatchPatterns( +const std::vector<MatchingPattern> PatternProvider::GetMatchPatterns( ServerFieldType type, - const std::string& page_language) { + const std::string& page_language) const { std::string pattern_name = AutofillType(type).ToString(); return GetMatchPatterns(pattern_name, page_language); } -PatternProvider* PatternProvider::getInstance() { - static base::NoDestructor<PatternProvider> instance; - return instance.get(); +const std::vector<MatchingPattern> PatternProvider::GetAllPatternsByType( + ServerFieldType type) const { + std::string type_str = AutofillType(type).ToString(); + return GetAllPatternsByType(type_str); +} + +const std::vector<MatchingPattern> PatternProvider::GetAllPatternsByType( + const std::string& type) const { + auto it = patterns_.find(type); + if (it == patterns_.end()) + return {}; + const std::map<std::string, 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 std::vector<MatchingPattern>& language_patterns = p.second; + for (const MatchingPattern& mp : language_patterns) { + if (page_language == kSourceCodeLanguage || + mp.language != kSourceCodeLanguage) { + all_language_patterns.push_back(mp); + } + } + } + return all_language_patterns; } } // namespace autofill 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 2cb7422023b..4ddd9cf6469 100644 --- a/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.h +++ b/chromium/components/autofill/core/browser/pattern_provider/pattern_provider.h @@ -7,52 +7,91 @@ #include <string> +#include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/no_destructor.h" +#include "base/sequence_checker.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/autofill_regex_constants.h" -#include "third_party/re2/src/re2/re2.h" namespace autofill { +// Base class for the Pattern Provider. This class contains the implementation +// for providing the matching patterns. Different subclasses provide different +// ways to load the data in for further use. class PatternProvider { public: - static PatternProvider* getInstance(); + // 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>>>; - // Setter for loaded patterns from external storage. - void SetPatterns( - const std::map<std::string, - std::map<std::string, std::vector<MatchingPattern>>>& - patterns); + // Returns a reference to the global Pattern Provider. + static PatternProvider& GetInstance(); - // Provides us with all patterns that can match our field type and page - // language. - const std::vector<MatchingPattern>& GetMatchPatterns( + // Setter for loading patterns from external storage. + void SetPatterns(const Map patterns, + const base::Version version, + const bool overwrite_equal_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 std::string& page_language) const; - const std::vector<MatchingPattern>& GetMatchPatterns( + // 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 std::string& page_language) const; - // Provides us with all patterns that can match our field type. - const std::vector<MatchingPattern>& GetAllPatternsBaseOnType( - ServerFieldType type); + // Find all patterns, across all languages, for a given server field |type|. + const std::vector<MatchingPattern> GetAllPatternsByType( + ServerFieldType type) const; + + // Find all patterns, across all languages, for a given server field |type|. + const std::vector<MatchingPattern> GetAllPatternsByType( + 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(); - private: PatternProvider(); ~PatternProvider(); - // Func to sort the incoming map by score. - void SortPatternsByScore(std::vector<MatchingPattern>& patterns); + const Map& patterns() const { return patterns_; } + + private: + FRIEND_TEST_ALL_PREFIXES(AutofillPatternProviderPipelineTest, + TestParsingEquivalent); + FRIEND_TEST_ALL_PREFIXES(AutofillPatternProviderPipelineTest, + DefaultPatternProviderLoads); + + 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. + SEQUENCE_CHECKER(sequence_checker_); // Local map to store a vector of patterns keyed by field type and // page language. - std::map<std::string, std::map<std::string, std::vector<MatchingPattern>>> - patterns_; + Map patterns_; - friend class base::NoDestructor<PatternProvider>; + // Version for keeping track which pattern set is currently used. + base::Version pattern_version_; }; } // namespace autofill -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_PROVIDER_H_
\ No newline at end of file +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PATTERN_PROVIDER_PATTERN_PROVIDER_H_ 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 7259a75d56e..5222b67d903 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 @@ -2,8 +2,6 @@ // 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/pattern_provider.h" - #include <stddef.h> #include <map> @@ -11,10 +9,83 @@ #include <vector> #include "base/test/gtest_util.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/browser/field_types.h" +#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 "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h" #include "testing/gtest/include/gtest/gtest.h" namespace autofill { +namespace { + +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.negative_pattern = ""; + m_p.match_field_attributes = MATCH_NAME; + m_p.match_field_input_types = MATCH_TEXT; + m_p.language = "en"; + 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.negative_pattern = ""; + m_p.match_field_attributes = MATCH_LABEL | MATCH_NAME; + m_p.match_field_input_types = MATCH_TEXT; + m_p.language = "de"; + return m_p; +} + +// Pattern Provider with custom values set for testing. +class UnitTestPatternProvider : public PatternProvider { + public: + UnitTestPatternProvider(); + UnitTestPatternProvider(const std::vector<MatchingPattern>& de_patterns, + const std::vector<MatchingPattern>& en_patterns); + ~UnitTestPatternProvider(); +}; + +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); +} + +UnitTestPatternProvider::~UnitTestPatternProvider() { + PatternProvider::ResetPatternProvider(); +} + +} // 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 && @@ -26,14 +97,165 @@ bool operator==(const MatchingPattern& mp1, const MatchingPattern& mp2) { } TEST(AutofillPatternProvider, Single_Match) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillUsePageLanguageToSelectFieldParsingPatterns); + MatchingPattern kCompanyPatternEn = GetCompanyPatternEn(); MatchingPattern kCompanyPatternDe = GetCompanyPatternDe(); - PatternProvider* pattern_provider = PatternProvider::getInstance(); + 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); +} + +// 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 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); + + 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(); + + TestPatternProvider test_pattern_provider; + + EXPECT_EQ(default_pattern_provider.patterns(), + test_pattern_provider.patterns()); +} + +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); +} + +TEST(AutofillPatternProvider, UnknownLanguages) { + { + base::test::ScopedFeatureList feature; + feature.InitWithFeatures( + // enabled + {features::kAutofillUsePageLanguageToSelectFieldParsingPatterns}, + // disabled + {features:: + kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics}); + UnitTestPatternProvider p; + EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", ""), + p.GetAllPatternsByType("COMPANY_NAME")); + EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", "blabla"), + p.GetAllPatternsByType("COMPANY_NAME")); + } + + { + base::test::ScopedFeatureList feature; + feature.InitWithFeatures( + // enabled + {features:: + kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics}, + // disabled + {features::kAutofillUsePageLanguageToSelectFieldParsingPatterns}); + UnitTestPatternProvider p; + EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", ""), + p.GetAllPatternsByType("COMPANY_NAME")); + EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", "blabla"), + p.GetAllPatternsByType("COMPANY_NAME")); + } +} + +TEST(AutofillPatternProvider, EnrichPatternsWithEnVersion) { + { + base::test::ScopedFeatureList feature; + feature.InitWithFeatures( + // enabled + {features::kAutofillUsePageLanguageToSelectFieldParsingPatterns}, + // disabled + {features:: + kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics}); + UnitTestPatternProvider p; + EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", "en"), + std::vector<MatchingPattern>{GetCompanyPatternEn()}); + EXPECT_EQ(p.GetMatchPatterns("COMPANY_NAME", "de"), + std::vector<MatchingPattern>( + {GetCompanyPatternDe(), GetCompanyPatternEn()})); + } + + { + base::test::ScopedFeatureList feature; + feature.InitWithFeatures( + // enabled + {features:: + kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics}, + // disabled + {features::kAutofillUsePageLanguageToSelectFieldParsingPatterns}); + 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()})); + } +} - ASSERT_TRUE(pattern_provider->GetMatchPatterns("COMPANY_NAME", "EN").size() > - 0); - EXPECT_EQ(pattern_provider->GetMatchPatterns("COMPANY_NAME", "EN")[0], - kCompanyPatternEn); +TEST(AutofillPatternProvider, SortPatternsByScore) { + base::test::ScopedFeatureList feature; + feature.InitWithFeatures( + // enabled + {features::kAutofillUsePageLanguageToSelectFieldParsingPatterns, + features::kAutofillApplyNegativePatternsForFieldTypeDetectionHeuristics}, + // disabled + {}); + std::vector<MatchingPattern> de_input_patterns; + de_input_patterns.push_back(GetCompanyPatternDe()); + de_input_patterns.push_back(GetCompanyPatternDe()); + de_input_patterns.push_back(GetCompanyPatternDe()); + de_input_patterns.push_back(GetCompanyPatternDe()); + de_input_patterns[0].positive_score = 3.0; + de_input_patterns[1].positive_score = 1.0; + de_input_patterns[2].positive_score = 5.0; + de_input_patterns[3].positive_score = 3.0; + UnitTestPatternProvider p(de_input_patterns, {}); + const std::vector<MatchingPattern>& de_patterns = + p.GetMatchPatterns(COMPANY_NAME, "de"); + 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); + EXPECT_EQ(de_patterns[2].positive_score, 3.0); + EXPECT_EQ(de_patterns[3].positive_score, 1.0); } -} // namespace autofill
\ No newline at end of file +} // namespace autofill 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 new file mode 100644 index 00000000000..16778eac24e --- /dev/null +++ b/chromium/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json @@ -0,0 +1,2947 @@ +{ + "ADDRESS_HOME_STREET_NAME": { + "en" : [ + { + "pattern_identifier": "en_street_name", + "positive_pattern": "street", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "de" : [ + { + "pattern_identifier": "de_street_name", + "positive_pattern": "stra(ss|ß)e", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ru" : [ + { + "pattern_identifier": "ru_street_name", + "positive_pattern": "улица|название.?улицы", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "pt" : [ + { + "pattern_identifier": "pt_street_name", + "positive_pattern": "rua|avenida|((?<!do |de )endereço)", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "ADDRESS_HOME_HOUSE_NUMBER":{ + "en": [ + { + "pattern_identifier": "en_house_number", + "positive_pattern": "(house.?|street.?|^)number", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "de": [ + { + "pattern_identifier": "de_house_number", + "positive_pattern": "(haus|^)nummer", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "pt": [ + { + "pattern_identifier": "pt_house_number", + "positive_pattern": "^\\*?.?número(.?\\*?$| da residência)", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ru": [ + { + "pattern_identifier": "ru_house_number", + "positive_pattern": "дом|номер.?дома", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "ATTENTION_IGNORED": { + "en": [ + { + "pattern_identifier": "en_attention_ignored_preserving", + "positive_pattern": "attention|attn", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "REGION_IGNORED": { + "en": [ + { + "pattern_identifier": "en_region_ignored_preserving", + "positive_pattern": "province|region|other", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_region_ignored_preserving", + "positive_pattern": "provincia", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "pt": [ + { + "pattern_identifier": "pt_region_ignored_preserving", + "positive_pattern": "bairro|suburb", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "ADDRESS_NAME_IGNORED": { + "en": [ + { + "pattern_identifier": "en_address_name_ignored_preserving", + "positive_pattern": "address.*nickname|address.*label", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "COMPANY": { + "en": [ + { + "pattern_identifier": "en_company_preserving", + "positive_pattern": "company|business|organization|organisation", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "de": [ + { + "pattern_identifier": "de_company_preserving", + "positive_pattern": "(?<!con)firma|firmenname", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_company_preserving", + "positive_pattern": "empresa", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_company_preserving", + "positive_pattern": "societe|société", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "it": [ + { + "pattern_identifier": "it_company_preserving", + "positive_pattern": "ragione.?sociale", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_company_preserving", + "positive_pattern": "会社", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ru": [ + { + "pattern_identifier": "ru_company_preserving", + "positive_pattern": "название.?компании", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "zh": [ + { + "pattern_identifier": "zh_company_preserving", + "positive_pattern": "单位|公司", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fa": [ + { + "pattern_identifier": "fa_company_preserving", + "positive_pattern": "شرکت", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ko": [ + { + "pattern_identifier": "ko_company_preserving", + "positive_pattern": "회사|직장", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "ADDRESS_LINE_1": { + "en": [ + { + "pattern_identifier": "en_address_line_1_preserving", + "positive_pattern": "^address$|address[_-]?line(one)?|address1|addr1|street|(?:shipping|billing)address$|house.?name", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "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_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ], + "de": [ + { + "pattern_identifier": "de_address_line_1_preserving", + "positive_pattern": "strasse|straße", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_address_line_1_preserving", + "positive_pattern": "direccion|dirección", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_address_line_1_preserving", + "positive_pattern": "adresse", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "fr_address_line_1_label_preserving", + "positive_pattern": "adresse", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ], + "it": [ + { + "pattern_identifier": "it_address_line_1_preserving", + "positive_pattern": "indirizzo", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "it_address_line_1_label_preserving", + "positive_pattern": "indirizzo", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_address_line_1_preserving", + "positive_pattern": "^住所$|住所1", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "ja_address_line_1_label_preserving", + "positive_pattern": "住所", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ], + "pt": [ + { + "pattern_identifier": "pt_address_line_1_preserving", + "positive_pattern": "morada|((?<!do |de )endereço)", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ru": [ + { + "pattern_identifier": "ru_address_line_1_preserving", + "positive_pattern": "Адрес", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "zh": [ + { + "pattern_identifier": "zh_address_line_1_preserving", + "positive_pattern": "地址", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "zh_address_line_1_label_preserving", + "positive_pattern": "地址", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ], + "tr": [ + { + "pattern_identifier": "tr_address_line_1_preserving", + "positive_pattern": "(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "tr_address_line_1_label_preserving", + "positive_pattern": "(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ], + "ko": [ + { + "pattern_identifier": "ko_address_line_1_preserving", + "positive_pattern": "^주소.?$|주소.?1", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + , + { + "pattern_identifier": "ko_address_line_1_label_preserving", + "positive_pattern": "주소", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ] + }, + "ADDRESS_LINE_2": { + "en": [ + { + "pattern_identifier": "en_address_line_2_preserving", + "positive_pattern": "address[_-]?line(2|two)|address2|addr2|street|suite|unit", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "en_address_line_2_label_preserving", + "positive_pattern": "address|line", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ], + "de": [ + { + "pattern_identifier": "de_address_line_2_preserving", + "positive_pattern": "adresszusatz|ergänzende.?angaben", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_address_line_2_preserving", + "positive_pattern": "direccion2|colonia|adicional", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_address_line_2_preserving", + "positive_pattern": "addresssuppl|complementnom|appartement", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "fr_address_line_2_label_preserving", + "positive_pattern": "adresse", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ], + "it": [ + { + "pattern_identifier": "it_address_line_2_preserving", + "positive_pattern": "indirizzo2", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "it_address_line_2_label_preserving", + "positive_pattern": "indirizzo", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_address_line_2_preserving", + "positive_pattern": "住所2", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "pt": [ + { + "pattern_identifier": "pt_address_line_2_preserving", + "positive_pattern": "complemento|addrcomplement", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ru": [ + { + "pattern_identifier": "ru_address_line_2_preserving", + "positive_pattern": "Улица", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "zh": [ + { + "pattern_identifier": "zh_address_line_2_preserving", + "positive_pattern": "地址2", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "zh_address_line_2_label_preserving", + "positive_pattern": "地址", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ], + "ko": [ + { + "pattern_identifier": "ko_address_line_2_preserving", + "positive_pattern": "주소.?2", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "ko_address_line_2_label_preserving", + "positive_pattern": "주소", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 1, + "match_field_input_types": 1 + } + ] + }, + "ADDRESS_LINE_EXTRA": { + "en": [ + { + "pattern_identifier": "en_address_line_extra_preserving", + "positive_pattern": "address.*line[3-9]|address[3-9]|addr[3-9]|street|line[3-9]", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_address_line_extra_preserving", + "positive_pattern": "municipio", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_address_line_extra_preserving", + "positive_pattern": "batiment|residence", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "it": [ + { + "pattern_identifier": "it_address_line_extra_preserving", + "positive_pattern": "indirizzo[3-9]", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "ADDRESS_LOOKUP": { + "en": [ + { + "pattern_identifier": "en_address_lookup_preserving", + "positive_pattern": "lookup", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "COUNTRY": { + "en": [ + { + "pattern_identifier": "en_country_preserving", + "positive_pattern": "country|countries", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "es": [ + { + "pattern_identifier": "es_country_preserving", + "positive_pattern": "país|pais", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "de": [ + { + "pattern_identifier": "de_country_preserving", + "positive_pattern": "(\\b|_)land(\\b|_)(?!.*(mark.*))", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ja": [ + { + "pattern_identifier": "ja_country_preserving", + "positive_pattern": "(?<!(入|出))国", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "zh": [ + { + "pattern_identifier": "zh_country_preserving", + "positive_pattern": "国家", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ko": [ + { + "pattern_identifier": "ko_country_preserving", + "positive_pattern": "국가|나라", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "tr": [ + { + "pattern_identifier": "tr_country_preserving", + "positive_pattern": "(\\b|_)(ülke|ulce|ulke)(\\b|_)", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "fa": [ + { + "pattern_identifier": "fa_country_preserving", + "positive_pattern": "کشور", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ] + }, + "COUNTRY_LOCATION": { + "en": [ + { + "pattern_identifier": "en_country_location_preserving", + "positive_pattern": "location", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 136 + } + ] + }, + "ZIP_CODE": { + "en": [ + { + "pattern_identifier": "en_zip_code_preserving", + "positive_pattern": "zip|postal|post.*code|pcode|pin.?code", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "de": [ + { + "pattern_identifier": "de_zip_code_preserving", + "positive_pattern": "postleitzahl", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "es": [ + { + "pattern_identifier": "es_zip_code_preserving", + "positive_pattern": "\\bcp\\b", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "fr": [ + { + "pattern_identifier": "fr_zip_code_preserving", + "positive_pattern": "\\bcdp\\b", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "it": [ + { + "pattern_identifier": "it_zip_code_preserving", + "positive_pattern": "\\bcap\\b", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "ja": [ + { + "pattern_identifier": "ja_zip_code_preserving", + "positive_pattern": "郵便番号", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "pt": [ + { + "pattern_identifier": "pt_zip_code_preserving", + "positive_pattern": "codigo|codpos|\\bcep\\b", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "ru": [ + { + "pattern_identifier": "ru_zip_code_preserving", + "positive_pattern": "Почтовый.?Индекс", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "hi": [ + { + "pattern_identifier": "hi_zip_code_preserving", + "positive_pattern": "पिन.?कोड", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "ml": [ + { + "pattern_identifier": "ml_zip_code_preserving", + "positive_pattern": "പിന്കോഡ്", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "zh": [ + { + "pattern_identifier": "zh_zip_code_preserving", + "positive_pattern": "邮政编码|邮编|郵遞區號", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "tr": [ + { + "pattern_identifier": "tr_zip_code_preserving", + "positive_pattern": "(\\b|_)posta kodu(\\b|_)", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "ko": [ + { + "pattern_identifier": "ko_zip_code_preserving", + "positive_pattern": "우편.?번호", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, + "ZIP_4": { + "en": [ + { + "pattern_identifier": "en_zip_4_preserving", + "positive_pattern": "zip|^-$|post2", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "pt": [ + { + "pattern_identifier": "pt_zip_4_preserving", + "positive_pattern": "codpos2", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, + "CITY": { + "en": [ + { + "pattern_identifier": "en_city_preserving", + "positive_pattern": "city|town|suburb", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "de": [ + { + "pattern_identifier": "de_city_preserving", + "positive_pattern": "\\bort\\b|stadt", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "es": [ + { + "pattern_identifier": "es_city_preserving", + "positive_pattern": "ciudad|provincia|localidad|poblacion", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "fr": [ + { + "pattern_identifier": "fr_city_preserving", + "positive_pattern": "ville|commune", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "it": [ + { + "pattern_identifier": "it_city_preserving", + "positive_pattern": "localita", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ja": [ + { + "pattern_identifier": "ja_city_preserving", + "positive_pattern": "市区町村", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "pt": [ + { + "pattern_identifier": "pt_city_preserving", + "positive_pattern": "cidade|município", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ru": [ + { + "pattern_identifier": "ru_city_preserving", + "positive_pattern": "Город|Населённый.?пункт", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "zh": [ + { + "pattern_identifier": "zh_city_preserving", + "positive_pattern": "市|分區", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "fa": [ + { + "pattern_identifier": "fa_city_preserving", + "positive_pattern": "شهر", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "hi": [ + { + "pattern_identifier": "hi_city_preserving", + "positive_pattern": "शहर|ग्राम|गाँव", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ml": [ + { + "pattern_identifier": "ml_city_preserving", + "positive_pattern": "നഗരം|ഗ്രാമം", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "tr": [ + { + "pattern_identifier": "tr_city_preserving", + "positive_pattern": "((\\b|_|\\*)([İii̇]l[cç]e(miz|niz)?)(\\b|_|\\*))", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ko": [ + { + "pattern_identifier": "ko_city_preserving", + "positive_pattern": "^시[^도·・]|시[·・]?군[·・]?구", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ] + }, + "STATE": { + "en": [ + { + "pattern_identifier": "en_state_preserving", + "positive_pattern": "(?<!(united|hist|history).?)state|county|region|province|county|principality", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ja": [ + { + "pattern_identifier": "ja_state_preserving", + "positive_pattern": "都道府県", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "pt": [ + { + "pattern_identifier": "pt_state_preserving", + "positive_pattern": "estado|provincia", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ru": [ + { + "pattern_identifier": "ru_state_preserving", + "positive_pattern": "область", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "zh": [ + { + "pattern_identifier": "zh_state_preserving", + "positive_pattern": "省|地區", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ml": [ + { + "pattern_identifier": "ml_state_preserving", + "positive_pattern": "സംസ്ഥാനം", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "fa": [ + { + "pattern_identifier": "fa_state_preserving", + "positive_pattern": "استان", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "hi": [ + { + "pattern_identifier": "hi_state_preserving", + "positive_pattern": "राज्य", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "tr": [ + { + "pattern_identifier": "tr_state_preserving", + "positive_pattern": "((\\b|_|\\*)(eyalet|[şs]ehir|[İii̇]l(imiz)?|kent)(\\b|_|\\*))", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ko": [ + { + "pattern_identifier": "ko_state_preserving", + "positive_pattern": "^시[·・]?도", + "positive_score": 1.1, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ] + }, + "SEARCH_TERM": { + "en": [ + { + "pattern_identifier": "en_search_term_preserving", + "positive_pattern": "^q$|search|query|qry", + "positive_score": 0.8, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 145 + } + ], + "de": [ + { + "pattern_identifier": "de_search_term_preserving", + "positive_pattern": "suche.*", + "positive_score": 0.8, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 145 + } + ], + "zh": [ + { + "pattern_identifier": "zh_search_term_preserving", + "positive_pattern": "搜索", + "positive_score": 0.8, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 145 + } + ], + "ja": [ + { + "pattern_identifier": "ja_search_term_preserving", + "positive_pattern": "探す|検索", + "positive_score": 0.8, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 145 + } + ], + "fr": [ + { + "pattern_identifier": "fr_search_term_preserving", + "positive_pattern": "recherch.*", + "positive_score": 0.8, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 145 + } + ], + "pt": [ + { + "pattern_identifier": "pt_search_term_preserving", + "positive_pattern": "busca", + "positive_score": 0.8, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 145 + } + ], + "fa": [ + { + "pattern_identifier": "fa_search_term_preserving", + "positive_pattern": "جستجو", + "positive_score": 0.8, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 145 + } + ], + "ru": [ + { + "pattern_identifier": "ru_search_term_preserving", + "positive_pattern": "искать|найти|поиск", + "positive_score": 0.8, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 145 + } + ] + }, + "PRICE": { + "en": [ + { + "pattern_identifier": "en_price_preserving", + "positive_pattern": "\\bprice\\b|\\brate\\b|\\bcost\\b", + "positive_score": 0.95, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 217 + } + ], + "ar": [ + { + "pattern_identifier": "ar_price_preserving", + "positive_pattern": "قیمة|سعر", + "positive_score": 0.95, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 217 + } + ], + "fa": [ + { + "pattern_identifier": "fa_price_preserving", + "positive_pattern": "قیمت", + "positive_score": 0.95, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 217 + } + ], + "fr": [ + { + "pattern_identifier": "fr_price_preserving", + "positive_pattern": "\\bprix\\b|\\bcoût\\b|\\bcout\\b|\\btarif\\b", + "positive_score": 0.95, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 217 + } + ] + }, + "NAME_ON_CARD": { + "en": [ + { + "pattern_identifier": "en_name_on_card_preserving", + "positive_pattern": "card.?(?:holder|owner)|name.*(\\b)?on(\\b)?.*card|(?:card|cc).?name|cc.?full.?name", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "de": [ + { + "pattern_identifier": "de_name_on_card_preserving", + "positive_pattern": "karteninhaber", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_name_on_card_preserving", + "positive_pattern": "nombre.*tarjeta", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_name_on_card_preserving", + "positive_pattern": "nom.*carte", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "it": [ + { + "pattern_identifier": "it_name_on_card_preserving", + "positive_pattern": "nome.*cart", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_name_on_card_preserving", + "positive_pattern": "名前", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ru": [ + { + "pattern_identifier": "ru_name_on_card_preserving", + "positive_pattern": "Имя.*карты", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "zh": [ + { + "pattern_identifier": "zh_name_on_card_preserving", + "positive_pattern": "信用卡开户名|开户名|持卡人姓名|持卡人姓名", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "NAME_ON_CARD_CONTEXTUAL": { + "en": [ + { + "pattern_identifier": "en_name_on_card_contextual_preserving", + "positive_pattern": "name", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "CREDIT_CARD_NUMBER": { + "en": [ + { + "pattern_identifier": "en_card_number_preserving", + "positive_pattern": "(add)?(?:card|cc|acct).?(?:number|#|no|num|field)", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ], + "de": [ + { + "pattern_identifier": "de_card_number_preserving", + "positive_pattern": "(?<!telefon|haus|person|fødsels)nummer", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ], + "ja": [ + { + "pattern_identifier": "ja_card_number_preserving", + "positive_pattern": "カード番号", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ], + "ru": [ + { + "pattern_identifier": "ru_card_number_preserving", + "positive_pattern": "Номер.*карты", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ], + "zh": [ + { + "pattern_identifier": "zh_card_number_preserving", + "positive_pattern": "信用卡号|信用卡号码|信用卡卡號", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ], + "ko": [ + { + "pattern_identifier": "ko_card_number_preserving", + "positive_pattern": "카드", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ], + "es": [ + { + "pattern_identifier": "es_card_number_preserving", + "positive_pattern": "(numero|número|numéro)(?!.*(document|fono|phone|réservation))", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ], + "pt": [ + { + "pattern_identifier": "pt_card_number_preserving", + "positive_pattern": "(numero|número|numéro)(?!.*(document|fono|phone|réservation))", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ], + "fr": [ + { + "pattern_identifier": "fr_card_number_preserving", + "positive_pattern": "(numero|número|numéro)(?!.*(document|fono|phone|réservation))", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ] + }, + "CREDIT_CARD_VERIFICATION_CODE": { + "en": [ + { + "pattern_identifier": "en_card_cvc_preserving", + "positive_pattern": "verification|card.?identification|security.?code|card.?code|security.?value|security.?number|card.?pin|c-v-v|(cvn|cvv|cvc|csc|cvd|cid|ccv)(field)?|\\bcid\\b", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 101 + } + ] + }, + "CREDIT_CARD_EXP_MONTH": { + "en": [ + { + "pattern_identifier": "en_card_exp_month_preserving", + "positive_pattern": "expir|exp.*mo|exp.*date|ccmonth|cardmonth|addmonth", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "de": [ + { + "pattern_identifier": "de_card_exp_month_preserving", + "positive_pattern": "gueltig|gültig|monat", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "es": [ + { + "pattern_identifier": "es_card_exp_month_preserving", + "positive_pattern": "fecha", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "fr": [ + { + "pattern_identifier": "fr_card_exp_month_preserving", + "positive_pattern": "date.*exp", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "it": [ + { + "pattern_identifier": "it_card_exp_month_preserving", + "positive_pattern": "scadenza", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "ja": [ + { + "pattern_identifier": "ja_card_exp_month_preserving", + "positive_pattern": "有効期限", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "pt": [ + { + "pattern_identifier": "pt_card_exp_month_preserving", + "positive_pattern": "validade", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "ru": [ + { + "pattern_identifier": "ru_card_exp_month_preserving", + "positive_pattern": "Срок действия карты", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "zh": [ + { + "pattern_identifier": "zh_card_exp_month_preserving", + "positive_pattern": "月", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ] + }, + "CREDIT_CARD_EXP_YEAR": { + "en": [ + { + "pattern_identifier": "en_card_exp_year_preserving", + "positive_pattern": "exp|^/|(add)?year", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "de": [ + { + "pattern_identifier": "de_card_exp_year_preserving", + "positive_pattern": "ablaufdatum|gueltig|gültig|jahr", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "es": [ + { + "pattern_identifier": "es_card_exp_year_preserving", + "positive_pattern": "fecha", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "it": [ + { + "pattern_identifier": "it_card_exp_year_preserving", + "positive_pattern": "scadenza", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "ja": [ + { + "pattern_identifier": "ja_card_exp_year_preserving", + "positive_pattern": "有効期限", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "pt": [ + { + "pattern_identifier": "pt_card_exp_year_preserving", + "positive_pattern": "validade", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "ru": [ + { + "pattern_identifier": "ru_card_exp_year_preserving", + "positive_pattern": "Срок действия карты", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "zh": [ + { + "pattern_identifier": "zh_card_exp_year_preserving", + "positive_pattern": "年|有效期", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ] + }, + "CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR": { + "en": [ + { + "pattern_identifier": "en_card_exp_date_2_digit_year_preserving", + "positive_pattern": "(?:exp.*date[^y\\n\\r]*|mm\\s*[-/]?\\s*)yy(?:[^y]|$)", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ] + }, + "CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR": { + "en": [ + { + "pattern_identifier": "en_card_exp_date_4_digit_year_preserving", + "positive_pattern": "(?:exp.*date[^y\\n\\r]*|mm\\s*[-/]?\\s*)yyyy(?:[^y]|$)", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ] + }, + "CREDIT_CARD_EXP_DATE": { + "en": [ + { + "pattern_identifier": "en_card_exp_date_preserving", + "positive_pattern": "expir|exp.*date|^expfield$", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "de": [ + { + "pattern_identifier": "de_card_exp_date_preserving", + "positive_pattern": "gueltig|gültig", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "es": [ + { + "pattern_identifier": "es_card_exp_date_preserving", + "positive_pattern": "fecha", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "fr": [ + { + "pattern_identifier": "fr_card_exp_date_preserving", + "positive_pattern": "date.*exp", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "it": [ + { + "pattern_identifier": "it_card_exp_date_preserving", + "positive_pattern": "scadenza", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "ja": [ + { + "pattern_identifier": "ja_card_exp_date_preserving", + "positive_pattern": "有効期限", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "pt": [ + { + "pattern_identifier": "pt_card_exp_date_preserving", + "positive_pattern": "validade", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ], + "ru": [ + { + "pattern_identifier": "ru_card_exp_date_preserving", + "positive_pattern": "Срок действия карты", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ] + }, + "CREDIT_CARD_EXP_MONTH_BEFORE_YEAR": { + "en": [ + { + "pattern_identifier": "en_card_exp_month_before_year_preserving", + "positive_pattern": "^mm$", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ] + }, + "CREDIT_CARD_EXP_YEAR_AFTER_MONTH": { + "en": [ + { + "pattern_identifier": "en_card_exp_year_after_month_preserving", + "positive_pattern": "^(yy|yyyy)$", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 205 + } + ] + }, + "GIFT_CARD": { + "en": [ + { + "pattern_identifier": "en_gift_card_preserving", + "positive_pattern": "gift.?(card|cert)", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 197 + } + ] + }, + "DEBIT_GIFT_CARD": { + "en": [ + { + "pattern_identifier": "en_debit_gift_card_preserving", + "positive_pattern": "(?:visa|mastercard|discover|amex|american express).*gift.?card", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 197 + } + ] + }, + "DEBIT_CARD": { + "en": [ + { + "pattern_identifier": "en_debit_card_preserving", + "positive_pattern": "debit.*card", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 197 + } + ] + }, + "DAY": { + "en": [ + { + "pattern_identifier": "en_day_preserving", + "positive_pattern": "day", + "positive_score": 1.0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 9 + } + ] + }, + "EMAIL_ADDRESS": { + "en": [ + { + "pattern_identifier": "en_email_preserving", + "positive_pattern": "e.?mail", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "fr": [ + { + "pattern_identifier": "fr_email_preserving", + "positive_pattern": "courriel", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "es": [ + { + "pattern_identifier": "es_email_preserving", + "positive_pattern": "correo.*electr(o|ó)nico", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "ja": [ + { + "pattern_identifier": "ja_email_preserving", + "positive_pattern": "メールアドレス", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "ru": [ + { + "pattern_identifier": "ru_email_preserving", + "positive_pattern": "Электронной.?Почты", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "zh": [ + { + "pattern_identifier": "zh_email_preserving", + "positive_pattern": "邮件|邮箱|電郵地址", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "ml": [ + { + "pattern_identifier": "ml_email_preserving", + "positive_pattern": "ഇ-മെയില്|ഇലക്ട്രോണിക്.?മെയിൽ", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "fa": [ + { + "pattern_identifier": "fa_email_preserving", + "positive_pattern": "ایمیل|پست.*الکترونیک", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "hi": [ + { + "pattern_identifier": "hi_email_preserving", + "positive_pattern": "ईमेल|इलॅक्ट्रॉनिक.?मेल", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "tr": [ + { + "pattern_identifier": "tr_email_preserving", + "positive_pattern": "(\\b|_)eposta(\\b|_)", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ], + "ko": [ + { + "pattern_identifier": "ko_email_preserving", + "positive_pattern": "(?:이메일|전자.?우편|[Ee]-?mail)(.?주소)?", + "positive_score": 1.4, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 3 + } + ] + }, + "NAME_IGNORED": { + "en": [ + { + "pattern_identifier": "en_name_ignored_preserving", + "positive_pattern": "user.?name|user.?id|nickname|maiden name|title|prefix|suffix", + "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 + } + ], + "de": [ + { + "pattern_identifier": "de_name_ignored_preserving", + "positive_pattern": "vollständiger.?name", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "zh": [ + { + "pattern_identifier": "zh_name_ignored_preserving", + "positive_pattern": "用户名", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ], + "ko": [ + { + "pattern_identifier": "ko_name_ignored_preserving", + "positive_pattern": "(?:사용자.?)?아이디|사용자.?ID", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 137 + } + ] + }, + "FULL_NAME": { + "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_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_full_name_preserving", + "positive_pattern": "nombre.*y.*apellidos", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_full_name_preserving", + "positive_pattern": "^nom(?!bre)", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_full_name_preserving", + "positive_pattern": "お名前|氏名", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "pt": [ + { + "pattern_identifier": "pt_full_name_preserving", + "positive_pattern": "^nome", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fa": [ + { + "pattern_identifier": "fa_full_name_preserving", + "positive_pattern": "نام.*نام.*خانوادگی", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "zh": [ + { + "pattern_identifier": "zh_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", + "positive_pattern": "(\\b|_|\\*)ad[ı]? soyad[ı]?(\\b|_|\\*)", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ko": [ + { + "pattern_identifier": "ko_full_name_preserving", + "positive_pattern": "성명", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "NAME_SPECIFIC": { + "en": [ + { + "pattern_identifier": "en_name_specific_preserving", + "positive_pattern": "^name", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_name_specific_preserving", + "positive_pattern": "^nom", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "pt": [ + { + "pattern_identifier": "pt_name_specific_preserving", + "positive_pattern": "^nome", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "FIRST_NAME": { + "en": [ + { + "pattern_identifier": "en_first_name_preserving", + "positive_pattern": "first.*name|initials|fname|first$|given.*name", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "de": [ + { + "pattern_identifier": "de_first_name_preserving", + "positive_pattern": "vorname", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_first_name_preserving", + "positive_pattern": "nombre", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_first_name_preserving", + "positive_pattern": "forename|prénom|prenom", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_first_name_preserving", + "positive_pattern": "名", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "pt": [ + { + "pattern_identifier": "pt_first_name_preserving", + "positive_pattern": "nome", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ru": [ + { + "pattern_identifier": "ru_first_name_preserving", + "positive_pattern": "Имя", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fa": [ + { + "pattern_identifier": "fa_first_name_preserving", + "positive_pattern": "نام", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ko": [ + { + "pattern_identifier": "ko_first_name_preserving", + "positive_pattern": "이름", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ml": [ + { + "pattern_identifier": "ml_first_name_preserving", + "positive_pattern": "പേര്", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "tr": [ + { + "pattern_identifier": "tr_first_name_preserving", + "positive_pattern": "(\\b|_|\\*)(isim|ad|ad(i|ı|iniz|ınız)?)(\\b|_|\\*)", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "hi": [ + { + "pattern_identifier": "hi_first_name_preserving", + "positive_pattern": "नाम", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "MIDDLE_INITIAL": { + "en": [ + { + "pattern_identifier": "en_middle_initial_preserving", + "positive_pattern": "middle.*initial|m\\.i\\.|mi$|\\bmi\\b", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "MIDDLE_NAME": { + "en": [ + { + "pattern_identifier": "en_middle_name_preserving", + "positive_pattern": "middle.*name|mname|middle$", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "LAST_NAME": { + "en": [ + { + "pattern_identifier": "en_last_name_preserving", + "positive_pattern": "last.*name|lname|surname(?!\\d)|last$|secondname|family.*name", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "de": [ + { + "pattern_identifier": "de_last_name_preserving", + "positive_pattern": "nachname", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_last_name_preserving", + "positive_pattern": "apellidos?", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_last_name_preserving", + "positive_pattern": "famille|^nom(?!bre)", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "it": [ + { + "pattern_identifier": "it_last_name_preserving", + "positive_pattern": "cognome", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_last_name_preserving", + "positive_pattern": "姓", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "pt": [ + { + "pattern_identifier": "pt_last_name_preserving", + "positive_pattern": "apelidos|surename|sobrenome", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ru": [ + { + "pattern_identifier": "ru_last_name_preserving", + "positive_pattern": "Фамилия", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fa": [ + { + "pattern_identifier": "fa_last_name_preserving", + "positive_pattern": "نام.*خانوادگی", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "hi": [ + { + "pattern_identifier": "hi_last_name_preserving", + "positive_pattern": "उपनाम", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ml": [ + { + "pattern_identifier": "ml_last_name_preserving", + "positive_pattern": "മറുപേര്", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "tr": [ + { + "pattern_identifier": "tr_last_name_preserving", + "positive_pattern": "(\\b|_|\\*)(soyisim|soyad(i|ı|iniz|ınız)?)(\\b|_|\\*)", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ko": [ + { + "pattern_identifier": "ko_last_name_preserving", + "positive_pattern": "\\b성(?:[^명]|\\b)", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "LAST_NAME_FIRST": { + "es": [ + { + "pattern_identifier": "es_last_name_first_preserving", + "positive_pattern": "(primer.*apellido)|(apellido1)|(apellido.*paterno)|surname_?1|first(\\s|_)?surname", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "LAST_NAME_SECOND": { + "es": [ + { + "pattern_identifier": "es_last_name_second_preserving", + "positive_pattern": "(segund.*apellido)|(apellido2)|(apellido.*materno)|surname_?2|second(\\s|_)?surname", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "HONORIFIC_PREFIX": { + "en": [ + { + "pattern_identifier": "en_honorific_prefix_preserving", + "positive_pattern": "^title:?$|(salutation(?! and given name))", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "de": [ + { + "pattern_identifier": "de_honorific_prefix_preserving", + "positive_pattern": "anrede|titel", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_honorific_prefix_preserving", + "positive_pattern": "tratamiento|encabezamiento", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "it": [ + { + "pattern_identifier": "it_honorific_prefix_preserving", + "positive_pattern": "titolo", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_honorific_prefix_preserving", + "positive_pattern": "titre", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ru": [ + { + "pattern_identifier": "ru_honorific_prefix_preserving", + "positive_pattern": "обраще́ние|зва́ние", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "el": [ + { + "pattern_identifier": "el_honorific_prefix_preserving", + "positive_pattern": "προσφώνηση", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "tr": [ + { + "pattern_identifier": "tr_honorific_prefix_preserving", + "positive_pattern": "hitap", + "positive_score": 0.9, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "PHONE": { + "en": [ + { + "pattern_identifier": "en_phone_preserving", + "positive_pattern": "phone|mobile|contact.?number", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "de": [ + { + "pattern_identifier": "de_phone_preserving", + "positive_pattern": "telefonnummer", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "es": [ + { + "pattern_identifier": "es_phone_preserving", + "positive_pattern": "telefono|teléfono", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "fr": [ + { + "pattern_identifier": "fr_phone_preserving", + "positive_pattern": "telfixe", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "ja": [ + { + "pattern_identifier": "ja_phone_preserving", + "positive_pattern": "電話", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "pt": [ + { + "pattern_identifier": "pt_phone_preserving", + "positive_pattern": "telefone|telemovel", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "ru": [ + { + "pattern_identifier": "ru_phone_preserving", + "positive_pattern": "телефон", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "hi": [ + { + "pattern_identifier": "hi_phone_preserving", + "positive_pattern": "मोबाइल", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "tr": [ + { + "pattern_identifier": "tr_phone_preserving", + "positive_pattern": "(\\b|_|\\*)telefon(\\b|_|\\*)", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "zh": [ + { + "pattern_identifier": "zh_phone_preserving", + "positive_pattern": "电话", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "ml": [ + { + "pattern_identifier": "ml_phone_preserving", + "positive_pattern": "മൊബൈല്", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "ko": [ + { + "pattern_identifier": "ko_phone_preserving", + "positive_pattern": "(?:전화|핸드폰|휴대폰|휴대전화)(?:.?번호)?", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, + "AUGMENTED_PHONE_COUNTRY_CODE": { + "en": [ + { + "pattern_identifier": "en_augmented_phone_country_code_preserving", + "positive_pattern": "^[^0-9+]*(?:\\+|00)\\s*([1-9]\\d{0,3})\\D*$", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "PHONE_COUNTRY_CODE": { + "en": [ + { + "pattern_identifier": "en_phone_country_code_preserving", + "positive_pattern": "country.*code|ccode|_cc|phone.*code|user.*phone.*code", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 77 + } + ] + }, + "PHONE_AREA_CODE_NO_TEXT": { + "en": [ + { + "pattern_identifier": "en_phone_area_code_no_text_preserving", + "positive_pattern": "^\\($", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, + "PHONE_AREA_CODE": { + "en": [ + { + "pattern_identifier": "en_phone_area_code_preserving", + "positive_pattern": "area.*code|acode|area", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "ko": [ + { + "pattern_identifier": "ko_phone_area_code_preserving", + "positive_pattern": "지역.?번호", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, + "PHONE_PREFIX_SEPARATOR": { + "en": [ + { + "pattern_identifier": "en_phone_prefix_separator_preserving", + "positive_pattern": "^-$|^\\)$", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, + "PHONE_SUFFIX_SEPARATOR": { + "en": [ + { + "pattern_identifier": "en_phone_suffix_separator_preserving", + "positive_pattern": "^-$", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, + "PHONE_PREFIX": { + "en": [ + { + "pattern_identifier": "en_phone_prefix_preserving", + "positive_pattern": "prefix|exchange", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "fr": [ + { + "pattern_identifier": "fr_phone_prefix_preserving", + "positive_pattern": "preselection", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "pt": [ + { + "pattern_identifier": "pt_phone_prefix_preserving", + "positive_pattern": "ddd", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, + "PHONE_SUFFIX": { + "en": [ + { + "pattern_identifier": "en_phone_suffix_preserving", + "positive_pattern": "suffix", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, + "PHONE_EXTENSION": { + "en": [ + { + "pattern_identifier": "en_phone_extension_preserving", + "positive_pattern": "\\bext|ext\\b|extension", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ], + "pt": [ + { + "pattern_identifier": "pt_phone_extension_preserving", + "positive_pattern": "ramal", + "positive_score": 1.3, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 69 + } + ] + }, + "PASSPORT": { + "en": [ + { + "pattern_identifier": "en_passport_preserving", + "positive_pattern": "document.*number|passport", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "fr": [ + { + "pattern_identifier": "fr_passport_preserving", + "positive_pattern": "passeport", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_passport_preserving", + "positive_pattern": "numero.*documento|pasaporte", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_passport_preserving", + "positive_pattern": "書類", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "TRAVEL_ORIGIN": { + "en": [ + { + "pattern_identifier": "en_travel_origin_preserving", + "positive_pattern": "point.*of.*entry|arrival", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_travel_origin_preserving", + "positive_pattern": "punto.*internaci(o|ó)n|fecha.*llegada", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_travel_origin_preserving", + "positive_pattern": "入国", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "TRAVEL_DESTINATION": { + "en": [ + { + "pattern_identifier": "en_travel_destination_preserving", + "positive_pattern": "departure", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_travel_destination_preserving", + "positive_pattern": "fecha.*salida|destino", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_travel_destination_preserving", + "positive_pattern": "出国", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "FLIGHT": { + "en": [ + { + "pattern_identifier": "en_flight_preserving", + "positive_pattern": "airline|flight", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "es": [ + { + "pattern_identifier": "es_flight_preserving", + "positive_pattern": "aerol(i|í)nea|n(u|ú)mero.*vuelo", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ], + "ja": [ + { + "pattern_identifier": "ja_flight_preserving", + "positive_pattern": "便名|航空会社", + "positive_score": 1.2, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "UPI_VIRTUAL_PAYMENT_ADDRESS": { + "en": [ + { + "pattern_identifier": "en_upi_virtual_payment_address_user@(IFSC/Aadhaar/Mobile/RuPay)_preserving", + "positive_pattern": "^[\\w.+-_]+@(\\w+\\.ifsc\\.npci|aadhaar\\.npci|mobile\\.npci|rupay\\.npci)$", + "positive_score": 0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + }, + { + "pattern_identifier": "en_upi_virtual_payment_address_user@(bank_list)_preserving", + "positive_pattern": "^[\\w.+-_]+@(airtel|airtelpaymentsbank|albk|allahabadbank|allbank|andb|apb|apl|axis|axisbank|axisgo|bandhan|barodampay|birla|boi|cbin|cboi|centralbank|cmsidfc|cnrb|csbcash|csbpay|cub|dbs|dcb|dcbbank|denabank|dlb|eazypay|equitas|ezeepay|fbl|federal|finobank|hdfcbank|hsbc|icici|idbi|idbibank|idfc|idfcbank|idfcnetc|ikwik|imobile|indbank|indianbank|indianbk|indus|iob|jkb|jsb|jsbp|karb|karurvysyabank|kaypay|kbl|kbl052|kmb|kmbl|kotak|kvb|kvbank|lime|lvb|lvbank|mahb|obc|okaxis|okbizaxis|okhdfcbank|okicici|oksbi|paytm|payzapp|pingpay|pnb|pockets|psb|purz|rajgovhdfcbank|rbl|sbi|sc|scb|scbl|scmobile|sib|srcb|synd|syndbank|syndicate|tjsb|tjsp|ubi|uboi|uco|unionbank|unionbankofindia|united|upi|utbi|vijayabank|vijb|vjb|ybl|yesbank|yesbankltd)$", + "positive_score": 0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "INTERNATIONAL_BANK_ACCOUNT_NUMBER": { + "en": [ + { + "pattern_identifier": "en_international_bank_account_number_preserving", + "positive_pattern": "^[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}$", + "positive_score": 0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "VALIDATION_CREDIT_CARD_VERIFICATION_CODE": { + "en": [ + { + "pattern_identifier": "en_credit_card_cvc_preserving", + "positive_pattern": "^\\d{3,4}$", + "positive_score": 0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "VALIDATION_CREDIT_CARD_EXP_YEAR": { + "en": [ + { + "pattern_identifier": "en_credit_card_exp_year_preserving", + "positive_pattern": "^[2][0][1-9][0-9]$", + "positive_score": 0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "URL_SEARCH_ACTION": { + "en": [ + { + "pattern_identifier": "en_url_search_action_preserving", + "positive_pattern": "/search(/|((\\w*\\.\\w+)?$))", + "positive_score": 0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "SOCIAL_SECURITY": { + "en": [ + { + "pattern_identifier": "en_social_security_preserving", + "positive_pattern": "ssn|social.?security.?(num(ber)?|#)*", + "positive_score": 0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + }, + "ONE_TIME_PASSWORD": { + "en": [ + { + "pattern_identifier": "en_one_time_password_preserving", + "positive_pattern": "one.?time|sms.?(code|token|password|pwd|pass)", + "positive_score": 0, + "negative_pattern": null, + "match_field_attributes": 3, + "match_field_input_types": 1 + } + ] + } +} 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 new file mode 100644 index 00000000000..e6e1a075863 --- /dev/null +++ b/chromium/components/autofill/core/browser/pattern_provider/test_pattern_provider.cc @@ -0,0 +1,36 @@ +// 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 new file mode 100644 index 00000000000..dc923237777 --- /dev/null +++ b/chromium/components/autofill/core/browser/pattern_provider/test_pattern_provider.h @@ -0,0 +1,23 @@ +// 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/payments/autofill_save_card_infobar_mobile.h b/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_mobile.h index 969131b3a1e..99d93d80d3e 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_mobile.h +++ b/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_mobile.h @@ -7,6 +7,8 @@ #include <memory> +#include "components/signin/public/identity_manager/account_info.h" + namespace infobars { class InfoBar; } @@ -15,9 +17,12 @@ namespace autofill { class AutofillSaveCardInfoBarDelegateMobile; -// Creates an infobar for saving a credit card on a mobile device. +// Creates an Infobar for saving a credit card on a mobile device. If +// AccountInfo contains the user's data, an account indication footer will be +// shown at the bottom of the Infobar. std::unique_ptr<infobars::InfoBar> CreateSaveCardInfoBarMobile( - std::unique_ptr<AutofillSaveCardInfoBarDelegateMobile> delegate); + std::unique_ptr<AutofillSaveCardInfoBarDelegateMobile> delegate, + base::Optional<AccountInfo> accountInfo); } // namespace autofill 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 7267cfdfd1a..25ea9af5ec7 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 @@ -7,25 +7,18 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/feature_list.h" #include "build/build_config.h" +#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" #include "google_apis/gaia/google_service_auth_error.h" -namespace { - -#if defined(OS_ANDROID) -constexpr base::Feature kWalletRequiresFirstSyncSetupComplete{ - "WalletRequiresFirstSyncSetupComplete", base::FEATURE_ENABLED_BY_DEFAULT}; -#endif - -} // namespace - namespace browser_sync { AutofillWalletModelTypeController::AutofillWalletModelTypeController( @@ -102,7 +95,8 @@ AutofillWalletModelTypeController::GetPreconditionState() const { pref_service_->GetBoolean(autofill::prefs::kAutofillCreditCardEnabled) && !sync_service_->GetAuthError().IsPersistentError(); #if defined(OS_ANDROID) - if (base::FeatureList::IsEnabled(kWalletRequiresFirstSyncSetupComplete)) { + if (base::FeatureList::IsEnabled( + autofill::features::kWalletRequiresFirstSyncSetupComplete)) { // On Android, it's also required that the initial Sync setup is complete // (i.e. the user has previously opted in to Sync-the-feature, even if it's // not enabled right now). @@ -114,6 +108,22 @@ AutofillWalletModelTypeController::GetPreconditionState() const { : PreconditionState::kMustStopAndClearData; } +bool AutofillWalletModelTypeController::ShouldRunInTransportOnlyMode() const { + if (type() != syncer::AUTOFILL_WALLET_DATA) { + return false; + } + if (!base::FeatureList::IsEnabled( + autofill::features::kAutofillEnableAccountWalletStorage)) { + return false; + } + if (sync_service_->GetUserSettings()->IsUsingSecondaryPassphrase() && + !base::FeatureList::IsEnabled( + switches::kSyncAllowWalletDataInTransportModeWithCustomPassphrase)) { + return false; + } + return true; +} + void AutofillWalletModelTypeController::OnUserPrefChanged() { DCHECK(CalledOnValidThread()); sync_service_->DataTypePreconditionChanged(type()); diff --git a/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h b/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h index 03d0312c094..8a016e00805 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h +++ b/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h @@ -47,6 +47,7 @@ class AutofillWalletModelTypeController : public syncer::ModelTypeController, void Stop(syncer::ShutdownReason shutdown_reason, StopCallback callback) override; PreconditionState GetPreconditionState() const override; + bool ShouldRunInTransportOnlyMode() const override; // syncer::SyncServiceObserver implementation. void OnStateChanged(syncer::SyncService* sync) override; 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 cbd1018b3dd..9b38d40a594 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 @@ -79,21 +79,12 @@ void CreditCardAccessManager::UpdateCreditCardFormEventLogger() { size_t server_record_type_count = 0; size_t local_record_type_count = 0; - bool has_server_nickname = false; for (CreditCard* credit_card : credit_cards) { - // If any masked server card has valid nickname, we will set to true no - // matter the flag is enabled or not. - if (credit_card->record_type() == CreditCard::MASKED_SERVER_CARD && - credit_card->HasNonEmptyValidNickname()) { - has_server_nickname = true; - } - if (credit_card->record_type() == CreditCard::LOCAL_CARD) local_record_type_count++; else server_record_type_count++; } - form_event_logger_->set_has_server_nickname(has_server_nickname); form_event_logger_->set_server_record_type_count(server_record_type_count); form_event_logger_->set_local_record_type_count(local_record_type_count); form_event_logger_->set_is_context_secure(client_->IsContextSecure()); 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 e9e8c203a20..2addf2ad08f 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 @@ -15,7 +15,7 @@ #include <vector> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/feature_list.h" #include "base/metrics/histogram_functions.h" #include "base/strings/string16.h" @@ -486,6 +486,8 @@ void CreditCardSaveManager::OfferCardUploadSave() { // should not display the offer-to-save infobar at all. if (!is_mobile_build || show_save_prompt_.value_or(true)) { user_did_accept_upload_prompt_ = false; + if (observer_for_testing_) + observer_for_testing_->OnOfferUploadSave(); client_->ConfirmSaveCreditCardToCloud( upload_request_.card, legal_message_lines_, AutofillClient::SaveCreditCardOptions() diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.h b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.h index cc5131c2956..a1e77140599 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.h +++ b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.h @@ -84,6 +84,7 @@ class CreditCardSaveManager { public: virtual ~ObserverForTest() {} virtual void OnOfferLocalSave() {} + virtual void OnOfferUploadSave() {} virtual void OnDecideToRequestUploadSave() {} virtual void OnReceivedGetUploadDetailsResponse() {} virtual void OnSentUploadCardRequest() {} 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 d6dbaf29876..1868db7fc2b 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,6 +31,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/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" @@ -349,6 +350,7 @@ class CreditCardSaveManagerTest : 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 TestFormDataImporter: TestCreditCardSaveManager* credit_card_save_manager_; // Ends up getting owned (and destroyed) by TestAutofillClient: 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 5843d193507..a7286883894 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,6 +28,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/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" @@ -313,6 +314,7 @@ 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/payments_client_unittest.cc b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc index 1faf3fad213..d96c2f31f4b 100644 --- a/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc @@ -12,7 +12,7 @@ #include "base/macros.h" #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" diff --git a/chromium/components/autofill/core/browser/payments/strike_database.cc b/chromium/components/autofill/core/browser/payments/strike_database.cc index d373314931e..a4acdfcbdb8 100644 --- a/chromium/components/autofill/core/browser/payments/strike_database.cc +++ b/chromium/components/autofill/core/browser/payments/strike_database.cc @@ -10,7 +10,7 @@ #include <vector> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/metrics/histogram_functions.h" #include "base/task/post_task.h" #include "base/task/thread_pool.h" 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 1d0f11324dd..039ea214d05 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 @@ -10,7 +10,7 @@ #include <vector> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/metrics/histogram_functions.h" #include "base/task/post_task.h" #include "base/time/time.h" diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc index 0a6960bbd43..11d891b1282 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager.cc @@ -1523,12 +1523,9 @@ const std::string& PersonalDataManager::GetDefaultCountryCodeForNewAddress() if (default_country_code_.empty()) default_country_code_ = MostCommonCountryCodeFromProfiles(); - if (base::FeatureList::IsEnabled( - features::kAutofillUseVariationCountryCode)) { - // Failing that, use the country code from variations service. - if (default_country_code_.empty()) - default_country_code_ = variations_country_code_; - } + // Failing that, use the country code from variations service. + if (default_country_code_.empty()) + default_country_code_ = variations_country_code_; // Failing that, guess based on system timezone. if (default_country_code_.empty()) @@ -1984,7 +1981,7 @@ bool PersonalDataManager::ShouldShowCardsFromAccountOption() const { return !is_opted_in; #else return false; -#endif // #if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_WIN) || \ +#endif // #if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_WIN) || \ // defined(OS_APPLE) } 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 c930dbc16a4..99b095478df 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc @@ -15,7 +15,7 @@ #include <vector> #include "base/base64.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/guid.h" #include "base/i18n/time_formatting.h" @@ -2035,7 +2035,6 @@ TEST_F(PersonalDataManagerTest, DefaultCountryCodeComesFromProfiles) { TEST_F(PersonalDataManagerTest, DefaultCountryCodeComesFromVariations) { base::test::ScopedFeatureList enabled; - enabled.InitAndEnableFeature(features::kAutofillUseVariationCountryCode); const std::string expected_country_code = "DE"; const std::string unepected_country_code = "FR"; diff --git a/chromium/components/autofill/core/browser/proto/BUILD.gn b/chromium/components/autofill/core/browser/proto/BUILD.gn index eb779aaae07..0aac1b74f7f 100644 --- a/chromium/components/autofill/core/browser/proto/BUILD.gn +++ b/chromium/components/autofill/core/browser/proto/BUILD.gn @@ -11,6 +11,7 @@ fuzzable_proto_library("proto") { "password_requirements.proto", "password_requirements_shard.proto", "server.proto", + "states.proto", "strike_data.proto", ] } diff --git a/chromium/components/autofill/core/browser/proto/server.proto b/chromium/components/autofill/core/browser/proto/server.proto index 1bc923e9f25..ea2d2a5f4b8 100644 --- a/chromium/components/autofill/core/browser/proto/server.proto +++ b/chromium/components/autofill/core/browser/proto/server.proto @@ -407,6 +407,7 @@ message AutofillUploadContents { DEPRECATED_FILLED_FORM_ON_START_PROVISIONAL_LOAD = 8; // unused DEPRECATED_FILLED_INPUT_ELEMENTS_ON_START_PROVISIONAL_LOAD = 9; // unused PROBABLE_FORM_SUBMISSION = 10; + CHANGE_PASSWORD_FORM_CLEARED = 11; } // The type of the event that was taken as an indication that the form has diff --git a/chromium/components/autofill/core/browser/proto/states.proto b/chromium/components/autofill/core/browser/proto/states.proto new file mode 100644 index 00000000000..3253823616b --- /dev/null +++ b/chromium/components/autofill/core/browser/proto/states.proto @@ -0,0 +1,31 @@ +// 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. + +syntax = "proto2"; + +package autofill; + +option optimize_for = LITE_RUNTIME; + +message StateEntry { + // Full name representing the state entry unique within a country. + // Example: + // "California" for "CA", "California", "The Golden State". + // "Bavaria" for "BY", "Bavaria", "Bayern". + optional string canonical_name = 1; + + // Abbreviations corresponding to the state entry. + repeated string abbreviations = 2; + + // Alternative names of the state. + repeated string alternative_names = 3; +} + +message StatesInCountry { + // Two digit country code. + optional string country_code = 1; + + // All the states belonging to the country. + repeated StateEntry states = 2; +} diff --git a/chromium/components/autofill/core/browser/rationalization_util_unittest.cc b/chromium/components/autofill/core/browser/rationalization_util_unittest.cc index 67ebfbe0af9..8ec974c7a65 100644 --- a/chromium/components/autofill/core/browser/rationalization_util_unittest.cc +++ b/chromium/components/autofill/core/browser/rationalization_util_unittest.cc @@ -10,7 +10,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_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_test_utils.h" #include "components/autofill/core/browser/autofill_type.h" diff --git a/chromium/components/autofill/core/browser/test_autofill_manager.h b/chromium/components/autofill/core/browser/test_autofill_manager.h index 51989ace0e5..2a5b103953c 100644 --- a/chromium/components/autofill/core/browser/test_autofill_manager.h +++ b/chromium/components/autofill/core/browser/test_autofill_manager.h @@ -72,6 +72,7 @@ class TestAutofillManager : public AutofillManager { void SetCallParentUploadFormData(bool value); using AutofillManager::is_rich_query_enabled; + using AutofillManager::pending_form_data; private: TestPersonalDataManager* personal_data_; // Weak reference. diff --git a/chromium/components/autofill/core/browser/test_autofill_profile_validator_delayed.cc b/chromium/components/autofill/core/browser/test_autofill_profile_validator_delayed.cc index ed92eae2fcf..7bdb471b44a 100644 --- a/chromium/components/autofill/core/browser/test_autofill_profile_validator_delayed.cc +++ b/chromium/components/autofill/core/browser/test_autofill_profile_validator_delayed.cc @@ -5,7 +5,7 @@ #include "components/autofill/core/browser/test_autofill_profile_validator_delayed.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/cancelable_callback.h" #include "base/threading/sequenced_task_runner_handle.h" diff --git a/chromium/components/autofill/core/browser/test_autofill_provider.cc b/chromium/components/autofill/core/browser/test_autofill_provider.cc index 2102a1974f9..ac9248b8d08 100644 --- a/chromium/components/autofill/core/browser/test_autofill_provider.cc +++ b/chromium/components/autofill/core/browser/test_autofill_provider.cc @@ -33,8 +33,8 @@ void TestAutofillProvider::OnSelectControlDidChange( const FormFieldData& field, const gfx::RectF& bounding_box) {} -void TestAutofillProvider::OnFocusNoLongerOnForm( - AutofillHandlerProxy* handler) {} +void TestAutofillProvider::OnFocusNoLongerOnForm(AutofillHandlerProxy* handler, + bool had_interacted_form) {} void TestAutofillProvider::OnFocusOnFormField(AutofillHandlerProxy* handler, const FormData& form, diff --git a/chromium/components/autofill/core/browser/test_autofill_provider.h b/chromium/components/autofill/core/browser/test_autofill_provider.h index 7a21e59c6bb..db7234195fa 100644 --- a/chromium/components/autofill/core/browser/test_autofill_provider.h +++ b/chromium/components/autofill/core/browser/test_autofill_provider.h @@ -37,7 +37,8 @@ class TestAutofillProvider : public AutofillProvider { const FormData& form, bool known_success, mojom::SubmissionSource source) override {} - void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler) override; + void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler, + bool had_interacted_form) override; void OnFocusOnFormField(AutofillHandlerProxy* handler, const FormData& form, const FormFieldData& field, diff --git a/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc b/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc index 03455605c04..49a051467f7 100644 --- a/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc @@ -11,7 +11,6 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" -#include "base/test/scoped_feature_list.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/credit_card.h" diff --git a/chromium/components/autofill/core/browser/ui/region_combobox_model.cc b/chromium/components/autofill/core/browser/ui/region_combobox_model.cc index 4779ab5f6c5..144e523b838 100644 --- a/chromium/components/autofill/core/browser/ui/region_combobox_model.cc +++ b/chromium/components/autofill/core/browser/ui/region_combobox_model.cc @@ -7,7 +7,7 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/geo/region_data_loader.h" #include "components/strings/grit/components_strings.h" diff --git a/chromium/components/autofill/core/browser/validation.cc b/chromium/components/autofill/core/browser/validation.cc index 33f0aa6dbde..deee50f2db7 100644 --- a/chromium/components/autofill/core/browser/validation.cc +++ b/chromium/components/autofill/core/browser/validation.cc @@ -14,12 +14,12 @@ #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_data_util.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" +#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/geo/phone_number_i18n.h" #include "components/autofill/core/browser/geo/state_names.h" #include "components/autofill/core/common/autofill_clock.h" -#include "components/autofill/core/common/autofill_regex_constants.h" -#include "components/autofill/core/common/autofill_regexes.h" #include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" 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 557cbac1485..9cda0484f05 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc @@ -12,6 +12,7 @@ #include <vector> #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/proto/autofill_sync.pb.h" #include "components/autofill/core/browser/webdata/autofill_table.h" @@ -287,7 +288,7 @@ void AutocompleteSyncBridge::CreateForWebDataServiceAndBackend( std::make_unique<AutocompleteSyncBridge>( web_data_backend, std::make_unique<ClientTagBasedModelTypeProcessor>( - syncer::AUTOFILL, /*dump_stack=*/base::RepeatingClosure()))); + syncer::AUTOFILL, /*dump_stack=*/base::DoNothing()))); } // static 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 d559076a9a4..4c580c90dc0 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 @@ -10,13 +10,13 @@ #include <vector> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -27,9 +27,9 @@ #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/mock_model_type_change_processor.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/webdata/common/web_database.h" #include "testing/gmock/include/gmock/gmock.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 627a0aac521..d6f098f58b0 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 @@ -9,6 +9,7 @@ #include <vector> #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/guid.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" @@ -60,7 +61,7 @@ void AutofillProfileSyncBridge::CreateForWebDataServiceAndBackend( std::make_unique<AutofillProfileSyncBridge>( std::make_unique<syncer::ClientTagBasedModelTypeProcessor>( syncer::AUTOFILL_PROFILE, - /*dump_stack=*/base::RepeatingClosure()), + /*dump_stack=*/base::DoNothing()), app_locale, web_data_backend)); } 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 f977b702cad..171f079d9f8 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 @@ -10,7 +10,7 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/feature_list.h" #include "base/files/scoped_temp_dir.h" #include "base/guid.h" @@ -18,7 +18,7 @@ #include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/time/time.h" @@ -35,12 +35,12 @@ #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/mock_model_type_change_processor.h" #include "components/sync/model/sync_data.h" #include "components/sync/model/sync_error_factory.h" -#include "components/sync/model/sync_error_factory_mock.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" #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_difference_tracker_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc index 403f270afec..03f8b7baef7 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 @@ -4,7 +4,7 @@ #include "components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/files/scoped_temp_dir.h" #include "base/strings/utf_string_conversions.h" #include "base/test/mock_callback.h" 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 4f639a23d22..aa3324298db 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc @@ -1044,9 +1044,10 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredAddresses) { AutofillProfile profile; profile.set_origin(std::string()); - profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STREET_ADDRESS, - ASCIIToUTF16("Street Address"), - VerificationStatus::kUserVerified); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, + ASCIIToUTF16("Street Name House Number Premise Subpremise"), + VerificationStatus::kUserVerified); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STREET_NAME, ASCIIToUTF16("Street Name"), VerificationStatus::kFormatted); @@ -1070,9 +1071,9 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredAddresses) { profile.SetRawInfoWithVerificationStatus( ADDRESS_HOME_COUNTRY, ASCIIToUTF16("DE"), VerificationStatus::kObserved); - profile.SetRawInfoWithVerificationStatus( - ADDRESS_HOME_DEPENDENT_STREET_NAME, ASCIIToUTF16("Dependent Street Name"), - VerificationStatus::kObserved); + profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_DEPENDENT_STREET_NAME, + ASCIIToUTF16(""), + VerificationStatus::kObserved); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_HOUSE_NUMBER, ASCIIToUTF16("House Number"), @@ -1131,7 +1132,7 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredAddresses) { EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_STREET_NAME), ASCIIToUTF16("Street Name")); EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_DEPENDENT_STREET_NAME), - ASCIIToUTF16("Dependent Street Name")); + ASCIIToUTF16("")); EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_HOUSE_NUMBER), ASCIIToUTF16("House Number")); EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_SUBPREMISE), @@ -1162,15 +1163,16 @@ TEST_F(AutofillTableTest, AutofillProfile profile; profile.set_origin(std::string()); - profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STREET_ADDRESS, - ASCIIToUTF16("Street Address"), - VerificationStatus::kUserVerified); + profile.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_ADDRESS, + ASCIIToUTF16("Street Name House Number Premise Subpremise"), + VerificationStatus::kUserVerified); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STREET_NAME, ASCIIToUTF16("Street Name"), VerificationStatus::kFormatted); - profile.SetRawInfoWithVerificationStatus( - ADDRESS_HOME_DEPENDENT_STREET_NAME, ASCIIToUTF16("Dependent Street Name"), - VerificationStatus::kObserved); + profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_DEPENDENT_STREET_NAME, + ASCIIToUTF16(""), + VerificationStatus::kObserved); profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_HOUSE_NUMBER, ASCIIToUTF16("House Number"), VerificationStatus::kUserVerified); @@ -1196,7 +1198,7 @@ TEST_F(AutofillTableTest, EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_STREET_NAME), ASCIIToUTF16("Street Name")); EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_DEPENDENT_STREET_NAME), - ASCIIToUTF16("Dependent Street Name")); + ASCIIToUTF16("")); EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_HOUSE_NUMBER), ASCIIToUTF16("House Number")); EXPECT_EQ(db_profile->GetRawInfo(ADDRESS_HOME_SUBPREMISE), 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 e43c36f6c40..c89d012e159 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 @@ -10,6 +10,7 @@ #include <vector> #include "base/base64.h" +#include "base/callback_helpers.h" #include "base/check_op.h" #include "base/notreached.h" #include "base/optional.h" @@ -310,7 +311,7 @@ void AutofillWalletMetadataSyncBridge::CreateForWebDataServiceAndBackend( std::make_unique<AutofillWalletMetadataSyncBridge>( std::make_unique<syncer::ClientTagBasedModelTypeProcessor>( syncer::AUTOFILL_WALLET_METADATA, - /*dump_stack=*/base::RepeatingClosure()), + /*dump_stack=*/base::DoNothing()), web_data_backend)); } 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 0cae0cf6b6d..f646c276c19 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 @@ -13,10 +13,10 @@ #include "base/base64.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/files/scoped_temp_dir.h" #include "base/run_loop.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "base/test/task_environment.h" #include "base/time/time.h" #include "components/autofill/core/browser/data_model/autofill_metadata.h" @@ -32,9 +32,9 @@ #include "components/sync/base/client_tag_hash.h" #include "components/sync/model/data_batch.h" #include "components/sync/model/entity_data.h" -#include "components/sync/model/mock_model_type_change_processor.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" #include "testing/gtest/include/gtest/gtest.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 c95389dba6d..bc4c93b79ba 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 @@ -6,6 +6,7 @@ #include <utility> +#include "base/callback_helpers.h" #include "base/logging.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/data_model/autofill_offer_data.h" @@ -47,7 +48,7 @@ void AutofillWalletOfferSyncBridge::CreateForWebDataServiceAndBackend( std::make_unique<AutofillWalletOfferSyncBridge>( std::make_unique<syncer::ClientTagBasedModelTypeProcessor>( syncer::AUTOFILL_WALLET_OFFER, - /*dump_stack=*/base::RepeatingClosure()), + /*dump_stack=*/base::DoNothing()), web_data_backend)); } 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 3132670cb95..5658421d159 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 @@ -12,7 +12,7 @@ #include "base/files/scoped_temp_dir.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" #include "base/time/time.h" @@ -26,12 +26,12 @@ #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/model/mock_model_type_change_processor.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/webdata/common/web_database.h" #include "testing/gtest/include/gtest/gtest.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 8cafefa1abb..47f257894e1 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 @@ -7,6 +7,7 @@ #include <utility> #include "base/base64.h" +#include "base/callback_helpers.h" #include "base/feature_list.h" #include "base/logging.h" #include "base/strings/string_util.h" @@ -146,7 +147,7 @@ void AutofillWalletSyncBridge::CreateForWebDataServiceAndBackend( std::make_unique<AutofillWalletSyncBridge>( std::make_unique<syncer::ClientTagBasedModelTypeProcessor>( syncer::AUTOFILL_WALLET_DATA, - /*dump_stack=*/base::RepeatingClosure()), + /*dump_stack=*/base::DoNothing()), web_data_backend)); } 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 540f2b9376b..ce186b41372 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 @@ -10,13 +10,12 @@ #include <utility> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/files/scoped_temp_dir.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/bind_test_util.h" +#include "base/test/bind.h" #include "base/test/mock_callback.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" @@ -36,12 +35,12 @@ #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/model/mock_model_type_change_processor.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/webdata/common/web_database.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc index 6487f6bb507..de0083a0599 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc @@ -5,7 +5,7 @@ #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/check.h" #include "base/location.h" #include "base/single_thread_task_runner.h" diff --git a/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc b/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc index dce7abdaf4a..b637f09bf1d 100644 --- a/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc @@ -7,7 +7,7 @@ #include <vector> #include "base/bind.h" -#include "base/bind_helpers.h" +#include "base/callback_helpers.h" #include "base/location.h" #include "base/macros.h" #include "base/memory/ptr_util.h" diff --git a/chromium/components/autofill/core/common/BUILD.gn b/chromium/components/autofill/core/common/BUILD.gn index 44d8b901795..948b48e1485 100644 --- a/chromium/components/autofill/core/common/BUILD.gn +++ b/chromium/components/autofill/core/common/BUILD.gn @@ -22,16 +22,13 @@ static_library("common") { "autofill_payments_features.h", "autofill_prefs.cc", "autofill_prefs.h", - "autofill_regex_constants.cc", - "autofill_regex_constants.h", - "autofill_regexes.cc", - "autofill_regexes.h", "autofill_switches.cc", "autofill_switches.h", "autofill_tick_clock.cc", "autofill_tick_clock.h", "autofill_util.cc", "autofill_util.h", + "dense_set.h", "field_data_manager.cc", "field_data_manager.h", "form_data.cc", @@ -46,11 +43,8 @@ static_library("common") { "gaia_id_hash.h", "logging/log_buffer.cc", "logging/log_buffer.h", - "password_form.cc", - "password_form.h", "password_form_fill_data.cc", "password_form_fill_data.h", - "password_form_generation_data.cc", "password_form_generation_data.h", "password_generation_util.cc", "password_generation_util.h", @@ -87,8 +81,8 @@ source_set("unit_tests") { "autofill_internals/logging_scope_unittest.cc", "autofill_l10n_util_unittest.cc", "autofill_prefs_unittest.cc", - "autofill_regexes_unittest.cc", "autofill_util_unittest.cc", + "dense_set_unittest.cc", "field_data_manager_unittest.cc", "form_data_unittest.cc", "form_field_data_unittest.cc", diff --git a/chromium/components/autofill/core/common/OWNERS b/chromium/components/autofill/core/common/OWNERS index 219017c1704..04e7201eab4 100644 --- a/chromium/components/autofill/core/common/OWNERS +++ b/chromium/components/autofill/core/common/OWNERS @@ -1,5 +1,3 @@ -per-file *password*=dvadym@chromium.org -per-file *password*=kolos@chromium.org -per-file *password*=vasilii@chromium.org +per-file *password*=file://components/password_manager/OWNERS per-file autofill_payments_features.*=file://components/autofill/core/browser/payments/OWNERS diff --git a/chromium/components/autofill/core/common/autofill_constants.cc b/chromium/components/autofill/core/common/autofill_constants.cc index f67d797bed9..5f9f040624a 100644 --- a/chromium/components/autofill/core/common/autofill_constants.cc +++ b/chromium/components/autofill/core/common/autofill_constants.cc @@ -12,25 +12,6 @@ namespace autofill { const char kSettingsOrigin[] = "Chrome settings"; -size_t MinRequiredFieldsForHeuristics() { - return base::FeatureList::IsEnabled( - autofill::features::kAutofillEnforceMinRequiredFieldsForHeuristics) - ? 3 - : 1; -} -size_t MinRequiredFieldsForQuery() { - return base::FeatureList::IsEnabled( - autofill::features::kAutofillEnforceMinRequiredFieldsForQuery) - ? 3 - : 1; -} -size_t MinRequiredFieldsForUpload() { - return base::FeatureList::IsEnabled( - autofill::features::kAutofillEnforceMinRequiredFieldsForUpload) - ? 3 - : 1; -} - bool IsAutofillEntryWithUseDateDeletable(const base::Time& use_date) { return use_date < AutofillClock::Now() - kDisusedDataModelDeletionTimeDelta; } diff --git a/chromium/components/autofill/core/common/autofill_constants.h b/chromium/components/autofill/core/common/autofill_constants.h index 84c5b916b03..07428810cc3 100644 --- a/chromium/components/autofill/core/common/autofill_constants.h +++ b/chromium/components/autofill/core/common/autofill_constants.h @@ -18,9 +18,9 @@ extern const char kSettingsOrigin[]; // The number of fields required by Autofill to execute its heuristic and // crowd-sourcing query/upload routines. -size_t MinRequiredFieldsForHeuristics(); -size_t MinRequiredFieldsForQuery(); -size_t MinRequiredFieldsForUpload(); +constexpr size_t kMinRequiredFieldsForHeuristics = 3; +constexpr size_t kMinRequiredFieldsForQuery = 1; +constexpr size_t kMinRequiredFieldsForUpload = 1; // The maximum number of form fields we are willing to parse, due to // computational costs. Several examples of forms with lots of fields that are diff --git a/chromium/components/autofill/core/common/autofill_features.cc b/chromium/components/autofill/core/common/autofill_features.cc index 50444cf12c2..349361c1c7b 100644 --- a/chromium/components/autofill/core/common/autofill_features.cc +++ b/chromium/components/autofill/core/common/autofill_features.cc @@ -24,6 +24,13 @@ namespace features { const base::Feature kAutofillAddressEnhancementVotes{ "kAutofillAddressEnhancementVotes", base::FEATURE_DISABLED_BY_DEFAULT}; +// TODO(crbug.com/1135188): Remove this feature flag after the explicit save +// prompts for address profiles is complete. +// When enabled, a save prompt will be shown to user upon form submission before +// storing any detected address profile. +const base::Feature kAutofillAddressProfileSavePrompt{ + "AutofillAddressProfileSavePrompt", base::FEATURE_DISABLED_BY_DEFAULT}; + // By default, AutofillAgent and, if |kAutofillProbableFormSubmissionInBrowser| // is enabled, also ContentAutofillDriver omit duplicate form submissions, even // though the form's data may have changed substantially. If enabled, the @@ -32,13 +39,6 @@ const base::Feature kAutofillAddressEnhancementVotes{ const base::Feature kAutofillAllowDuplicateFormSubmissions{ "AutofillAllowDuplicateFormSubmissions", base::FEATURE_DISABLED_BY_DEFAULT}; -// Controls if a full country name instead of a country code in a field with a -// type derived from HTML_TYPE_COUNTRY_CODE can be used to set the profile -// country. -const base::Feature kAutofillAllowHtmlTypeCountryCodesWithFullNames{ - "AutofillAllowHtmlTypeCountryCodesWithFullNames", - base::FEATURE_DISABLED_BY_DEFAULT}; - // Controls whether autofill activates on non-HTTP(S) pages. Useful for // automated with data URLS in cases where it's too difficult to use the // embedded test server. Generally avoid using. @@ -86,6 +86,28 @@ const base::Feature kAutofillEnableAugmentedPhoneCountryCode{ const base::Feature kAutofillEnableHideSuggestionsUI{ "AutofillEnableHideSuggestionsUI", 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. +const base::Feature + kAutofillEnableInfoBarAccountIndicationFooterForSingleAccountUsers{ + "AutofillEnableInfoBarAccountIndicationFooterForSingleAccountUsers", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// When enabled and user is syncing, 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. +const base::Feature kAutofillEnableInfoBarAccountIndicationFooterForSyncUsers{ + "AutofillEnableInfoBarAccountIndicationFooterForSyncUsers", + 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. +const base::Feature kAutofillEnablePasswordInfoBarAccountIndicationFooter{ + "AutofillEnablePasswordInfoBarAccountIndicationFooter", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls if Autofill supports new structure in names. // TODO(crbug.com/1098943): Remove once launched. const base::Feature kAutofillEnableSupportForMoreStructureInNames{ @@ -104,23 +126,12 @@ const base::Feature kAutofillEnableSupportForMergingSubsetNames{ "AutofillEnableSupportForMergingSubsetNames", base::FEATURE_DISABLED_BY_DEFAULT}; -// Controls whether or not a minimum number of fields is required before -// heuristic field type prediction is run for a form. -const base::Feature kAutofillEnforceMinRequiredFieldsForHeuristics{ - "AutofillEnforceMinRequiredFieldsForHeuristics", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Controls whether or not a minimum number of fields is required before -// crowd-sourced field type predictions are queried for a form. -const base::Feature kAutofillEnforceMinRequiredFieldsForQuery{ - "AutofillEnforceMinRequiredFieldsForQuery", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Controls whether or not a minimum number of fields is required before -// field type votes are uploaded to the crowd-sourcing server. -const base::Feature kAutofillEnforceMinRequiredFieldsForUpload{ - "AutofillEnforceMinRequiredFieldsForUpload", - base::FEATURE_DISABLED_BY_DEFAULT}; +// 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}; // Controls whether or not all datalist shall be extracted into FormFieldData. // This feature is enabled in both WebView and WebLayer where all datalists @@ -137,30 +148,17 @@ const base::Feature kAutofillExtractAllDatalists{ const base::Feature kAutofillFixFillableFieldTypes{ "AutofillFixFillableFieldTypes", base::FEATURE_DISABLED_BY_DEFAULT}; -// If enabled, prefilled country and state values are not reset before -// an address profile import. -// TODO(crbug.com/1100231): Remove once fix is tested. -const base::Feature kAutofillImportPrefilledCountryAndStateValues{ - "AutofillImportPrefilledCountryAndStateValues", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// When enabled, Autofill keeps the initial field values in the |FormStructure| -// cache for all field types. -const base::Feature kAutofillKeepInitialFormValuesInCache{ - "AutofillKeepCachedFormValues", base::FEATURE_ENABLED_BY_DEFAULT}; - -// When enabled, Autofill will use FieldRendererIds instead of unique_names -// to align forms in FormStructure::RetrieveFromCache(). -const base::Feature kAutofillRetrieveFromCacheWithRendererIds{ - "AutofillRetrieveFromCacheWithRendererIds", - 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. +const base::Feature kAutofillRefillWithRendererIds{ + "AutofillRefillWithRendererIds", base::FEATURE_DISABLED_BY_DEFAULT}; -// When enabled, Autofill will try to retrieve cached fields by signatures as a -// fallback that is useful if unique renderer ids are unstable. -// TODO(crbug.com/1125624): Remove experiment once trial ended. -const base::Feature kAutofillRetrieveFromCacheWithFieldSignatureAsFallback{ - "AutofillRetrieveFromCacheWithFieldSignatureAsFallback", - base::FEATURE_DISABLED_BY_DEFAULT}; +// When enabled, Autofill will use FormRendererIds instead of +// unique_name() to create unique section names. +// TODO(crbug/896689): Remove once experiment is finished. +const base::Feature kAutofillNameSectionsWithRendererIds{ + "AutofillNameSectionsWithRendererIds", base::FEATURE_DISABLED_BY_DEFAULT}; // When enabled, autofill suggestions are displayed in the keyboard accessory // instead of the regular popup. @@ -196,15 +194,6 @@ const base::Feature kAutofillProbableFormSubmissionInBrowser{ const base::Feature kAutofillProfileClientValidation{ "AutofillProfileClientValidation", base::FEATURE_DISABLED_BY_DEFAULT}; -const base::Feature kAutofillProfileImportFromUnifiedSection{ - "AutofillProfileImportFromUnifiedSection", - 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{ @@ -263,14 +252,17 @@ const base::Feature kAutofillSkipFillingFieldsWithChangedValues{ const base::Feature kAutofillTokenPrefixMatching{ "AutofillTokenPrefixMatching", base::FEATURE_DISABLED_BY_DEFAULT}; -// Enables the touch to fill feature for Android. -const base::Feature kAutofillTouchToFill = {"TouchToFillAndroid", - base::FEATURE_ENABLED_BY_DEFAULT}; - // Autofill upload throttling is used for testing. const base::Feature kAutofillUploadThrottling{"AutofillUploadThrottling", base::FEATURE_ENABLED_BY_DEFAULT}; +// Controls whether to use the AutofillUseAlternativeStateNameMap for filling +// of state selection fields, comparison of profiles and sending state votes to +// the server. +// TODO(crbug.com/1143516): Remove the feature when the experiment is completed. +const base::Feature kAutofillUseAlternativeStateNameMap{ + "AutofillUseAlternativeStateNameMap", base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether suggestions' labels use the improved label disambiguation // format. const base::Feature kAutofillUseImprovedLabelDisambiguation{ @@ -282,31 +274,12 @@ const base::Feature kAutofillUseImprovedLabelDisambiguation{ const base::Feature kAutofillUseNewSectioningMethod{ "AutofillUseNewSectioningMethod", base::FEATURE_DISABLED_BY_DEFAULT}; -// TODO(crbug.com/1075604): Remove once launched. -// Controls whether the page language is used as a fall-back locale to translate -// the country name when a profile is imported from a form. -const base::Feature kAutofillUsePageLanguageToTranslateCountryNames{ - "AutofillUsePageLanguageToTranslateCountryNames", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Controls whether to use the |ParseCityStateCountryZipCode| or not for -// predicting the heuristic type. -// |ParseCityStateCountryZipCode| is intended to prevent the misclassification -// of the country field into |ADDRESS_HOME_STATE| while determining the -// heuristic type. The misclassification happens sometimes because the regular -// expression for |ADDRESS_HOME_STATE| contains the term "region" which is also -// used for country selectors. -const base::Feature kAutofillUseParseCityStateCountryZipCodeInHeuristic{ - "AutofillUseParseCityStateCountryZipCodeInHeuristic", +// 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}; -// Controls whether or not autofill utilizes the country code from the Chrome -// variation service. The country code is used for determining the address -// requirements for address profile creation and as source for a default country -// used in a new address profile. -const base::Feature kAutofillUseVariationCountryCode{ - "AutofillUseVariationCountryCode", base::FEATURE_DISABLED_BY_DEFAULT}; - #if defined(OS_ANDROID) // Controls whether the Autofill manual fallback for Addresses and Payments is // present on Android. @@ -335,5 +308,13 @@ const base::Feature kAutofillUseUniqueRendererIDsOnIOS{ "AutofillUseUniqueRendererIDsOnIOS", base::FEATURE_DISABLED_BY_DEFAULT}; #endif +#if defined(OS_ANDROID) +// Controls whether the Wallet (GPay) integration requires first-sync-setup to +// be complete. +// TODO(crbug.com/1134564): Clean up after launch. +const base::Feature kWalletRequiresFirstSyncSetupComplete{ + "WalletRequiresFirstSyncSetupComplete", base::FEATURE_ENABLED_BY_DEFAULT}; +#endif + } // namespace features } // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_features.h b/chromium/components/autofill/core/common/autofill_features.h index 1bbf23972e7..8209085714b 100644 --- a/chromium/components/autofill/core/common/autofill_features.h +++ b/chromium/components/autofill/core/common/autofill_features.h @@ -22,8 +22,8 @@ namespace features { // All features in alphabetical order. extern const base::Feature kAutofillAddressEnhancementVotes; +extern const base::Feature kAutofillAddressProfileSavePrompt; extern const base::Feature kAutofillAllowDuplicateFormSubmissions; -extern const base::Feature kAutofillAllowHtmlTypeCountryCodesWithFullNames; extern const base::Feature kAutofillAllowNonHttpActivation; extern const base::Feature kAutofillAlwaysFillAddresses; extern const base::Feature @@ -33,20 +33,20 @@ extern const base::Feature kAutofillCreateDataForTest; extern const base::Feature kAutofillEnableAccountWalletStorage; extern const base::Feature kAutofillEnableAugmentedPhoneCountryCode; extern const base::Feature kAutofillEnableHideSuggestionsUI; +extern const base::Feature + kAutofillEnableInfoBarAccountIndicationFooterForSingleAccountUsers; +extern const base::Feature + kAutofillEnableInfoBarAccountIndicationFooterForSyncUsers; +extern const base::Feature + kAutofillEnablePasswordInfoBarAccountIndicationFooter; extern const base::Feature kAutofillEnableSupportForMoreStructureInNames; extern const base::Feature kAutofillEnableSupportForMoreStructureInAddresses; extern const base::Feature kAutofillEnableSupportForMergingSubsetNames; -extern const base::Feature kAutofillEnableSupportForHouseNumbers; -extern const base::Feature kAutofillEnforceMinRequiredFieldsForHeuristics; -extern const base::Feature kAutofillEnforceMinRequiredFieldsForQuery; -extern const base::Feature kAutofillEnforceMinRequiredFieldsForUpload; +extern const base::Feature kAutofillEnableUIForHonorificPrefixesInSettings; extern const base::Feature kAutofillExtractAllDatalists; extern const base::Feature kAutofillFixFillableFieldTypes; -extern const base::Feature kAutofillImportPrefilledCountryAndStateValues; -extern const base::Feature kAutofillKeepInitialFormValuesInCache; -extern const base::Feature kAutofillRetrieveFromCacheWithRendererIds; -extern const base::Feature - kAutofillRetrieveFromCacheWithFieldSignatureAsFallback; +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; @@ -55,8 +55,6 @@ extern const base::Feature kAutofillOffNoServerData; extern const base::Feature kAutofillPreventMixedFormsFilling; extern const base::Feature kAutofillProbableFormSubmissionInBrowser; extern const base::Feature kAutofillProfileClientValidation; -extern const base::Feature kAutofillProfileImportFromUnfocusableFields; -extern const base::Feature kAutofillProfileImportFromUnifiedSection; extern const base::Feature kAutofillProfileServerValidation; extern const base::Feature kAutofillRestrictUnownedFieldsToFormlessCheckout; extern const base::Feature kAutofillRichMetadataQueries; @@ -67,13 +65,11 @@ extern const base::Feature kAutofillShowTypePredictions; extern const base::Feature kAutofillSkipComparingInferredLabels; extern const base::Feature kAutofillSkipFillingFieldsWithChangedValues; extern const base::Feature kAutofillTokenPrefixMatching; -extern const base::Feature kAutofillTouchToFill; extern const base::Feature kAutofillUploadThrottling; +extern const base::Feature kAutofillUseAlternativeStateNameMap; extern const base::Feature kAutofillUseImprovedLabelDisambiguation; extern const base::Feature kAutofillUseNewSectioningMethod; -extern const base::Feature kAutofillUsePageLanguageToTranslateCountryNames; -extern const base::Feature kAutofillUseParseCityStateCountryZipCodeInHeuristic; -extern const base::Feature kAutofillUseVariationCountryCode; +extern const base::Feature kAutofillUsePageLanguageToSelectFieldParsingPatterns; #if defined(OS_ANDROID) extern const base::Feature kAutofillManualFallbackAndroid; @@ -97,6 +93,10 @@ bool IsMacViewsAutofillPopupExperimentEnabled(); extern const base::Feature kAutofillUseUniqueRendererIDsOnIOS; #endif // OS_IOS +#if defined(OS_ANDROID) +extern const base::Feature kWalletRequiresFirstSyncSetupComplete; +#endif + } // namespace features } // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_payments_features.cc b/chromium/components/autofill/core/common/autofill_payments_features.cc index 5d89d6e7431..8d74f541c6c 100644 --- a/chromium/components/autofill/core/common/autofill_payments_features.cc +++ b/chromium/components/autofill/core/common/autofill_payments_features.cc @@ -84,6 +84,12 @@ const base::Feature kAutofillEnableGoogleIssuedCard{ const base::Feature kAutofillEnableOffersInDownstream{ "kAutofillEnableOffersInDownstream", 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 SaveCardInfoBar. +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{ diff --git a/chromium/components/autofill/core/common/autofill_payments_features.h b/chromium/components/autofill/core/common/autofill_payments_features.h index 29ab241633c..4cadf5421ad 100644 --- a/chromium/components/autofill/core/common/autofill_payments_features.h +++ b/chromium/components/autofill/core/common/autofill_payments_features.h @@ -30,6 +30,8 @@ extern const base::Feature kAutofillEnableCardNicknameUpstream; extern const base::Feature kAutofillEnableFixedPaymentsBubbleLogging; extern const base::Feature kAutofillEnableGoogleIssuedCard; 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; diff --git a/chromium/components/autofill/core/common/autofill_prefs.cc b/chromium/components/autofill/core/common/autofill_prefs.cc index c31f2e3c56b..23a0ce36f01 100644 --- a/chromium/components/autofill/core/common/autofill_prefs.cc +++ b/chromium/components/autofill/core/common/autofill_prefs.cc @@ -185,11 +185,12 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { registry->RegisterTimePref(prefs::kAutofillUploadEventsLastResetTimestamp, base::Time()); registry->RegisterDictionaryPref(prefs::kAutofillSyncTransportOptIn); - registry->RegisterStringPref(prefs::kAutofillStatesDataDir, ""); // Deprecated prefs registered for migration. registry->RegisterBooleanPref(kAutofillJapanCityFieldMigratedDeprecated, false); + // Deprecated in profile prefs. + registry->RegisterStringPref(prefs::kAutofillStatesDataDir, ""); } void MigrateDeprecatedAutofillPrefs(PrefService* prefs) { @@ -223,6 +224,11 @@ void MigrateDeprecatedAutofillPrefs(PrefService* prefs) { // Added 10/2019. prefs->ClearPref(kAutofillJapanCityFieldMigratedDeprecated); + + // Added 11/2020 + // TODO(crbug.com/1147852): Remove deprecated kAutofillStatesDataDir from + // autofill profile prefs. + prefs->ClearPref(kAutofillStatesDataDir); } bool IsAutocompleteEnabled(const PrefService* prefs) { diff --git a/chromium/components/autofill/core/common/autofill_regexes_unittest.cc b/chromium/components/autofill/core/common/autofill_regexes_unittest.cc deleted file mode 100644 index 4201bceef30..00000000000 --- a/chromium/components/autofill/core/common/autofill_regexes_unittest.cc +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/autofill/core/common/autofill_regexes.h" - -#include <stddef.h> - -#include "base/macros.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/common/autofill_regex_constants.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::ASCIIToUTF16; - -namespace autofill { - -struct InputPatternTestCase { - const char* const input; - const char* const pattern; - }; - - class PositiveSampleTest - : public testing::TestWithParam<InputPatternTestCase> {}; - - TEST_P(PositiveSampleTest, SampleRegexes) { - auto test_case = GetParam(); - SCOPED_TRACE(test_case.input); - SCOPED_TRACE(test_case.pattern); - EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), - ASCIIToUTF16(test_case.pattern))); - } - - INSTANTIATE_TEST_SUITE_P(AutofillRegexes, - PositiveSampleTest, - testing::Values( - // Empty pattern - InputPatternTestCase{"", ""}, - InputPatternTestCase{ - "Look, ma' -- a non-empty string!", ""}, - // Substring - InputPatternTestCase{"string", "tri"}, - // Substring at beginning - InputPatternTestCase{"string", "str"}, - InputPatternTestCase{"string", "^str"}, - // Substring at end - InputPatternTestCase{"string", "ring"}, - InputPatternTestCase{"string", "ring$"}, - // Case-insensitive - InputPatternTestCase{"StRiNg", "string"})); - - class NegativeSampleTest - : public testing::TestWithParam<InputPatternTestCase> {}; - - TEST_P(NegativeSampleTest, SampleRegexes) { - auto test_case = GetParam(); - SCOPED_TRACE(test_case.input); - SCOPED_TRACE(test_case.pattern); - EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), - ASCIIToUTF16(test_case.pattern))); -} - -INSTANTIATE_TEST_SUITE_P(AutofillRegexes, - NegativeSampleTest, - testing::Values( - // Empty string - InputPatternTestCase{ - "", "Look, ma' -- a non-empty pattern!"}, - // Substring - InputPatternTestCase{"string", "trn"}, - // Substring at beginning - InputPatternTestCase{"string", " str"}, - InputPatternTestCase{"string", "^tri"}, - // Substring at end - InputPatternTestCase{"string", "ring "}, - InputPatternTestCase{"string", "rin$"})); - -struct InputTestCase { - const char* const input; - }; - - class ExpirationDate2DigitYearPositive - : public testing::TestWithParam<InputTestCase> {}; - - TEST_P(ExpirationDate2DigitYearPositive, ExpirationDate2DigitYearRegexes) { - auto test_case = GetParam(); - SCOPED_TRACE(test_case.input); - const base::string16 pattern = ASCIIToUTF16(kExpirationDate2DigitYearRe); - EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); - } - - INSTANTIATE_TEST_SUITE_P( - AutofillRegexes, - ExpirationDate2DigitYearPositive, - testing::Values(InputTestCase{"mm / yy"}, - InputTestCase{"mm/ yy"}, - InputTestCase{"mm /yy"}, - InputTestCase{"mm/yy"}, - InputTestCase{"mm - yy"}, - InputTestCase{"mm- yy"}, - InputTestCase{"mm -yy"}, - InputTestCase{"mm-yy"}, - InputTestCase{"mmyy"}, - // Complex two year cases - InputTestCase{"Expiration Date (MM / YY)"}, - InputTestCase{"Expiration Date (MM/YY)"}, - InputTestCase{"Expiration Date (MM - YY)"}, - InputTestCase{"Expiration Date (MM-YY)"}, - InputTestCase{"Expiration Date MM / YY"}, - InputTestCase{"Expiration Date MM/YY"}, - InputTestCase{"Expiration Date MM - YY"}, - InputTestCase{"Expiration Date MM-YY"}, - InputTestCase{"expiration date yy"}, - InputTestCase{"Exp Date (MM / YY)"})); - - class ExpirationDate2DigitYearNegative - : public testing::TestWithParam<InputTestCase> {}; - - TEST_P(ExpirationDate2DigitYearNegative, ExpirationDate2DigitYearRegexes) { - auto test_case = GetParam(); - SCOPED_TRACE(test_case.input); - const base::string16 pattern = ASCIIToUTF16(kExpirationDate2DigitYearRe); - EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); - } - - INSTANTIATE_TEST_SUITE_P( - AutofillRegexes, - ExpirationDate2DigitYearNegative, - testing::Values(InputTestCase{""}, - InputTestCase{"Look, ma' -- an invalid string!"}, - InputTestCase{"mmfavouritewordyy"}, - InputTestCase{"mm a yy"}, - InputTestCase{"mm a yyyy"}, - // Simple four year cases - InputTestCase{"mm / yyyy"}, - InputTestCase{"mm/ yyyy"}, - InputTestCase{"mm /yyyy"}, - InputTestCase{"mm/yyyy"}, - InputTestCase{"mm - yyyy"}, - InputTestCase{"mm- yyyy"}, - InputTestCase{"mm -yyyy"}, - InputTestCase{"mm-yyyy"}, - InputTestCase{"mmyyyy"}, - // Complex four year cases - InputTestCase{"Expiration Date (MM / YYYY)"}, - InputTestCase{"Expiration Date (MM/YYYY)"}, - InputTestCase{"Expiration Date (MM - YYYY)"}, - InputTestCase{"Expiration Date (MM-YYYY)"}, - InputTestCase{"Expiration Date MM / YYYY"}, - InputTestCase{"Expiration Date MM/YYYY"}, - InputTestCase{"Expiration Date MM - YYYY"}, - InputTestCase{"Expiration Date MM-YYYY"}, - InputTestCase{"expiration date yyyy"}, - InputTestCase{"Exp Date (MM / YYYY)"})); - - class ExpirationDate4DigitYearPositive - : public testing::TestWithParam<InputTestCase> {}; - - TEST_P(ExpirationDate4DigitYearPositive, ExpirationDate4DigitYearRegexes) { - auto test_case = GetParam(); - const base::string16 pattern = ASCIIToUTF16(kExpirationDate4DigitYearRe); - SCOPED_TRACE(test_case.input); - EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); - } - - INSTANTIATE_TEST_SUITE_P(AutofillRegexes, - ExpirationDate4DigitYearPositive, - testing::Values( - // Simple four year cases - InputTestCase{"mm / yyyy"}, - InputTestCase{"mm/ yyyy"}, - InputTestCase{"mm /yyyy"}, - InputTestCase{"mm/yyyy"}, - InputTestCase{"mm - yyyy"}, - InputTestCase{"mm- yyyy"}, - InputTestCase{"mm -yyyy"}, - InputTestCase{"mm-yyyy"}, - InputTestCase{"mmyyyy"}, - // Complex four year cases - InputTestCase{"Expiration Date (MM / YYYY)"}, - InputTestCase{"Expiration Date (MM/YYYY)"}, - InputTestCase{"Expiration Date (MM - YYYY)"}, - InputTestCase{"Expiration Date (MM-YYYY)"}, - InputTestCase{"Expiration Date MM / YYYY"}, - InputTestCase{"Expiration Date MM/YYYY"}, - InputTestCase{"Expiration Date MM - YYYY"}, - InputTestCase{"Expiration Date MM-YYYY"}, - InputTestCase{"expiration date yyyy"}, - InputTestCase{"Exp Date (MM / YYYY)"})); - - class ExpirationDate4DigitYearNegative - : public testing::TestWithParam<InputTestCase> {}; - - TEST_P(ExpirationDate4DigitYearNegative, ExpirationDate4DigitYearRegexes) { - auto test_case = GetParam(); - const base::string16 pattern = ASCIIToUTF16(kExpirationDate4DigitYearRe); - SCOPED_TRACE(test_case.input); - EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); -} - -INSTANTIATE_TEST_SUITE_P( - AutofillRegexes, - ExpirationDate4DigitYearNegative, - testing::Values(InputTestCase{""}, - InputTestCase{"Look, ma' -- an invalid string!"}, - InputTestCase{"mmfavouritewordyy"}, - InputTestCase{"mm a yy"}, - InputTestCase{"mm a yyyy"}, - // Simple two year cases - InputTestCase{"mm / yy"}, - InputTestCase{"mm/ yy"}, - InputTestCase{"mm /yy"}, - InputTestCase{"mm/yy"}, - InputTestCase{"mm - yy"}, - InputTestCase{"mm- yy"}, - InputTestCase{"mm -yy"}, - InputTestCase{"mm-yy"}, - InputTestCase{"mmyy"}, - // Complex two year cases - InputTestCase{"Expiration Date (MM / YY)"}, - InputTestCase{"Expiration Date (MM/YY)"}, - InputTestCase{"Expiration Date (MM - YY)"}, - InputTestCase{"Expiration Date (MM-YY)"}, - InputTestCase{"Expiration Date MM / YY"}, - InputTestCase{"Expiration Date MM/YY"}, - InputTestCase{"Expiration Date MM - YY"}, - InputTestCase{"Expiration Date MM-YY"}, - InputTestCase{"expiration date yy"}, - InputTestCase{"Exp Date (MM / YY)"})); - -} // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_util.cc b/chromium/components/autofill/core/common/autofill_util.cc index f738895aa7f..d9a17f403a6 100644 --- a/chromium/components/autofill/core/common/autofill_util.cc +++ b/chromium/components/autofill/core/common/autofill_util.cc @@ -65,7 +65,7 @@ bool IsKeyboardAccessoryEnabled() { bool IsTouchToFillEnabled() { #if defined(OS_ANDROID) - return base::FeatureList::IsEnabled(features::kAutofillTouchToFill); + return true; #else // !defined(OS_ANDROID) return false; #endif diff --git a/chromium/components/autofill/core/common/dense_set.h b/chromium/components/autofill/core/common/dense_set.h new file mode 100644 index 00000000000..cd52b04a025 --- /dev/null +++ b/chromium/components/autofill/core/common/dense_set.h @@ -0,0 +1,278 @@ +// 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_DENSE_SET_H_ +#define COMPONENTS_AUTOFILL_CORE_COMMON_DENSE_SET_H_ + +#include <bitset> +#include <cstddef> +#include <iterator> +#include <type_traits> + +#include "base/check.h" +#include "base/check_op.h" +#include "base/numerics/safe_conversions.h" + +namespace autofill { + +// A set container with a std::set<T>-like interface for integral or enum types +// T that have a dense and small representation as unsigned integers. +// +// The order of the elements in the container corresponds to their integer +// representation. +// +// The lower and upper bounds of elements storable in a container are +// [T(0), kEnd). +// +// 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. +// +// 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> +class DenseSet { + private: + using Index = std::make_unsigned_t<T>; + + public: + // A bidirectional iterator for the DenseSet. + class Iterator { + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = T; + + constexpr Iterator() = default; + + friend bool operator==(const Iterator& a, const Iterator& b) { + DCHECK(a.owner_); + DCHECK_EQ(a.owner_, b.owner_); + return a.index_ == b.index_; + } + + friend bool operator!=(const Iterator& a, const Iterator& b) { + return !(a == b); + } + + T operator*() const { + DCHECK(derefenceable()); + return index_to_value(index_); + } + + Iterator& operator++() { + ++index_; + Skip(kForward); + return *this; + } + + Iterator operator++(int) { + auto that = *this; + operator++(); + return that; + } + + Iterator& operator--() { + --index_; + Skip(kBackward); + return *this; + } + + Iterator operator--(int) { + auto that = *this; + operator--(); + return that; + } + + private: + friend DenseSet; + + enum Direction { kBackward = -1, kForward = 1 }; + + constexpr Iterator(const DenseSet* owner, Index index) + : owner_(owner), index_(index) {} + + // Advances the index, starting from the current position, to the next + // non-empty one. std::bitset does not offer a find-next-set operation. + void Skip(Direction direction) { + DCHECK_LE(index_, owner_->max_size()); + while (index_ < owner_->max_size() && !derefenceable()) { + index_ += direction; + } + } + + bool derefenceable() const { + DCHECK_LT(index_, owner_->max_size()); + return owner_->bitset_.test(index_); + } + + const DenseSet* owner_ = nullptr; + + // The current index is in the interval [0, owner_->max_size()]. + Index index_ = 0; + }; + + using value_type = T; + using iterator = Iterator; + using const_iterator = Iterator; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + constexpr DenseSet() = default; + + DenseSet(std::initializer_list<T> init) { + for (const auto& x : init) { + insert(x); + } + } + + template <typename InputIt> + DenseSet(InputIt first, InputIt last) { + for (auto it = first; it != last; ++it) { + insert(*it); + } + } + + friend bool operator==(const DenseSet& a, const DenseSet& b) { + return a.bitset_ == b.bitset_; + } + + friend bool operator!=(const DenseSet& a, const DenseSet& b) { + return !(a == b); + } + + // Iterators. + + // Returns an iterator to the beginning. + iterator begin() const { + const_iterator it(this, 0); + it.Skip(Iterator::kForward); + return it; + } + const_iterator cbegin() const { return begin(); } + + // Returns an iterator to the end. + iterator end() const { return iterator(this, max_size()); } + const_iterator cend() const { return end(); } + + // Returns a reverse iterator to the beginning. + reverse_iterator rbegin() const { return reverse_iterator(end()); } + const_reverse_iterator crbegin() const { return rbegin(); } + + // Returns a reverse iterator to the end. + reverse_iterator rend() const { return reverse_iterator(begin()); } + const_reverse_iterator crend() const { return rend(); } + + // Capacity. + + // Returns true if the set is empty, otherwise false. + bool empty() const { return bitset_.none(); } + + // Returns the number of elements the set has. + size_t size() const { return bitset_.count(); } + + // Returns the maximum number of elements the set can have. + size_t max_size() const { return bitset_.size(); } + + // Modifiers. + + // Clears the contents. + void clear() { bitset_.reset(); } + + // Inserts value |x| if it is not present yet, and returns an iterator to the + // inserted or existing element and a boolean that indicates whether the + // insertion took place. + std::pair<iterator, bool> insert(T x) { + bool contained = contains(x); + bitset_.set(value_to_index(x)); + return {find(x), !contained}; + } + + // 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) { + bool contained = contains(x); + bitset_.reset(value_to_index(x)); + return contained ? 1 : 0; + } + + // Erases the element |*it| and returns an iterator to its successor. + iterator erase(const_iterator it) { + DCHECK(it.owner_ == this && it.derefenceable()); + bitset_.reset(it.index_); + it.Skip(const_iterator::kForward); + return it; + } + + // Erases the elements [first,last) and returns |last|. + iterator erase(const_iterator first, const_iterator last) { + DCHECK(first.owner_ == this && last.owner_ == this); + while (first != last) { + bitset_.reset(first.index_); + ++first; + } + return last; + } + + // Lookup. + + // Returns 1 if |x| is an element, otherwise 0. + size_t count(T x) const { return contains(x) ? 1 : 0; } + + // Returns an iterator to the element |x| if it exists, otherwise end(). + const_iterator find(T x) const { + return contains(x) ? const_iterator(this, value_to_index(x)) : cend(); + } + + // Returns true if |x| is an element, else |false|. + bool contains(T x) const { return bitset_.test(value_to_index(x)); } + + // 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)); + it.Skip(Iterator::kForward); + return it; + } + + // Returns an iterator to the first element greater than |x|, or end(). + const_iterator upper_bound(T x) const { + const_iterator it(this, value_to_index(x) + 1); + it.Skip(Iterator::kForward); + return it; + } + + private: + friend Iterator; + + struct Wrapper { + using type = T; + }; + + static constexpr Index value_to_index(T x) { + DCHECK(index_to_value(0) <= x && x < kEnd); + return base::checked_cast<Index>(x); + } + + static constexpr T index_to_value(Index i) { + DCHECK_LT(i, base::checked_cast<Index>(kEnd)); + using UnderlyingType = + typename std::conditional_t<std::is_enum<T>::value, + std::underlying_type<T>, Wrapper>::type; + return static_cast<T>(base::checked_cast<UnderlyingType>(i)); + } + + static_assert(std::is_integral<T>::value || std::is_enum<T>::value, ""); + static_assert(index_to_value(0) <= kEnd, ""); + + std::bitset<base::checked_cast<Index>(kEnd)> bitset_{}; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_COMMON_BITSET_H_ diff --git a/chromium/components/autofill/core/common/dense_set_unittest.cc b/chromium/components/autofill/core/common/dense_set_unittest.cc new file mode 100644 index 00000000000..c6df3972962 --- /dev/null +++ b/chromium/components/autofill/core/common/dense_set_unittest.cc @@ -0,0 +1,484 @@ +// 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/common/dense_set.h" + +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/ranges/algorithm.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +TEST(DenseSet, initialization) { + enum class T : size_t { + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, + kEnd = 6 + }; + using DS = DenseSet<T, T::kEnd>; + + DS s; + EXPECT_TRUE(s.empty()); + EXPECT_EQ(s.size(), 0u); + EXPECT_EQ(DS(s.begin(), s.end()), s); + s.insert(T::Two); + s.insert(T::Four); + s.insert(T::One); + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(DS(s.begin(), s.end()), s); + EXPECT_EQ(DS(s.cbegin(), s.cend()), s); + EXPECT_EQ(DS(s.rbegin(), s.rend()), s); + EXPECT_EQ(DS(s.crbegin(), s.crend()), s); + EXPECT_EQ(DS({T::Four, T::Two, T::One}), s); +} + +TEST(DenseSet, iterators_begin_end) { + enum class T : int { + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, + kEnd = 6 + }; + using DS = DenseSet<T, T::kEnd>; + + DS s; + s.insert(T::Two); + s.insert(T::Four); + s.insert(T::One); + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(std::distance(s.begin(), s.end()), 3); + + { + auto it = s.begin(); + auto x1 = *it++; + auto x2 = *it++; + auto x3 = *it++; + EXPECT_EQ(it, s.end()); + EXPECT_EQ(x1, T::One); + EXPECT_EQ(x2, T::Two); + EXPECT_EQ(x3, T::Four); + } + + { + auto it = s.begin(); + auto x1 = *it; + auto x2 = *++it; + auto x3 = *++it; + EXPECT_NE(it, s.end()); + EXPECT_EQ(x1, T::One); + EXPECT_EQ(x2, T::Two); + EXPECT_EQ(x3, T::Four); + } + + EXPECT_THAT(s, ::testing::ElementsAre(T::One, T::Two, T::Four)); +} + +TEST(DenseSet, iterators_begin_end_reverse) { + enum class T : char { + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, + kEnd = 6 + }; + using DS = DenseSet<T, T::kEnd>; + + DS s; + s.insert(T::Two); + s.insert(T::Four); + s.insert(T::One); + EXPECT_EQ(s.size(), 3u); + + { + auto it = s.end(); + it--; + auto x3 = *it--; + auto x2 = *it--; + auto x1 = *it; + EXPECT_EQ(it, s.begin()); + EXPECT_EQ(x1, T::One); + EXPECT_EQ(x2, T::Two); + EXPECT_EQ(x3, T::Four); + } + + { + auto it = s.end(); + auto x3 = *--it; + auto x2 = *--it; + auto x1 = *--it; + EXPECT_EQ(it, s.begin()); + EXPECT_EQ(x1, T::One); + EXPECT_EQ(x2, T::Two); + EXPECT_EQ(x3, T::Four); + } +} + +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>; + + DS s; + s.insert(T::Two); + s.insert(T::Four); + s.insert(T::One); + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(std::distance(s.rbegin(), s.rend()), 3); + + { + auto it = s.rbegin(); + auto x3 = *it++; + auto x2 = *it++; + auto x1 = *it++; + EXPECT_EQ(it, s.rend()); + EXPECT_EQ(x1, T::One); + EXPECT_EQ(x2, T::Two); + EXPECT_EQ(x3, T::Four); + } + + { + auto it = s.rbegin(); + auto x3 = *it; + auto x2 = *++it; + auto x1 = *++it; + EXPECT_NE(it, s.rend()); + EXPECT_EQ(x1, T::One); + EXPECT_EQ(x2, T::Two); + EXPECT_EQ(x3, T::Four); + } + + EXPECT_THAT(std::vector<T>(s.rbegin(), s.rend()), + ::testing::ElementsAre(T::Four, T::Two, T::One)); +} + +TEST(DenseSet, lookup) { + enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5, kEnd = 6 }; + using DS = DenseSet<T, T::kEnd>; + + DS s; + s.insert(T::Two); + s.insert(T::Four); + s.insert(T::One); + + EXPECT_FALSE(s.contains(static_cast<T>(0))); + EXPECT_TRUE(s.contains(T::One)); + EXPECT_TRUE(s.contains(T::Two)); + EXPECT_FALSE(s.contains(T::Three)); + EXPECT_TRUE(s.contains(T::Four)); + EXPECT_FALSE(s.contains(T::Five)); + + EXPECT_EQ(s.contains(static_cast<T>(0)), 0u); + EXPECT_EQ(s.contains(T::One), 1u); + EXPECT_EQ(s.contains(T::Two), 1u); + EXPECT_EQ(s.contains(T::Three), 0u); + EXPECT_EQ(s.contains(T::Four), 1u); + EXPECT_EQ(s.contains(T::Five), 0u); + + EXPECT_EQ(s.find(static_cast<T>(0)), s.end()); + EXPECT_NE(s.find(T::One), s.end()); + EXPECT_NE(s.find(T::Two), s.end()); + EXPECT_EQ(s.find(T::Three), s.end()); + EXPECT_NE(s.find(T::Four), s.end()); + EXPECT_EQ(s.find(T::Five), s.end()); + + EXPECT_EQ(*s.find(T::One), T::One); + EXPECT_EQ(*s.find(T::Two), T::Two); + EXPECT_EQ(*s.find(T::Four), T::Four); + + EXPECT_NE(s.find(static_cast<T>(0)), s.lower_bound(static_cast<T>(0))); + EXPECT_EQ(s.find(T::One), s.lower_bound(T::One)); + EXPECT_EQ(s.find(T::Two), s.lower_bound(T::Two)); + 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)); +} + +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>; + + DS s; + s.insert(T::Two); + s.insert(T::Four); + s.insert(T::One); + EXPECT_EQ(s.size(), 3u); + + EXPECT_EQ(s.lower_bound(static_cast<T>(0)), s.begin()); + EXPECT_EQ(s.lower_bound(T::One), s.begin()); + + EXPECT_EQ(s.upper_bound(T::Four), s.end()); + EXPECT_EQ(s.upper_bound(T::Five), s.end()); + + { + auto it = s.lower_bound(static_cast<T>(0)); + auto jt = s.upper_bound(static_cast<T>(0)); + EXPECT_EQ(it, jt); + } + + { + auto it = s.lower_bound(T::One); + auto jt = s.upper_bound(T::One); + auto x1 = *it++; + EXPECT_EQ(it, jt); + EXPECT_EQ(x1, T::One); + } + + { + auto it = s.lower_bound(T::Four); + auto jt = s.upper_bound(T::Four); + auto x3 = *it++; + EXPECT_EQ(it, jt); + EXPECT_EQ(x3, T::Four); + } + + { + auto it = s.lower_bound(T::Five); + auto jt = s.upper_bound(T::Five); + EXPECT_EQ(it, jt); + } + + { + auto it = s.lower_bound(T::One); + auto jt = s.upper_bound(T::Five); + auto x1 = *it++; + auto x2 = *it++; + auto x3 = *it++; + EXPECT_EQ(it, jt); + EXPECT_EQ(x1, T::One); + EXPECT_EQ(x2, T::Two); + EXPECT_EQ(x3, T::Four); + } + + { + auto it = s.lower_bound(T::Three); + auto jt = s.upper_bound(T::Four); + auto x3 = *it++; + EXPECT_EQ(jt, s.end()); + EXPECT_EQ(it, jt); + EXPECT_EQ(x3, T::Four); + } + + EXPECT_EQ(static_cast<size_t>(std::distance(s.begin(), s.end())), s.size()); + EXPECT_EQ(std::next(std::next(std::next(s.begin()))), s.end()); +} + +TEST(DenseSet, max_size) { + const int One = 1; + const int Two = 2; + // const int Three = 3; + const int Four = 4; + // const int Five = 5; + const int kEnd = 6; + using DS = DenseSet<int, kEnd>; + + DS s; + EXPECT_TRUE(s.empty()); + EXPECT_EQ(s.size(), 0u); + EXPECT_EQ(s.max_size(), 6u); + s.insert(Two); + EXPECT_FALSE(s.empty()); + EXPECT_EQ(s.size(), 1u); + s.insert(Four); + EXPECT_FALSE(s.empty()); + EXPECT_EQ(s.size(), 2u); + s.insert(One); + EXPECT_FALSE(s.empty()); + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(s.max_size(), 6u); +} + +TEST(DenseSet, modifiers) { + const size_t One = 1; + const size_t Two = 2; + 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>; + + DS s; + s.insert(Two); + s.insert(Four); + s.insert(One); + EXPECT_EQ(s.size(), 3u); + + auto EXPECT_INSERTION = [](auto& set, auto value, bool took_place) { + auto it = set.insert(value); + EXPECT_EQ(it, std::make_pair(set.find(value), took_place)); + }; + + DS t; + EXPECT_NE(s, t); + EXPECT_INSERTION(t, Two, true); + EXPECT_INSERTION(t, Two, false); + EXPECT_INSERTION(t, Four, true); + EXPECT_INSERTION(t, Four, false); + EXPECT_INSERTION(t, One, true); + EXPECT_INSERTION(t, One, false); + EXPECT_EQ(s, t); + EXPECT_EQ(t.size(), 3u); + + EXPECT_INSERTION(t, Three, true); + EXPECT_INSERTION(t, Three, false); + EXPECT_EQ(t.erase(Three), 1u); + EXPECT_EQ(t.erase(Three), 0u); + EXPECT_EQ(s, t); + EXPECT_EQ(t.size(), 3u); + + EXPECT_EQ(s.erase(One), 1u); + EXPECT_EQ(t.erase(Four), 1u); + EXPECT_NE(s, t); + EXPECT_EQ(s.size(), 2u); + EXPECT_EQ(t.size(), 2u); + + EXPECT_INSERTION(s, One, true); + EXPECT_INSERTION(t, Four, true); + EXPECT_EQ(s, t); + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(t.size(), 3u); + + EXPECT_EQ(s.erase(s.find(One)), s.find(Two)); + EXPECT_EQ(t.erase(t.lower_bound(One), t.upper_bound(One)), t.find(Two)); + EXPECT_FALSE(s.contains(One)); + EXPECT_EQ(s, t); + EXPECT_EQ(s.size(), 2u); + EXPECT_EQ(t.size(), 2u); + + EXPECT_INSERTION(s, One, true); + EXPECT_INSERTION(t, One, true); + EXPECT_EQ(s, t); + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(t.size(), 3u); + + EXPECT_EQ(s.erase(s.find(Two), s.end()), s.end()); + EXPECT_EQ(t.erase(t.lower_bound(Two), t.upper_bound(Four)), t.end()); + EXPECT_TRUE(s.contains(One)); + EXPECT_EQ(s, t); + EXPECT_EQ(s.size(), 1u); + EXPECT_EQ(t.size(), 1u); + + EXPECT_INSERTION(s, Two, true); + EXPECT_INSERTION(t, Two, true); + EXPECT_INSERTION(s, Four, true); + EXPECT_INSERTION(t, Four, true); + EXPECT_EQ(s, t); + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(t.size(), 3u); + + s.clear(); + EXPECT_EQ(s, DS()); + EXPECT_EQ(s.size(), 0u); + + EXPECT_INSERTION(s, *t.begin(), true); + EXPECT_TRUE(s.contains(One)); + EXPECT_INSERTION(s, *std::next(t.begin()), true); + EXPECT_TRUE(s.contains(Two)); + EXPECT_INSERTION(s, *std::prev(t.end()), true); + EXPECT_TRUE(s.contains(Four)); + EXPECT_EQ(s, t); + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(t.size(), 3u); +} + +TEST(DenseSet, std_set) { + constexpr size_t kEnd = 50; + DenseSet<size_t, kEnd> dense_set; + std::set<size_t> std_set; + + auto expect_equivalence = [&] { + EXPECT_EQ(dense_set.empty(), std_set.empty()); + EXPECT_EQ(dense_set.size(), std_set.size()); + EXPECT_TRUE(base::ranges::equal(dense_set, std_set)); + }; + + auto random_insert = [&] { + expect_equivalence(); + size_t value = base::RandUint64() % kEnd; + auto p = dense_set.insert(value); + auto q = std_set.insert(value); + EXPECT_EQ(p.second, q.second); + EXPECT_EQ(p.first == dense_set.end(), q.first == std_set.end()); + EXPECT_TRUE(!p.second || p.first == dense_set.find(value)); + EXPECT_TRUE(!q.second || q.first == std_set.find(value)); + }; + + auto random_erase = [&] { + expect_equivalence(); + size_t value = base::RandUint64() % kEnd; + EXPECT_EQ(dense_set.erase(value), std_set.erase(value)); + }; + + auto random_erase_iterator = [&] { + expect_equivalence(); + size_t value = base::RandUint64() % kEnd; + auto it = dense_set.find(value); + auto jt = std_set.find(value); + EXPECT_EQ(it == dense_set.end(), jt == std_set.end()); + if (it == dense_set.end() || jt == std_set.end()) + return; + auto succ_it = dense_set.erase(it); + auto succ_jt = std_set.erase(jt); + EXPECT_EQ(succ_it == dense_set.end(), succ_jt == std_set.end()); + EXPECT_TRUE(succ_it == dense_set.upper_bound(value)); + EXPECT_TRUE(succ_jt == std_set.upper_bound(value)); + EXPECT_TRUE(succ_it == dense_set.end() || *succ_it == *succ_jt); + }; + + auto random_erase_range = [&] { + expect_equivalence(); + size_t min_value = base::RandUint64() % kEnd; + size_t max_value = base::RandUint64() % kEnd; + 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), + dense_set.upper_bound(max_value)); + std_set.erase(std_set.lower_bound(min_value), + std_set.upper_bound(max_value)); + }; + + for (size_t i = 0; i < kEnd; ++i) { + random_insert(); + } + + for (size_t i = 0; i < kEnd / 2; ++i) { + random_erase(); + } + + expect_equivalence(); + dense_set.clear(); + std_set.clear(); + expect_equivalence(); + + for (size_t i = 0; i < kEnd; ++i) { + random_insert(); + } + + for (size_t i = 0; i < kEnd; ++i) { + random_erase_iterator(); + } + + expect_equivalence(); + dense_set.clear(); + std_set.clear(); + expect_equivalence(); + + for (size_t i = 0; i < kEnd; ++i) { + random_insert(); + } + + for (size_t i = 0; i < kEnd; ++i) { + random_erase_range(); + } + + expect_equivalence(); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/common/logging/log_buffer.cc b/chromium/components/autofill/core/common/logging/log_buffer.cc index 0f9e6c349d5..961e66e457b 100644 --- a/chromium/components/autofill/core/common/logging/log_buffer.cc +++ b/chromium/components/autofill/core/common/logging/log_buffer.cc @@ -76,7 +76,7 @@ bool TryCoalesceString(std::vector<base::Value>* buffer, base::Value CreateEmptyFragment() { base::Value::DictStorage storage; - storage.try_emplace("type", std::make_unique<base::Value>("fragment")); + storage.try_emplace("type", "fragment"); return base::Value(storage); } @@ -115,9 +115,8 @@ LogBuffer& operator<<(LogBuffer& buf, Tag&& tag) { return buf; base::Value::DictStorage storage; - storage.try_emplace("type", std::make_unique<base::Value>("element")); - storage.try_emplace("value", - std::make_unique<base::Value>(std::move(tag.name))); + storage.try_emplace("type", "element"); + storage.try_emplace("value", std::move(tag.name)); buf.buffer_.emplace_back(std::move(storage)); return buf; } @@ -148,8 +147,7 @@ LogBuffer& operator<<(LogBuffer& buf, Attrib&& attrib) { base::Value(std::move(attrib.value))); } else { base::Value::DictStorage dict; - dict.try_emplace(std::move(attrib.name), - std::make_unique<base::Value>(std::move(attrib.value))); + dict.try_emplace(std::move(attrib.name), std::move(attrib.value)); node.SetKey("attributes", base::Value(std::move(dict))); } @@ -173,10 +171,10 @@ LogBuffer& operator<<(LogBuffer& buf, base::StringPiece text) { return buf; base::Value::DictStorage storage; - storage.try_emplace("type", std::make_unique<base::Value>("text")); + storage.try_emplace("type", "text"); // This text is not HTML escaped because the rest of the frame work takes care // of that and it must not be escaped twice. - storage.try_emplace("value", std::make_unique<base::Value>(text)); + storage.try_emplace("value", text); base::Value node_to_add(std::move(storage)); AppendChildToLastNode(&buf.buffer_, std::move(node_to_add)); return buf; diff --git a/chromium/components/autofill/core/common/mojom/BUILD.gn b/chromium/components/autofill/core/common/mojom/BUILD.gn index 4c3ab8abd86..12cc7be0213 100644 --- a/chromium/components/autofill/core/common/mojom/BUILD.gn +++ b/chromium/components/autofill/core/common/mojom/BUILD.gn @@ -72,10 +72,6 @@ mojom("mojo_types") { mojom = "autofill.mojom.PasswordGenerationUIData" cpp = "::autofill::password_generation::PasswordGenerationUIData" }, - { - mojom = "autofill.mojom.ValueElementPair" - cpp = "::autofill::ValueElementPair" - }, ] traits_headers = [ "autofill_types_mojom_traits.h" ] traits_sources = [ "autofill_types_mojom_traits.cc" ] diff --git a/chromium/components/autofill/core/common/mojom/autofill_types.mojom b/chromium/components/autofill/core/common/mojom/autofill_types.mojom index 6ad1a8fc233..4c35cd75787 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types.mojom +++ b/chromium/components/autofill/core/common/mojom/autofill_types.mojom @@ -27,19 +27,7 @@ enum SubmissionIndicatorEvent { // DEPRECATED_FILLED_FORM_ON_START_PROVISIONAL_LOAD, // DEPRECATED_FILLED_INPUT_ELEMENTS_ON_START_PROVISIONAL_LOAD, PROBABLE_FORM_SUBMISSION = 10, -}; - -// This enum lists form field types as understood by the password manager, -// essentially a digest of |autofill::ServerFieldType|. Note that we cannot -// simply reuse |autofill::ServerFieldType| as it is defined in the browser, -// while this enum will be used by both the browser and renderer. -// TODO(https://crbug.com/1067347): move this enum to browser code. It is not -// used in Mojo anymore. -enum PasswordFormFieldPredictionType { - kUsername, - kCurrentPassword, - kNewPassword, - kNotPassword, + CHANGE_PASSWORD_FORM_CLEARED = 11, }; enum SubmissionSource { 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 bc85d1db9f7..5b9b8780e67 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 @@ -23,8 +23,6 @@ namespace autofill { -using mojom::PasswordFormFieldPredictionType; - const std::vector<const char*> kOptions = {"Option1", "Option2", "Option3", "Option4"}; namespace { diff --git a/chromium/components/autofill/core/common/password_form.cc b/chromium/components/autofill/core/common/password_form.cc deleted file mode 100644 index 6585faa88e8..00000000000 --- a/chromium/components/autofill/core/common/password_form.cc +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/autofill/core/common/password_form.h" - -#include <algorithm> -#include <ostream> -#include <sstream> - -#include "base/json/json_writer.h" -#include "base/strings/string16.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" - -namespace autofill { - -namespace { - -std::string ToString(PasswordForm::Store in_store) { - switch (in_store) { - case PasswordForm::Store::kNotSet: - return "Not Set"; - case PasswordForm::Store::kProfileStore: - return "Profile Store"; - case PasswordForm::Store::kAccountStore: - return "Account Store"; - } -} - -std::string ToString(PasswordForm::Scheme scheme) { - switch (scheme) { - case PasswordForm::Scheme::kHtml: - return "HTML"; - case PasswordForm::Scheme::kBasic: - return "Basic"; - case PasswordForm::Scheme::kDigest: - return "Digest"; - case PasswordForm::Scheme::kOther: - return "Other"; - case PasswordForm::Scheme::kUsernameOnly: - return "UsernameOnly"; - } - - NOTREACHED(); - return std::string(); -} - -std::string ToString(PasswordForm::Type type) { - switch (type) { - case PasswordForm::Type::kManual: - return "Manual"; - case PasswordForm::Type::kGenerated: - return "Generated"; - case PasswordForm::Type::kApi: - return "API"; - } - - NOTREACHED(); - return std::string(); -} - -std::string ToString(PasswordForm::GenerationUploadStatus status) { - switch (status) { - case PasswordForm::GenerationUploadStatus::kNoSignalSent: - return "No Signal Sent"; - case PasswordForm::GenerationUploadStatus::kPositiveSignalSent: - return "Positive Signal Sent"; - case PasswordForm::GenerationUploadStatus::kNegativeSignalSent: - return "Negative Signal Sent"; - } - - NOTREACHED(); - return std::string(); -} - -// Utility function that creates a std::string from an object supporting the -// ostream operator<<. -template <typename T> -std::string ToString(const T& obj) { - std::ostringstream ostream; - ostream << obj; - return ostream.str(); -} - -base::string16 ValueElementVectorToString( - const ValueElementVector& value_element_pairs) { - std::vector<base::string16> pairs(value_element_pairs.size()); - std::transform(value_element_pairs.begin(), value_element_pairs.end(), - pairs.begin(), [](const ValueElementPair& p) { - return p.first + base::ASCIIToUTF16("+") + p.second; - }); - return base::JoinString(pairs, base::ASCIIToUTF16(", ")); -} - -// Serializes a PasswordForm to a JSON object. Used only for logging in tests. -void PasswordFormToJSON(const PasswordForm& form, - base::DictionaryValue* target) { - target->SetString("scheme", ToString(form.scheme)); - target->SetString("signon_realm", form.signon_realm); - target->SetBoolean("is_public_suffix_match", form.is_public_suffix_match); - target->SetBoolean("is_affiliation_based_match", - form.is_affiliation_based_match); - target->SetString("url", form.url.possibly_invalid_spec()); - target->SetString("action", form.action.possibly_invalid_spec()); - target->SetString("submit_element", form.submit_element); - target->SetString("username_element", form.username_element); - target->SetInteger("username_element_renderer_id", - form.username_element_renderer_id.value()); - target->SetString("username_value", form.username_value); - target->SetString("password_element", form.password_element); - target->SetString("password_value", form.password_value); - target->SetInteger("password_element_renderer_id", - form.password_element_renderer_id.value()); - target->SetString("new_password_element", form.new_password_element); - target->SetInteger("password_element_renderer_id", - form.password_element_renderer_id.value()); - target->SetString("new_password_value", form.new_password_value); - target->SetString("confirmation_password_element", - form.confirmation_password_element); - target->SetInteger("confirmation_password_element_renderer_id", - form.confirmation_password_element_renderer_id.value()); - target->SetString("all_possible_usernames", - ValueElementVectorToString(form.all_possible_usernames)); - target->SetString("all_possible_passwords", - ValueElementVectorToString(form.all_possible_passwords)); - target->SetBoolean("blocked_by_user", form.blocked_by_user); - target->SetDouble("date_last_used", form.date_last_used.ToDoubleT()); - target->SetDouble("date_created", form.date_created.ToDoubleT()); - target->SetDouble("date_synced", form.date_synced.ToDoubleT()); - target->SetString("type", ToString(form.type)); - target->SetInteger("times_used", form.times_used); - target->SetString("form_data", ToString(form.form_data)); - target->SetString("generation_upload_status", - ToString(form.generation_upload_status)); - target->SetString("display_name", form.display_name); - target->SetString("icon_url", form.icon_url.possibly_invalid_spec()); - target->SetString("federation_origin", form.federation_origin.Serialize()); - target->SetBoolean("skip_next_zero_click", form.skip_zero_click); - target->SetBoolean("was_parsed_using_autofill_predictions", - form.was_parsed_using_autofill_predictions); - target->SetString("affiliated_web_realm", form.affiliated_web_realm); - target->SetString("app_display_name", form.app_display_name); - target->SetString("app_icon_url", form.app_icon_url.possibly_invalid_spec()); - target->SetString("submission_event", ToString(form.submission_event)); - target->SetBoolean("only_for_fallback", form.only_for_fallback); - target->SetBoolean("is_gaia_with_skip_save_password_form", - form.form_data.is_gaia_with_skip_save_password_form); - target->SetBoolean("is_new_password_reliable", form.is_new_password_reliable); - target->SetString("in_store", ToString(form.in_store)); - - std::vector<std::string> hashes; - hashes.reserve(form.moving_blocked_for_list.size()); - for (const auto& gaia_id_hash : form.moving_blocked_for_list) { - hashes.push_back(gaia_id_hash.ToBase64()); - } - target->SetString("moving_blocked_for_list", base::JoinString(hashes, ", ")); -} - -} // namespace - -PasswordForm::PasswordForm() = default; - -PasswordForm::PasswordForm(const PasswordForm& other) = default; - -PasswordForm::PasswordForm(PasswordForm&& other) = default; - -PasswordForm::~PasswordForm() = default; - -PasswordForm& PasswordForm::operator=(const PasswordForm& form) = default; - -PasswordForm& PasswordForm::operator=(PasswordForm&& form) = default; - -bool PasswordForm::IsPossibleChangePasswordForm() const { - return !new_password_element.empty(); -} - -bool PasswordForm::IsPossibleChangePasswordFormWithoutUsername() const { - return IsPossibleChangePasswordForm() && username_element.empty(); -} - -bool PasswordForm::HasUsernameElement() const { - return !username_element_renderer_id.is_null(); -} - -bool PasswordForm::HasPasswordElement() const { - return !password_element_renderer_id.is_null(); -} - -bool PasswordForm::HasNewPasswordElement() const { - return !new_password_element_renderer_id.is_null(); -} - -bool PasswordForm::IsFederatedCredential() const { - return !federation_origin.opaque(); -} - -bool PasswordForm::IsSingleUsername() const { - return HasUsernameElement() && !HasPasswordElement() && - !HasNewPasswordElement(); -} - -bool PasswordForm::IsUsingAccountStore() const { - return in_store == Store::kAccountStore; -} - -bool PasswordForm::HasNonEmptyPasswordValue() const { - return !password_value.empty() || !new_password_value.empty(); -} - -bool PasswordForm::operator==(const PasswordForm& form) const { - return scheme == form.scheme && signon_realm == form.signon_realm && - url == form.url && action == form.action && - submit_element == form.submit_element && - username_element == form.username_element && - username_element_renderer_id == form.username_element_renderer_id && - username_value == form.username_value && - all_possible_usernames == form.all_possible_usernames && - all_possible_passwords == form.all_possible_passwords && - form_has_autofilled_value == form.form_has_autofilled_value && - password_element == form.password_element && - password_element_renderer_id == form.password_element_renderer_id && - password_value == form.password_value && - new_password_element == form.new_password_element && - confirmation_password_element_renderer_id == - form.confirmation_password_element_renderer_id && - confirmation_password_element == form.confirmation_password_element && - confirmation_password_element_renderer_id == - form.confirmation_password_element_renderer_id && - new_password_value == form.new_password_value && - date_created == form.date_created && date_synced == form.date_synced && - date_last_used == form.date_last_used && - blocked_by_user == form.blocked_by_user && type == form.type && - times_used == form.times_used && - form_data.SameFormAs(form.form_data) && - generation_upload_status == form.generation_upload_status && - display_name == form.display_name && icon_url == form.icon_url && - // We compare the serialization of the origins here, as we want unique - // origins to compare as '=='. - federation_origin.Serialize() == form.federation_origin.Serialize() && - skip_zero_click == form.skip_zero_click && - was_parsed_using_autofill_predictions == - form.was_parsed_using_autofill_predictions && - is_public_suffix_match == form.is_public_suffix_match && - is_affiliation_based_match == form.is_affiliation_based_match && - affiliated_web_realm == form.affiliated_web_realm && - app_display_name == form.app_display_name && - app_icon_url == form.app_icon_url && - submission_event == form.submission_event && - only_for_fallback == form.only_for_fallback && - is_new_password_reliable == form.is_new_password_reliable && - in_store == form.in_store && - moving_blocked_for_list == form.moving_blocked_for_list; -} - -bool PasswordForm::operator!=(const PasswordForm& form) const { - return !operator==(form); -} - -bool ArePasswordFormUniqueKeysEqual(const PasswordForm& left, - const PasswordForm& right) { - return (left.signon_realm == right.signon_realm && left.url == right.url && - left.username_element == right.username_element && - left.username_value == right.username_value && - left.password_element == right.password_element); -} - -std::ostream& operator<<(std::ostream& os, PasswordForm::Scheme scheme) { - return os << ToString(scheme); -} - -std::ostream& operator<<(std::ostream& os, const PasswordForm& form) { - base::DictionaryValue form_json; - PasswordFormToJSON(form, &form_json); - - // Serialize the default PasswordForm, and remove values from the result that - // are equal to this to make the results more concise. - base::DictionaryValue default_form_json; - PasswordFormToJSON(PasswordForm(), &default_form_json); - for (base::DictionaryValue::Iterator it_default_key_values(default_form_json); - !it_default_key_values.IsAtEnd(); it_default_key_values.Advance()) { - const base::Value* actual_value; - if (form_json.Get(it_default_key_values.key(), &actual_value) && - it_default_key_values.value().Equals(actual_value)) { - form_json.Remove(it_default_key_values.key(), nullptr); - } - } - - std::string form_as_string; - base::JSONWriter::WriteWithOptions( - form_json, base::JSONWriter::OPTIONS_PRETTY_PRINT, &form_as_string); - base::TrimWhitespaceASCII(form_as_string, base::TRIM_ALL, &form_as_string); - return os << "PasswordForm(" << form_as_string << ")"; -} - -std::ostream& operator<<(std::ostream& os, PasswordForm* form) { - return os << "&" << *form; -} - -} // namespace autofill diff --git a/chromium/components/autofill/core/common/password_form.h b/chromium/components/autofill/core/common/password_form.h deleted file mode 100644 index 3d3e786a73a..00000000000 --- a/chromium/components/autofill/core/common/password_form.h +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_H__ -#define COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_H__ - -#include <map> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include "base/time/time.h" -#include "components/autofill/core/common/form_data.h" -#include "components/autofill/core/common/gaia_id_hash.h" -#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" -#include "components/autofill/core/common/renderer_id.h" -#include "url/gurl.h" -#include "url/origin.h" - -namespace autofill { - -// Pair of a value and the name of the element that contained this value. -using ValueElementPair = std::pair<base::string16, base::string16>; - -// Vector of possible username values and corresponding field names. -using ValueElementVector = std::vector<ValueElementPair>; - -// The PasswordForm struct encapsulates information about a login form, -// which can be an HTML form or a dialog with username/password text fields. -// -// The Web Data database stores saved username/passwords and associated form -// metdata using a PasswordForm struct, typically one that was created from -// a parsed HTMLFormElement or LoginDialog, but the saved entries could have -// also been created by imported data from another browser. -// -// The PasswordManager implements a fuzzy-matching algorithm to compare saved -// PasswordForm entries against PasswordForms that were created from a parsed -// HTML or dialog form. As one might expect, the more data contained in one -// of the saved PasswordForms, the better the job the PasswordManager can do -// in matching it against the actual form it was saved on, and autofill -// accurately. But it is not always possible, especially when importing from -// other browsers with different data models, to copy over all the information -// about a particular "saved password entry" to our PasswordForm -// representation. -// -// The field descriptions in the struct specification below are intended to -// describe which fields are not strictly required when adding a saved password -// entry to the database and how they can affect the matching process. -// -// TODO(crbug.com/1067347): Move complete class to password_manager namespace. -struct PasswordForm { - // Enum to differentiate between HTML form based authentication, and dialogs - // using basic or digest schemes. Default is kHtml. Only PasswordForms of the - // same Scheme will be matched/autofilled against each other. - enum class Scheme { - kHtml, - kBasic, - kDigest, - kOther, - kUsernameOnly, - kMinValue = kHtml, - kMaxValue = kUsernameOnly, - }; - - // Enum to differentiate between manually filled forms, forms with auto- - // generated passwords, and forms generated from the DOM API. - // - // Always append new types at the end. This enum is converted to int and - // stored in password store backends, so it is important to keep each - // value assigned to the same integer. - enum class Type { - kManual, - kGenerated, - kApi, - kMinValue = kManual, - kMaxValue = kApi, - }; - - // Enum to keep track of what information has been sent to the server about - // this form regarding password generation. - enum class GenerationUploadStatus { - kNoSignalSent, - kPositiveSignalSent, - kNegativeSignalSent, - kMinValue = kNoSignalSent, - kMaxValue = kNegativeSignalSent, - }; - - Scheme scheme = Scheme::kHtml; - - // The "Realm" for the sign-on. This is scheme, host, port for SCHEME_HTML. - // Dialog based forms also contain the HTTP realm. Android based forms will - // contain a string of the form "android://<hash of cert>@<package name>" - // - // The signon_realm is effectively the primary key used for retrieving - // data from the database, so it must not be empty. - std::string signon_realm; - - // An URL consists of the scheme, host, port and path; the rest is stripped. - // This is the primary data used by the PasswordManager to decide (in longest - // matching prefix fashion) whether or not a given PasswordForm result from - // the database is a good fit for a particular form on a page. - // - // This should not be empty except for Android based credentials. - GURL url; - - // The action target of the form; like |origin| URL consists of the scheme, - // host, port and path; the rest is stripped. This is the primary data used by - // the PasswordManager for form autofill; that is, the action of the saved - // credentials must match the action of the form on the page to be autofilled. - // If this is empty / not available, it will result in a "restricted" IE-like - // autofill policy, where we wait for the user to type in their username - // before autofilling the password. In these cases, after successful login the - // action URL will automatically be assigned by the PasswordManager. - // - // When parsing an HTML form, this must always be set. - GURL action; - - // The web realm affiliated with the Android application, if the form is an - // Android credential. Otherwise, the string is empty. If there are several - // realms affiliated with the application, an arbitrary realm is chosen. The - // field is filled out when the PasswordStore injects affiliation and branding - // information, i.e. in InjectAffiliationAndBrandingInformation. If there was - // no prior call to this method, the string is empty. - std::string affiliated_web_realm; - - // The display name (e.g. Play Store name) of the Android application if the - // form is an Android credential. Otherwise, the string is empty. The field is - // filled out when the PasswordStore injects affiliation and branding - // information, i.e. in InjectAffiliationAndBrandingInformation. If there was - // no prior call to this method, the string is empty. - std::string app_display_name; - - // The icon URL (e.g. Play Store icon URL) of the Android application if the - // form is an Android credential. Otherwise, the URL is empty. The field is - // filled out when the PasswordStore injects affiliation and branding - // information, i.e. in InjectAffiliationAndBrandingInformation. If there was - // no prior call to this method, the URL is empty. - GURL app_icon_url; - - // The name of the submit button used. Optional; only used in scoring - // of PasswordForm results from the database to make matches as tight as - // possible. - base::string16 submit_element; - - // The name of the username input element. - base::string16 username_element; - - // The renderer id of the username input element. It is set during the new - // form parsing and not persisted. - FieldRendererId username_element_renderer_id; - - // True if the server-side classification was successful. - bool server_side_classification_successful = false; - - // True if the server-side classification believes that the field may be - // pre-filled with a placeholder in the value attribute. It is set during - // form parsing and not persisted. - bool username_may_use_prefilled_placeholder = false; - - // When parsing an HTML form, this is typically empty unless the site - // has implemented some form of autofill. - base::string16 username_value; - - // This member is populated in cases where we there are multiple input - // elements that could possibly be the username. Used when our heuristics for - // determining the username are incorrect. Optional. - ValueElementVector all_possible_usernames; - - // This member is populated in cases where we there are multiple possible - // password values. Used in pending password state, to populate a dropdown - // for possible passwords. Contains all possible passwords. Optional. - ValueElementVector all_possible_passwords; - - // True if |all_possible_passwords| have autofilled value or its part. - bool form_has_autofilled_value = false; - - // The name of the input element corresponding to the current password. - // Optional (improves scoring). - // - // When parsing an HTML form, this will always be set, unless it is a sign-up - // form or a change password form that does not ask for the current password. - // In these two cases the |new_password_element| will always be set. - base::string16 password_element; - - // The renderer id of the password input element. It is set during the new - // form parsing and not persisted. - FieldRendererId password_element_renderer_id; - - // The current password. Must be non-empty for PasswordForm instances that are - // meant to be persisted to the password store. - // - // When parsing an HTML form, this is typically empty. - base::string16 password_value; - - // The current encrypted password. Must be non-empty for PasswordForm - // instances retrieved from the password store or coming in a - // PasswordStoreChange that is not of type REMOVE. - std::string encrypted_password; - - // If the form was a sign-up or a change password form, the name of the input - // element corresponding to the new password. Optional, and not persisted. - base::string16 new_password_element; - - // The renderer id of the new password input element. It is set during the new - // form parsing and not persisted. - FieldRendererId new_password_element_renderer_id; - - // The confirmation password element. Optional, only set on form parsing, and - // not persisted. - base::string16 confirmation_password_element; - - // The renderer id of the confirmation password input element. It is set - // during the new form parsing and not persisted. - FieldRendererId confirmation_password_element_renderer_id; - - // The new password. Optional, and not persisted. - base::string16 new_password_value; - - // When the login was last used by the user to login to the site. Defaults to - // |date_created|, except for passwords that were migrated from the now - // deprecated |preferred| flag. Their default is set when migrating the login - // database to have the "date_last_used" column. - // - // When parsing an HTML form, this is not used. - base::Time date_last_used; - - // When the login was saved (by chrome). - // - // When parsing an HTML form, this is not used. - base::Time date_created; - - // When the login was downloaded from the sync server. For local passwords is - // not used. - // - // When parsing an HTML form, this is not used. - base::Time date_synced; - - // Tracks if the user opted to never remember passwords for this form. Default - // to false. - // - // When parsing an HTML form, this is not used. - bool blocked_by_user = false; - - // The form type. - Type type = Type::kManual; - - // The number of times that this username/password has been used to - // authenticate the user. - // - // When parsing an HTML form, this is not used. - int times_used = 0; - - // Autofill representation of this form. Used to communicate with the - // Autofill servers if necessary. Currently this is only used to help - // determine forms where we can trigger password generation. - // - // When parsing an HTML form, this is normally set. - FormData form_data; - - // What information has been sent to the Autofill server about this form. - GenerationUploadStatus generation_upload_status = - GenerationUploadStatus::kNoSignalSent; - - // These following fields are set by a website using the Credential Manager - // API. They will be empty and remain unused for sites which do not use that - // API. - // - // User friendly name to show in the UI. - base::string16 display_name; - - // The URL of this credential's icon, such as the user's avatar, to display - // in the UI. - GURL icon_url; - - // The origin of identity provider used for federated login. - url::Origin federation_origin; - - // If true, Chrome will not return this credential to a site in response to - // 'navigator.credentials.request()' without user interaction. - // Once user selects this credential the flag is reseted. - bool skip_zero_click = false; - - // If true, this form was parsed using Autofill predictions. - bool was_parsed_using_autofill_predictions = false; - - // If true, this match was found using public suffix matching. - bool is_public_suffix_match = false; - - // If true, this is a credential saved through an Android application, and - // found using affiliation-based match. - bool is_affiliation_based_match = false; - - // The type of the event that was taken as an indication that this form is - // being or has already been submitted. This field is not persisted and filled - // out only for submitted forms. - mojom::SubmissionIndicatorEvent submission_event = - mojom::SubmissionIndicatorEvent::NONE; - - // True iff heuristics declined this form for normal saving or filling (e.g. - // only credit card fields were found). But this form can be saved or filled - // only with the fallback. - bool only_for_fallback = false; - - // True iff the new password field was found with server hints or autocomplete - // attributes. Only set on form parsing for filling, and not persisted. Used - // as signal for password generation eligibility. - bool is_new_password_reliable = false; - - // Serialized to prefs, so don't change numeric values! - // These values are persisted to logs. Entries should not be renumbered and - // numeric values should never be reused. - enum class Store { - // Default value. - kNotSet = 0, - // Credential came from the profile (i.e. local) storage. - kProfileStore = 1, - // Credential came from the Gaia-account-scoped storage. - kAccountStore = 2, - kMaxValue = kAccountStore - }; - Store in_store = Store::kNotSet; - - // Vector of hashes of the gaia id for users who prefer not to move this - // password form to their account. This list is used to suppress the move - // prompt for those users. - std::vector<GaiaIdHash> moving_blocked_for_list; - - // Return true if we consider this form to be a change password form. - // We use only client heuristics, so it could include signup forms. - bool IsPossibleChangePasswordForm() const; - - // Return true if we consider this form to be a change password form - // without username field. We use only client heuristics, so it could - // include signup forms. - bool IsPossibleChangePasswordFormWithoutUsername() const; - - // Returns true if current password element is set. - bool HasUsernameElement() const; - - // Returns true if current password element is set. - bool HasPasswordElement() const; - - // Returns true if current password element is set. - bool HasNewPasswordElement() const; - - // True iff |federation_origin| isn't empty. - bool IsFederatedCredential() const; - - // True if username element is set and password and new password elements are - // not set. - bool IsSingleUsername() const; - - // Returns whether this form is stored in the account-scoped store, i.e. - // whether |in_store == Store::kAccountStore|. - bool IsUsingAccountStore() const; - - // Returns true when |password_value| or |new_password_value| are non-empty. - bool HasNonEmptyPasswordValue() const; - - // Equality operators for testing. - bool operator==(const PasswordForm& form) const; - bool operator!=(const PasswordForm& form) const; - - PasswordForm(); - PasswordForm(const PasswordForm& other); - PasswordForm(PasswordForm&& other); - ~PasswordForm(); - - PasswordForm& operator=(const PasswordForm& form); - PasswordForm& operator=(PasswordForm&& form); -}; - -// True if the unique keys for the forms are the same. The unique key is -// (origin, username_element, username_value, password_element, signon_realm). -bool ArePasswordFormUniqueKeysEqual(const PasswordForm& left, - const PasswordForm& right); - -// For testing. -std::ostream& operator<<(std::ostream& os, PasswordForm::Scheme scheme); -std::ostream& operator<<(std::ostream& os, const PasswordForm& form); -std::ostream& operator<<(std::ostream& os, PasswordForm* form); - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_H__ diff --git a/chromium/components/autofill/core/common/password_form_fill_data.h b/chromium/components/autofill/core/common/password_form_fill_data.h index 36f83ca6b03..93081879d5d 100644 --- a/chromium/components/autofill/core/common/password_form_fill_data.h +++ b/chromium/components/autofill/core/common/password_form_fill_data.h @@ -9,7 +9,6 @@ #include <vector> #include "components/autofill/core/common/form_data.h" -#include "components/autofill/core/common/password_form.h" #include "components/autofill/core/common/renderer_id.h" namespace autofill { diff --git a/chromium/components/autofill/core/common/password_form_generation_data.cc b/chromium/components/autofill/core/common/password_form_generation_data.cc deleted file mode 100644 index 04cccb19fbb..00000000000 --- a/chromium/components/autofill/core/common/password_form_generation_data.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 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/common/password_form_generation_data.h" - -#include <utility> - -namespace autofill { - -PasswordFormGenerationData::PasswordFormGenerationData() = default; - -PasswordFormGenerationData::PasswordFormGenerationData( - FieldRendererId new_password_renderer_id, - FieldRendererId confirmation_password_renderer_id) - : new_password_renderer_id(new_password_renderer_id), - confirmation_password_renderer_id(confirmation_password_renderer_id) {} - -#if defined(OS_IOS) -PasswordFormGenerationData::PasswordFormGenerationData( - FormRendererId form_renderer_id, - FieldRendererId new_password_renderer_id, - FieldRendererId confirmation_password_renderer_id) - : form_renderer_id(form_renderer_id), - new_password_renderer_id(new_password_renderer_id), - confirmation_password_renderer_id(confirmation_password_renderer_id) {} - -PasswordFormGenerationData::PasswordFormGenerationData( - const PasswordFormGenerationData&) = default; - -PasswordFormGenerationData& PasswordFormGenerationData::operator=( - const PasswordFormGenerationData&) = default; - -PasswordFormGenerationData::PasswordFormGenerationData( - PasswordFormGenerationData&&) = default; - -PasswordFormGenerationData& PasswordFormGenerationData::operator=( - PasswordFormGenerationData&&) = default; - -PasswordFormGenerationData::~PasswordFormGenerationData() = default; - -#endif - -} // namespace autofill diff --git a/chromium/components/autofill/core/common/password_form_generation_data.h b/chromium/components/autofill/core/common/password_form_generation_data.h index 2618629d47a..7c22909ac82 100644 --- a/chromium/components/autofill/core/common/password_form_generation_data.h +++ b/chromium/components/autofill/core/common/password_form_generation_data.h @@ -5,32 +5,15 @@ #ifndef COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_GENERATION_DATA_H_ #define COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_GENERATION_DATA_H_ -#include <stdint.h> - -#include "base/optional.h" #include "build/build_config.h" #include "components/autofill/core/common/renderer_id.h" -#include "url/gurl.h" namespace autofill { // Structure used for sending information from browser to renderer about on // which fields password should be generated. struct PasswordFormGenerationData { - PasswordFormGenerationData(); - PasswordFormGenerationData(FieldRendererId new_password_renderer_id, - FieldRendererId confirmation_password_renderer_id); #if defined(OS_IOS) - PasswordFormGenerationData(FormRendererId form_renderer_id, - FieldRendererId new_password_renderer_id, - FieldRendererId confirmation_password_renderer_id); - - PasswordFormGenerationData(const PasswordFormGenerationData&); - PasswordFormGenerationData& operator=(const PasswordFormGenerationData&); - PasswordFormGenerationData(PasswordFormGenerationData&&); - PasswordFormGenerationData& operator=(PasswordFormGenerationData&&); - ~PasswordFormGenerationData(); - FormRendererId form_renderer_id; #endif FieldRendererId new_password_renderer_id; diff --git a/chromium/components/autofill/core/common/password_generation_util.h b/chromium/components/autofill/core/common/password_generation_util.h index 43115bfaae3..abcee73daae 100644 --- a/chromium/components/autofill/core/common/password_generation_util.h +++ b/chromium/components/autofill/core/common/password_generation_util.h @@ -5,7 +5,9 @@ #ifndef COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_GENERATION_UTIL_H_ #define COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_GENERATION_UTIL_H_ -#include "components/autofill/core/common/password_form.h" +#include "base/i18n/rtl.h" +#include "base/strings/string16.h" +#include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/renderer_id.h" #include "ui/gfx/geometry/rect_f.h" diff --git a/chromium/components/autofill/core/common/save_password_progress_logger.cc b/chromium/components/autofill/core/common/save_password_progress_logger.cc index cc87fcd332e..4890a9be31a 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger.cc +++ b/chromium/components/autofill/core/common/save_password_progress_logger.cc @@ -353,8 +353,8 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "Generation disabled: no sync"; case STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE: return "Generation: automatic generation is available"; - case STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP: - return "Show generation popup triggered manually"; + case STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP: + return "Show generation popup triggered"; case STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED: return "Generated password accepted"; case STRING_SUCCESSFUL_SUBMISSION_INDICATOR_EVENT: diff --git a/chromium/components/autofill/core/common/save_password_progress_logger.h b/chromium/components/autofill/core/common/save_password_progress_logger.h index 2c23c2e54f4..aea961ea912 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger.h +++ b/chromium/components/autofill/core/common/save_password_progress_logger.h @@ -120,7 +120,7 @@ class SavePasswordProgressLogger { STRING_GENERATION_DISABLED_SAVING_DISABLED, STRING_GENERATION_DISABLED_NO_SYNC, STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE, - STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP, + STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP, STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED, STRING_SUCCESSFUL_SUBMISSION_INDICATOR_EVENT, STRING_MAIN_FRAME_ORIGIN, diff --git a/chromium/components/autofill/core/common/save_password_progress_logger_unittest.cc b/chromium/components/autofill/core/common/save_password_progress_logger_unittest.cc index bce8f456d66..550f2e9479a 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger_unittest.cc +++ b/chromium/components/autofill/core/common/save_password_progress_logger_unittest.cc @@ -11,7 +11,6 @@ #include "base/bind.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/common/password_form.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" diff --git a/chromium/components/autofill/ios/browser/autofill_agent.mm b/chromium/components/autofill/ios/browser/autofill_agent.mm index 1fcafb96c9f..c33daac5902 100644 --- a/chromium/components/autofill/ios/browser/autofill_agent.mm +++ b/chromium/components/autofill/ios/browser/autofill_agent.mm @@ -15,6 +15,7 @@ #include "base/mac/foundation_util.h" #include "base/memory/weak_ptr.h" #include "base/metrics/field_trial.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/sys_string_conversions.h" @@ -107,7 +108,8 @@ void GetFormField(autofill::FormFieldData* field, void UpdateFieldManagerWithFillingResults( scoped_refptr<FieldDataManager> fieldDataManager, - NSString* jsonString) { + NSString* jsonString, + size_t numFieldsInFormData) { std::map<uint32_t, base::string16> fillingResults; if (autofill::ExtractFillingResults(jsonString, &fillingResults)) { for (auto& fillData : fillingResults) { @@ -116,6 +118,8 @@ void UpdateFieldManagerWithFillingResults( kAutofilledOnUserTrigger); } } + // TODO(crbug/1131038): Remove once the experiment is over. + UMA_HISTOGRAM_BOOLEAN("Autofill.FormFillSuccessIOS", !fillingResults.empty()); } void UpdateFieldManagerForClearedIDs( @@ -759,9 +763,9 @@ autofillManagerFromWebState:(web::WebState*)webState }; // The document has now been fully loaded. Scan for forms to be extracted. size_t min_required_fields = - MIN(autofill::MinRequiredFieldsForUpload(), - MIN(autofill::MinRequiredFieldsForHeuristics(), - autofill::MinRequiredFieldsForQuery())); + MIN(autofill::kMinRequiredFieldsForUpload, + MIN(autofill::kMinRequiredFieldsForHeuristics, + autofill::kMinRequiredFieldsForQuery)); [self fetchFormsFiltered:NO withName:base::string16() minimumRequiredFieldsCount:min_required_fields @@ -944,6 +948,7 @@ 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 @@ -953,7 +958,8 @@ autofillManagerFromWebState:(web::WebState*)webState if (!strongSelf) return; UpdateFieldManagerWithFillingResults( - strongSelf->_fieldDataManager, jsonString); + strongSelf->_fieldDataManager, jsonString, + numFieldsInFormData); // 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_driver_ios.mm b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm index 9948c219d46..f72958bede1 100644 --- a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm +++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm @@ -175,7 +175,7 @@ net::IsolationInfo AutofillDriverIOS::IsolationInfo() { return net::IsolationInfo(); return net::IsolationInfo::Create( - net::IsolationInfo::RedirectMode::kUpdateNothing, + net::IsolationInfo::RequestType::kOther, url::Origin::Create(main_web_frame->GetSecurityOrigin()), url::Origin::Create(web_frame->GetSecurityOrigin()), net::SiteForCookies()); |