diff options
Diffstat (limited to 'chromium/components/autofill')
338 files changed, 16931 insertions, 12653 deletions
diff --git a/chromium/components/autofill/DEPS b/chromium/components/autofill/DEPS index 51fd81751ef..a89377bb4b1 100644 --- a/chromium/components/autofill/DEPS +++ b/chromium/components/autofill/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/autofill_assistant/core/public/autofill_assistant_intent.h", "+components/autofill/android/jni_headers", "+components/prefs", "+components/strings/grit/components_strings.h", diff --git a/chromium/components/autofill/PRESUBMIT.py b/chromium/components/autofill/PRESUBMIT.py index 68d395017f8..f98de555916 100644 --- a/chromium/components/autofill/PRESUBMIT.py +++ b/chromium/components/autofill/PRESUBMIT.py @@ -92,6 +92,7 @@ def _CheckWebViewExposedExperiments(input_api, output_api): _PRODUCTION_SUPPORT_FILE = ('android_webview/java/src/org/chromium/' + 'android_webview/common/ProductionSupportedFlagList.java') + _GENERATE_FLAG_LABELS_PY = 'android_webview/tools/generate_flag_labels.py' def is_autofill_features_file(f): return (f.LocalPath().startswith('components/autofill/') and @@ -106,9 +107,12 @@ def _CheckWebViewExposedExperiments(input_api, output_api): warnings = [] if (any_file_matches(is_autofill_features_file) and not any_file_matches(is_webview_features_file)): - warnings += [ output_api.PresubmitPromptWarning( - 'You may need to modify {} if your feature affects WebView.' - .format(_PRODUCTION_SUPPORT_FILE)) ] + warnings += [ + output_api.PresubmitPromptWarning(( + 'You may need to modify {} and run {} and follow its '+ + 'instructions if your feature affects WebView.' + ).format(_PRODUCTION_SUPPORT_FILE, _GENERATE_FLAG_LABELS_PY)) + ] return warnings diff --git a/chromium/components/autofill/content/browser/BUILD.gn b/chromium/components/autofill/content/browser/BUILD.gn index f4478af4054..4e660b6503e 100644 --- a/chromium/components/autofill/content/browser/BUILD.gn +++ b/chromium/components/autofill/content/browser/BUILD.gn @@ -37,6 +37,7 @@ static_library("browser") { deps = [ "//base", "//base:i18n", + "//components/autofill_assistant/core/public:public", "//components/os_crypt", "//components/prefs", "//components/profile_metrics", @@ -80,9 +81,13 @@ source_set("test_support") { "content_autofill_router_test_api.h", "form_forest_test_api.cc", "form_forest_test_api.h", + "test_autofill_manager_injector.h", ] - public_deps = [ ":browser" ] + public_deps = [ + ":browser", + "//content/public/browser", + ] deps = [ "//base" ] } diff --git a/chromium/components/autofill/content/browser/autofill_internals_log_router_unittest.cc b/chromium/components/autofill/content/browser/autofill_internals_log_router_unittest.cc index 186c2fdb381..65bdc6a597d 100644 --- a/chromium/components/autofill/content/browser/autofill_internals_log_router_unittest.cc +++ b/chromium/components/autofill/content/browser/autofill_internals_log_router_unittest.cc @@ -23,7 +23,7 @@ class MockLogReceiver : public autofill::LogReceiver { public: MockLogReceiver() {} - MOCK_METHOD(void, LogEntry, (const base::Value&), (override)); + MOCK_METHOD(void, LogEntry, (const base::Value::Dict&), (override)); }; } // namespace @@ -53,10 +53,10 @@ TEST_F(AutofillLogRouterFactoryTest, ServiceActiveNonIncognito) { testing::StrictMock<MockLogReceiver> receiver; ASSERT_TRUE(log_router); - EXPECT_EQ(std::vector<base::Value>(), - log_router->RegisterReceiver(&receiver)); + log_router->RegisterReceiver(&receiver); - base::Value log_entry = autofill::LogRouter::CreateEntryForText(kTestText); + base::Value::Dict log_entry = + autofill::LogRouter::CreateEntryForText(kTestText); EXPECT_CALL(receiver, LogEntry(testing::Eq(testing::ByRef(log_entry)))) .Times(1); log_router->ProcessLog(kTestText); diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc index 3e47876252d..8c12f8b8339 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc @@ -79,6 +79,10 @@ bool ContentAutofillDriver::IsIncognito() const { ->IsOffTheRecord(); } +bool ContentAutofillDriver::IsInActiveFrame() const { + return render_frame_host_->IsActive(); +} + bool ContentAutofillDriver::IsInAnyMainFrame() const { return render_frame_host_->GetMainFrame() == render_frame_host_; } @@ -111,17 +115,6 @@ bool ContentAutofillDriver::RendererIsAvailable() { return render_frame_host_->GetRenderViewHost() != nullptr; } -webauthn::InternalAuthenticator* -ContentAutofillDriver::GetOrCreateCreditCardInternalAuthenticator() { - if (!authenticator_impl_ && autofill_manager_ && - autofill_manager_->client()) { - authenticator_impl_ = - autofill_manager_->client()->CreateCreditCardInternalAuthenticator( - render_frame_host_); - } - return authenticator_impl_.get(); -} - void ContentAutofillDriver::PopupHidden() { // If the unmask prompt is shown, keep showing the preview. The preview // will be cleared when the prompt closes. @@ -167,11 +160,6 @@ void ContentAutofillDriver::FillOrPreviewFormImpl( GetAutofillAgent()->FillOrPreviewForm(query_id, data, action); } -void ContentAutofillDriver::HandleParsedForms( - const std::vector<const FormData*>& forms) { - // No op. -} - void ContentAutofillDriver::SendAutofillTypePredictionsToRendererImpl( const std::vector<FormDataPredictions>& type_predictions) { if (!RendererIsAvailable()) @@ -331,13 +319,13 @@ void ContentAutofillDriver::SelectControlDidChangeImpl( } void ContentAutofillDriver::AskForValuesToFillImpl( - int32_t query_id, const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, + int32_t query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible) { - autofill_manager_->OnAskForValuesToFill(query_id, form, field, bounding_box, + autofill_manager_->OnAskForValuesToFill(form, field, bounding_box, query_id, autoselect_first_suggestion, touch_to_fill_eligible); } @@ -374,24 +362,27 @@ void ContentAutofillDriver::DidEndTextFieldEditingImpl() { void ContentAutofillDriver::SelectFieldOptionsDidChangeImpl( const FormData& form) { - autofill_manager_->SelectFieldOptionsDidChange(form); + autofill_manager_->OnSelectFieldOptionsDidChange(form); } void ContentAutofillDriver::JavaScriptChangedAutofilledValueImpl( const FormData& form, const FormFieldData& field, const std::u16string& old_value) { - autofill_manager_->JavaScriptChangedAutofilledValue(form, field, old_value); + autofill_manager_->OnJavaScriptChangedAutofilledValue(form, field, old_value); } void ContentAutofillDriver::FillFormForAssistantImpl( const AutofillableData& fill_data, const FormData& form, - const FormFieldData& field) { + const FormFieldData& field, + const autofill_assistant::AutofillAssistantIntent intent) { DCHECK(autofill_manager_); if (fill_data.is_profile()) { + autofill_manager_->SetProfileFillViaAutofillAssistantIntent(intent); autofill_manager_->FillProfileForm(fill_data.profile(), form, field); } else if (fill_data.is_credit_card()) { + autofill_manager_->SetCreditCardFillViaAutofillAssistantIntent(intent); autofill_manager_->FillCreditCardForm( /*query_id=*/kNoQueryId, form, field, fill_data.credit_card(), fill_data.cvc()); @@ -487,10 +478,10 @@ void ContentAutofillDriver::SelectControlDidChange( } void ContentAutofillDriver::AskForValuesToFill( - int32_t query_id, const FormData& raw_form, const FormFieldData& raw_field, const gfx::RectF& bounding_box, + int32_t query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible) { if (!bad_message::CheckFrameNotPrerendering(render_frame_host_)) @@ -499,8 +490,8 @@ void ContentAutofillDriver::AskForValuesToFill( FormFieldData field = raw_field; SetFrameAndFormMetaData(form, &field); GetAutofillRouter().AskForValuesToFill( - this, query_id, form, field, - TransformBoundingBoxToViewportCoordinates(bounding_box), + this, form, field, + TransformBoundingBoxToViewportCoordinates(bounding_box), query_id, autoselect_first_suggestion, touch_to_fill_eligible); } @@ -573,11 +564,13 @@ void ContentAutofillDriver::JavaScriptChangedAutofilledValue( void ContentAutofillDriver::FillFormForAssistant( const AutofillableData& fill_data, const FormData& raw_form, - const FormFieldData& raw_field) { + const FormFieldData& raw_field, + const autofill_assistant::AutofillAssistantIntent intent) { FormData form = raw_form; FormFieldData field = raw_field; SetFrameAndFormMetaData(form, &field); - GetAutofillRouter().FillFormForAssistant(this, fill_data, form, field); + GetAutofillRouter().FillFormForAssistant(this, fill_data, form, field, + intent); } void ContentAutofillDriver::DidNavigateFrame( diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h index ab8995b1cee..fc6caef891f 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver.h @@ -17,7 +17,7 @@ #include "components/autofill/core/browser/autofill_driver.h" #include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/common/form_data_predictions.h" -#include "components/webauthn/core/browser/internal_authenticator.h" +#include "components/autofill_assistant/core/public/autofill_assistant_intent.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_widget_host.h" #include "mojo/public/cpp/bindings/associated_receiver.h" @@ -133,15 +133,14 @@ class ContentAutofillDriver : public AutofillDriver, // AutofillDriver: bool IsIncognito() const override; + bool IsInActiveFrame() const override; bool IsInAnyMainFrame() const override; bool IsPrerendering() const override; bool CanShowAutofillUi() const override; ui::AXTreeID GetAxTreeId() const override; scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override; bool RendererIsAvailable() override; - webauthn::InternalAuthenticator* GetOrCreateCreditCardInternalAuthenticator() - override; - void HandleParsedForms(const std::vector<const FormData*>& forms) override; + void HandleParsedForms(const std::vector<FormData>& forms) override {} void PopupHidden() override; net::IsolationInfo IsolationInfo() override; @@ -221,10 +220,10 @@ class ContentAutofillDriver : public AutofillDriver, void SelectControlDidChange(const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box) override; - void AskForValuesToFill(int32_t query_id, - const FormData& form, + void AskForValuesToFill(const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, + int32_t query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible) override; void HidePopup() override; @@ -260,10 +259,10 @@ class ContentAutofillDriver : public AutofillDriver, void SelectControlDidChangeImpl(const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box); - void AskForValuesToFillImpl(int32_t query_id, - const FormData& form, + void AskForValuesToFillImpl(const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, + int32_t query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible); void HidePopupImpl(); @@ -288,14 +287,16 @@ class ContentAutofillDriver : public AutofillDriver, // FillFormForAssistant() is located in ContentAutofillDriver so that // |raw_form| and |raw_field| get their meta data set analogous to // AskForValuesToFill(). - // TODO(crbug/1224094): Migrate Autofill Assistant to the standard Autofill - // flow. - void FillFormForAssistant(const AutofillableData& fill_data, - const FormData& raw_form, - const FormFieldData& raw_field); - void FillFormForAssistantImpl(const AutofillableData& fill_data, - const FormData& form, - const FormFieldData& field); + void FillFormForAssistant( + const AutofillableData& fill_data, + const FormData& raw_form, + const FormFieldData& raw_field, + const autofill_assistant::AutofillAssistantIntent intent); + void FillFormForAssistantImpl( + const AutofillableData& fill_data, + const FormData& form, + const FormFieldData& fiel, + const autofill_assistant::AutofillAssistantIntent intent); // Transform bounding box coordinates to real viewport coordinates. In the // case of a page spanning multiple renderer processes, subframe renderers @@ -384,9 +385,6 @@ class ContentAutofillDriver : public AutofillDriver, // code. std::unique_ptr<AutofillManager> autofill_manager_ = nullptr; - // Pointer to an implementation of InternalAuthenticator. - std::unique_ptr<webauthn::InternalAuthenticator> authenticator_impl_; - content::RenderWidgetHost::KeyPressEventCallback key_press_handler_; mojo::AssociatedReceiver<mojom::AutofillDriver> receiver_{this}; diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc index 7992fb1794d..85785d46cd5 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc @@ -160,8 +160,8 @@ void ContentAutofillDriverFactory::RenderFrameDeleted( ContentAutofillDriver* driver = it->second.get(); DCHECK(driver); - if (render_frame_host->GetLifecycleState() != - content::RenderFrameHost::LifecycleState::kPrerendering && + if (!render_frame_host->IsInLifecycleState( + content::RenderFrameHost::LifecycleState::kPrerendering) && driver->autofill_manager()) { driver->autofill_manager()->ReportAutofillWebOTPMetrics( render_frame_host->DocumentUsedWebOTP()); @@ -174,8 +174,8 @@ void ContentAutofillDriverFactory::RenderFrameDeleted( // and therefore won't close the popup. bool is_iframe = !driver->IsInAnyMainFrame(); if (is_iframe && router_.last_queried_source() == driver) { - DCHECK_NE(content::RenderFrameHost::LifecycleState::kPrerendering, - render_frame_host->GetLifecycleState()); + DCHECK(!render_frame_host->IsInLifecycleState( + content::RenderFrameHost::LifecycleState::kPrerendering)); router_.HidePopup(driver); } @@ -208,8 +208,11 @@ void ContentAutofillDriverFactory::DidFinishNavigation( navigation_handle->HasSubframeNavigationEntryCommitted())) { if (auto* driver = DriverForFrame(navigation_handle->GetRenderFrameHost())) { - if (!navigation_handle->IsInPrerenderedMainFrame()) + if (!navigation_handle->IsInPrerenderedMainFrame()) { client_->HideAutofillPopup(PopupHidingReason::kNavigation); + if (client_->IsTouchToFillCreditCardSupported()) + client_->HideTouchToFillCreditCard(); + } driver->DidNavigateFrame(navigation_handle); } } @@ -217,29 +220,10 @@ void ContentAutofillDriverFactory::DidFinishNavigation( void ContentAutofillDriverFactory::OnVisibilityChanged( content::Visibility visibility) { - if (visibility == content::Visibility::HIDDEN) + if (visibility == content::Visibility::HIDDEN) { client_->HideAutofillPopup(PopupHidingReason::kTabGone); -} - -void ContentAutofillDriverFactory::ReadyToCommitNavigation( - content::NavigationHandle* navigation_handle) { - content::RenderFrameHost* render_frame_host = - navigation_handle->GetRenderFrameHost(); - content::GlobalRenderFrameHostId render_frame_host_id( - render_frame_host->GetProcess()->GetID(), - render_frame_host->GetRoutingID()); - // No need to report the metrics here if navigating to a different - // RenderFrameHost. It will be reported in |RenderFrameDeleted|. - // TODO(crbug.com/936696): Remove this logic when RenderDocument is enabled - // everywhere. - if (render_frame_host_id != - navigation_handle->GetPreviousRenderFrameHostId()) { - return; - } - // Do not report metrics if prerendering. - if (render_frame_host->GetLifecycleState() == - content::RenderFrameHost::LifecycleState::kPrerendering) { - return; + if (client_->IsTouchToFillCreditCardSupported()) + client_->HideTouchToFillCreditCard(); } } diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h index 33aa10fa79b..fc52dfc2923 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h @@ -78,8 +78,6 @@ class ContentAutofillDriverFactory : public content::WebContentsObserver, void DidFinishNavigation( content::NavigationHandle* navigation_handle) override; void OnVisibilityChanged(content::Visibility visibility) override; - void ReadyToCommitNavigation( - content::NavigationHandle* navigation_handle) override; AutofillClient* client() { return client_; } diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc b/chromium/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc index cae01dbd367..b17c0448468 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc @@ -16,6 +16,7 @@ #include "components/autofill/core/browser/test_autofill_driver.h" #include "components/autofill/core/common/autofill_features.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/web_contents.h" #include "content/public/common/content_features.h" #include "content/public/test/navigation_simulator.h" #include "content/public/test/test_renderer_host.h" diff --git a/chromium/components/autofill/content/browser/content_autofill_router.cc b/chromium/components/autofill/content/browser/content_autofill_router.cc index 3b8038f0caa..a507bb05f5e 100644 --- a/chromium/components/autofill/content/browser/content_autofill_router.cc +++ b/chromium/components/autofill/content/browser/content_autofill_router.cc @@ -349,14 +349,14 @@ void ContentAutofillRouter::SelectControlDidChange( void ContentAutofillRouter::AskForValuesToFill( ContentAutofillDriver* source, - int32_t query_id, const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, + int32_t query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible) { if (!base::FeatureList::IsEnabled(features::kAutofillAcrossIframes)) { - source->AskForValuesToFillImpl(query_id, form, field, bounding_box, + source->AskForValuesToFillImpl(form, field, bounding_box, query_id, autoselect_first_suggestion, touch_to_fill_eligible); return; @@ -374,7 +374,7 @@ void ContentAutofillRouter::AskForValuesToFill( AFCHECK(target, return ); SetLastQueriedSource(source); SetLastQueriedTarget(target); - target->AskForValuesToFillImpl(query_id, browser_form, field, bounding_box, + target->AskForValuesToFillImpl(browser_form, field, bounding_box, query_id, autoselect_first_suggestion, touch_to_fill_eligible); } @@ -559,9 +559,10 @@ void ContentAutofillRouter::FillFormForAssistant( ContentAutofillDriver* source, const AutofillableData& fill_data, const FormData& form, - const FormFieldData& field) { + const FormFieldData& field, + const autofill_assistant::AutofillAssistantIntent intent) { if (!base::FeatureList::IsEnabled(features::kAutofillAcrossIframes)) { - source->FillFormForAssistantImpl(fill_data, form, field); + source->FillFormForAssistantImpl(fill_data, form, field, intent); return; } @@ -577,7 +578,7 @@ void ContentAutofillRouter::FillFormForAssistant( AFCHECK(target, return ); SetLastQueriedSource(source); SetLastQueriedTarget(target); - target->FillFormForAssistantImpl(fill_data, form, field); + target->FillFormForAssistantImpl(fill_data, form, field, intent); } // Routing of events triggered by the browser. diff --git a/chromium/components/autofill/content/browser/content_autofill_router.h b/chromium/components/autofill/content/browser/content_autofill_router.h index 61d97cdc6ea..72ec08667f1 100644 --- a/chromium/components/autofill/content/browser/content_autofill_router.h +++ b/chromium/components/autofill/content/browser/content_autofill_router.h @@ -16,6 +16,7 @@ #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_assistant/core/public/autofill_assistant_intent.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_widget_host.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -189,10 +190,10 @@ class ContentAutofillRouter { const FormFieldData& field, const gfx::RectF& bounding_box); void AskForValuesToFill(ContentAutofillDriver* source_driver, - int32_t query_id, const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, + int32_t query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible); void HidePopup(ContentAutofillDriver* source_driver); @@ -215,10 +216,12 @@ class ContentAutofillRouter { const std::u16string& old_value); // Event called by Autofill Assistant as if it was called by the renderer. - void FillFormForAssistant(ContentAutofillDriver* source_driver, - const AutofillableData& fill_data, - const FormData& form, - const FormFieldData& field); + void FillFormForAssistant( + ContentAutofillDriver* source_driver, + const AutofillableData& fill_data, + const FormData& form, + const FormFieldData& field, + const autofill_assistant::AutofillAssistantIntent intent); // Routing of events called by the browser: std::vector<FieldGlobalId> FillOrPreviewForm( diff --git a/chromium/components/autofill/content/browser/form_forest_unittest.cc b/chromium/components/autofill/content/browser/form_forest_unittest.cc index 0e20d765940..5f8d31fea4a 100644 --- a/chromium/components/autofill/content/browser/form_forest_unittest.cc +++ b/chromium/components/autofill/content/browser/form_forest_unittest.cc @@ -10,8 +10,10 @@ #include <utility> #include <vector> +#include "base/containers/contains.h" #include "base/strings/strcat.h" #include "base/strings/string_piece.h" +#include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "components/autofill/content/browser/form_forest.h" #include "components/autofill/content/browser/form_forest_test_api.h" @@ -23,6 +25,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/permissions_policy/permissions_policy.h" +#include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom-shared.h" using FrameData = autofill::internal::FormForest::FrameData; using FrameDataSet = @@ -147,6 +150,7 @@ auto CreateFieldTypeMap(const FormData& form) { // A profile is a 6-bit integer, whose bits indicate different values of first // and last name, credit card number, expiration month, expiration year, CVC. using Profile = base::StrongAlias<struct ProfileTag, size_t>; +using ::autofill::test::WithoutValues; // Fills the fields 0..5 of |form| with data according to |profile|, the // fields 6..11 with |profile|+1, etc. @@ -166,13 +170,6 @@ FormData WithValues(FormData& form, Profile profile = Profile(0)) { return form; } -// Clears the values of all fields in |form|. -FormData WithoutValues(FormData form) { - for (FormFieldData& field : form.fields) - field.value.clear(); - return form; -} - // Utility functions and constants. // Use strings for non-opaque origins and URLs because constructors must not be diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.cc b/chromium/components/autofill/content/browser/risk/fingerprint.cc index 9b474a64008..b20c65da8fa 100644 --- a/chromium/components/autofill/content/browser/risk/fingerprint.cc +++ b/chromium/components/autofill/content/browser/risk/fingerprint.cc @@ -88,14 +88,14 @@ std::string GetOperatingSystemVersion() { } // Adds the list of |fonts| to the |machine|. -void AddFontsToFingerprint(const base::ListValue& fonts, +void AddFontsToFingerprint(const base::Value::List& fonts, Fingerprint::MachineCharacteristics* machine) { - for (const auto& it : fonts.GetListDeprecated()) { + for (const auto& it : fonts) { // Each item in the list is a two-element list such that the first element // is the font family and the second is the font name. DCHECK(it.is_list()); - std::string font_name = it.GetListDeprecated()[1].GetString(); + std::string font_name = it.GetList()[1].GetString(); machine->add_font(font_name); } @@ -203,7 +203,7 @@ class FingerprintDataLoader : public content::GpuDataManagerObserver { void OnGpuInfoUpdate() override; // Callbacks for asynchronously loaded data. - void OnGotFonts(std::unique_ptr<base::ListValue> fonts); + void OnGotFonts(base::Value::List fonts); void OnGotPlugins(const std::vector<content::WebPluginInfo>& plugins); void OnGotGeoposition(device::mojom::GeopositionPtr geoposition); @@ -238,7 +238,7 @@ class FingerprintDataLoader : public content::GpuDataManagerObserver { const base::Time install_time_; // Data that will be loaded asynchronously. - std::unique_ptr<base::ListValue> fonts_; + std::unique_ptr<base::Value::List> fonts_; std::vector<content::WebPluginInfo> plugins_; bool waiting_on_plugins_; device::mojom::Geoposition geoposition_; @@ -329,9 +329,9 @@ void FingerprintDataLoader::OnGpuInfoUpdate() { MaybeFillFingerprint(); } -void FingerprintDataLoader::OnGotFonts(std::unique_ptr<base::ListValue> fonts) { +void FingerprintDataLoader::OnGotFonts(base::Value::List fonts) { DCHECK(!fonts_); - fonts_ = std::move(fonts); + fonts_ = std::make_unique<base::Value::List>(std::move(fonts)); MaybeFillFingerprint(); } diff --git a/chromium/components/autofill/content/browser/test_autofill_manager_injector.h b/chromium/components/autofill/content/browser/test_autofill_manager_injector.h new file mode 100644 index 00000000000..733bae614bf --- /dev/null +++ b/chromium/components/autofill/content/browser/test_autofill_manager_injector.h @@ -0,0 +1,82 @@ +// Copyright 2022 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_CONTENT_BROWSER_TEST_AUTOFILL_MANAGER_INJECTOR_H_ +#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_TEST_AUTOFILL_MANAGER_INJECTOR_H_ + +#include "components/autofill/content/browser/content_autofill_driver.h" +#include "components/autofill/content/browser/content_autofill_driver_factory.h" +#include "components/autofill/core/browser/autofill_client.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" + +namespace autofill { + +// Upon construction, and in response to ReadyToCommitNavigation, installs an +// AutofillManager of type `T`. +// +// Typical usage as a RAII type: +// +// class MockAutofillManager : BrowserAutofillManager { +// public: +// MockAutofillManager(ContentAutofillDriver* driver, +// AutofillClient* client) +// : BrowserAutofillManager(driver, client, "en-US", +// EnableDownloadManager(true)) {} +// MOCK_METHOD(...); +// ... +// }; +// +// TestAutofillManagerInjector<MockAutofillManager> injector(web_contents()); +// NavigateToURL(...); +template <typename T> +class TestAutofillManagerInjector : public content::WebContentsObserver { + public: + // Builds the managers using `T(ContentAutofillDriver*, AutofillClient*)`. + explicit TestAutofillManagerInjector(content::WebContents* web_contents) + : WebContentsObserver(web_contents) { + Inject(web_contents->GetPrimaryMainFrame()); + } + + ~TestAutofillManagerInjector() override = default; + + T* GetForPrimaryMainFrame() { + return GetForFrame(web_contents()->GetPrimaryMainFrame()); + } + + T* GetForFrame(content::RenderFrameHost* rfh) { + ContentAutofillDriverFactory* driver_factory = + ContentAutofillDriverFactory::FromWebContents(web_contents()); + return static_cast<T*>( + driver_factory->DriverForFrame(rfh)->autofill_manager()); + } + + private: + // content::WebContentsObserver: + void ReadyToCommitNavigation( + content::NavigationHandle* navigation_handle) override { + if (!navigation_handle->IsPrerenderedPageActivation() && + !navigation_handle->IsSameDocument()) { + Inject(navigation_handle->GetRenderFrameHost()); + } + } + + void Inject(content::RenderFrameHost* rfh) { + ContentAutofillDriverFactory* driver_factory = + ContentAutofillDriverFactory::FromWebContents(web_contents()); + AutofillClient* client = driver_factory->client(); + ContentAutofillDriver* driver = driver_factory->DriverForFrame(rfh); + driver->set_autofill_manager(CreateManager(driver, client)); + } + + std::unique_ptr<T> CreateManager(ContentAutofillDriver* driver, + AutofillClient* client) { + return std::make_unique<T>(driver, client); + } +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_TEST_AUTOFILL_MANAGER_INJECTOR_H_ diff --git a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom index 5db3e7b4486..e823c14b3f2 100644 --- a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom +++ b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom @@ -56,10 +56,10 @@ interface AutofillDriver { // |touch_to_fill_eligible| indicates if the Touch To Fill surface could // be used for showing suggestions (e.g. when the user taps an input element // but not on text change). - AskForValuesToFill(int32 query_id, - FormData form, + AskForValuesToFill(FormData form, FormFieldData field, gfx.mojom.RectF bounding_box, + int32 query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible); @@ -106,10 +106,8 @@ interface PasswordManagerDriver { PasswordFormsParsed(array<FormData> forms_data); // Notification that initial layout has occurred and the following password - // forms are visible on the page (e.g. not set to display:none.), and whether - // all frames in the page have been rendered. - PasswordFormsRendered(array<FormData> visible_forms_data, - bool did_stop_loading); + // forms are visible on the page (e.g. not set to display:none.). + PasswordFormsRendered(array<FormData> visible_forms_data); // Notification that this password form was submitted by the user. PasswordFormSubmitted(FormData form_data); diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc index 88d7f1dd32f..e9b7331aa6f 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/autofill_agent.cc @@ -46,7 +46,6 @@ #include "content/public/common/origin_util.h" #include "content/public/common/url_constants.h" #include "content/public/renderer/render_frame.h" -#include "content/public/renderer/render_view.h" #include "net/cert/cert_status_flags.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -176,14 +175,15 @@ class AutofillAgent::DeferringAutofillDriver : public mojom::AutofillDriver { void SelectFieldOptionsDidChange(const FormData& form) override { DeferMsg(&mojom::AutofillDriver::SelectFieldOptionsDidChange, form); } - void AskForValuesToFill(int32_t query_id, - const FormData& form, + void AskForValuesToFill(const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, + int32_t query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible) override { - DeferMsg(&mojom::AutofillDriver::AskForValuesToFill, query_id, form, field, - bounding_box, autoselect_first_suggestion, touch_to_fill_eligible); + DeferMsg(&mojom::AutofillDriver::AskForValuesToFill, form, field, + bounding_box, query_id, autoselect_first_suggestion, + touch_to_fill_eligible); } void HidePopup() override { DeferMsg(&mojom::AutofillDriver::HidePopup); } void FocusNoLongerOnForm(bool had_interacted_form) override { @@ -238,7 +238,7 @@ AutofillAgent::AutofillAgent(content::RenderFrame* render_frame, render_frame->GetWebFrame()->SetAutofillClient(this); password_autofill_agent->SetAutofillAgent(this); AddFormObserver(this); - registry->AddInterface(base::BindRepeating( + registry->AddInterface<mojom::AutofillAgent>(base::BindRepeating( &AutofillAgent::BindPendingReceiver, base::Unretained(this))); } @@ -255,14 +255,7 @@ void AutofillAgent::BindPendingReceiver( } void AutofillAgent::DidCommitProvisionalLoad(ui::PageTransition transition) { - blink::WebFrame* frame = render_frame()->GetWebFrame(); - // TODO(dvadym): check if we need to check if it is main frame navigation - // http://crbug.com/443155 - if (frame->Parent()) - return; // Not a top-level navigation. - // Navigation to a new page or a page refresh. - element_.Reset(); form_cache_.Reset(); @@ -295,14 +288,17 @@ void AutofillAgent::DidChangeScrollOffset() { void AutofillAgent::DidChangeScrollOffsetImpl( const WebFormControlElement& element) { - if (element != element_ || element_.IsNull() || focus_requires_scroll_ || - !is_popup_possibly_visible_ || !element_.Focused()) + if (element != element_ || element.IsNull() || focus_requires_scroll_ || + !is_popup_possibly_visible_ || !element.Focused()) { return; + } + + DCHECK(IsOwnedByFrame(element, render_frame())); FormData form; FormFieldData field; if (FindFormAndFieldForFormControlElement( - element_, field_data_manager_.get(), + element, field_data_manager_.get(), static_cast<ExtractMask>(form_util::EXTRACT_BOUNDS | GetExtractDatalistMask()), &form, &field)) { @@ -323,11 +319,13 @@ void AutofillAgent::FocusedElementChanged(const WebElement& element) { return; } - const WebInputElement input = element.DynamicTo<WebInputElement>(); + const WebFormControlElement form_control_element = + element.DynamicTo<WebFormControlElement>(); bool focus_moved_to_new_form = false; if (!last_interacted_form_.IsNull() && - (input.IsNull() || last_interacted_form_ != input.Form())) { + (form_control_element.IsNull() || + last_interacted_form_ != form_control_element.Form())) { // The focused element is not part of the last interacted form (could be // in a different form). GetAutofillDriver().FocusNoLongerOnForm(/*had_interacted_form=*/true); @@ -349,11 +347,13 @@ void AutofillAgent::FocusedElementChanged(const WebElement& element) { if (focus_moved_to_new_form) return; - if (input.IsNull() || !input.IsEnabled() || input.IsReadOnly() || - !input.IsTextField()) + if (form_control_element.IsNull() || !form_control_element.IsEnabled() || + form_control_element.IsReadOnly() || + !form_util::IsTextAreaElementOrTextInput(form_control_element)) { return; + } - element_ = input; + element_ = form_control_element; FormData form; FormFieldData field; @@ -526,8 +526,8 @@ void AutofillAgent::TriggerRefillIfNeeded(const FormData& form) { WebLocalFrame* frame = render_frame()->GetWebFrame(); std::vector<FormData> forms; forms.push_back(updated_form); - // Always communicate to browser process for topmost frame. - if (!forms.empty() || !frame->Parent()) { + // Always communicate to browser process for the outermost main frame. + if (!forms.empty() || frame->IsOutermostMainFrame()) { GetAutofillDriver().FormsSeen(forms, {}); } } @@ -630,9 +630,8 @@ void AutofillAgent::FillFieldWithValue(FieldRendererId field_id, return; } - WebInputElement input_element = element_.DynamicTo<WebInputElement>(); - if (!input_element.IsNull()) - DoFillFieldWithValue(value, input_element, WebAutofillState::kAutofilled); + if (form_util::IsTextAreaElementOrTextInput(element_)) + DoFillFieldWithValue(value, element_, WebAutofillState::kAutofilled); } void AutofillAgent::PreviewFieldWithValue(FieldRendererId field_id, @@ -688,14 +687,7 @@ void AutofillAgent::AcceptDataListSuggestion( WebInputElement input_element = element_.DynamicTo<WebInputElement>(); if (input_element.IsNull()) { - // For reasons not understood yet, this is triggered on elements which are - // not input elements. - - // TODO(crbug.com/1048270) Gather debug data. - DEBUG_ALIAS_FOR_CSTR(element_name, element_.TagName().Latin1().c_str(), 64); - base::debug::DumpWithoutCrashing(); - - // Keep this return after removing the TODO(crbug.com/1048270) above. + // Early return for non-input fields such as textarea. return; } std::u16string new_value = suggested_value; @@ -721,7 +713,7 @@ void AutofillAgent::AcceptDataListSuggestion( new_value = base::JoinString(parts, u","); } - DoFillFieldWithValue(new_value, input_element, WebAutofillState::kNotFilled); + DoFillFieldWithValue(new_value, element_, WebAutofillState::kNotFilled); } void AutofillAgent::FillPasswordSuggestion(const std::u16string& username, @@ -945,18 +937,24 @@ void AutofillAgent::QueryAutofillSuggestions( is_popup_possibly_visible_ = true; GetAutofillDriver().AskForValuesToFill( - autofill_query_id_, form, field, field.bounds, + form, field, field.bounds, autofill_query_id_, autoselect_first_suggestion, touch_to_fill_eligible); } void AutofillAgent::DoFillFieldWithValue(const std::u16string& value, - WebInputElement& node, + blink::WebFormControlElement& element, WebAutofillState autofill_state) { - DCHECK(IsOwnedByFrame(node, render_frame())); + DCHECK(IsOwnedByFrame(element, render_frame())); form_tracker_.set_ignore_control_changes(true); - node.SetAutofillValue(blink::WebString::FromUTF16(value), autofill_state); - password_autofill_agent_->UpdateStateForTextChange(node); + + element.SetAutofillValue(blink::WebString::FromUTF16(value), autofill_state); + + WebInputElement input_element = element.DynamicTo<WebInputElement>(); + // `input_element` can be null for textarea elements. + if (!input_element.IsNull()) + password_autofill_agent_->UpdateStateForTextChange(input_element); + form_tracker_.set_ignore_control_changes(false); } @@ -984,9 +982,9 @@ void AutofillAgent::ProcessForms() { FormCache::UpdateFormCacheResult cache = form_cache_.UpdateFormCache(field_data_manager_.get()); - // Always communicate to browser process for topmost frame. + // Always communicate to browser process for the outermost main frame. if (!cache.updated_forms.empty() || !cache.removed_forms.empty() || - !render_frame()->GetWebFrame()->Parent()) { + render_frame()->GetWebFrame()->IsOutermostMainFrame()) { GetAutofillDriver().FormsSeen(cache.updated_forms, std::move(cache.removed_forms).extract()); } @@ -1155,10 +1153,11 @@ void AutofillAgent::HandleFocusChangeComplete() { // are used, treat the focused node as if it was the last clicked. Also check // to ensure focus is on a field where text can be entered. if ((focused_node_was_last_clicked_ || is_screen_reader_enabled_) && - !focused_element.IsNull() && focused_element.IsFormControlElement() && - (form_util::IsTextInput(focused_element.DynamicTo<WebInputElement>()) || - focused_element.HasHTMLTagName("textarea"))) { - FormControlElementClicked(focused_element.To<WebFormControlElement>()); + !focused_element.IsNull() && focused_element.IsFormControlElement()) { + WebFormControlElement focused_form_control_element = + focused_element.To<WebFormControlElement>(); + if (form_util::IsTextAreaElementOrTextInput(focused_form_control_element)) + FormControlElementClicked(focused_form_control_element); } focused_node_was_last_clicked_ = false; @@ -1269,19 +1268,13 @@ void AutofillAgent::OnFormSubmitted(const WebFormElement& form) { } void AutofillAgent::OnInferredFormSubmission(SubmissionSource source) { - // Only handle iframe for FRAME_DETACHED or main frame for - // SAME_DOCUMENT_NAVIGATION. - if ((source == SubmissionSource::FRAME_DETACHED && - !render_frame()->GetWebFrame()->Parent()) || - (source == SubmissionSource::SAME_DOCUMENT_NAVIGATION && - render_frame()->GetWebFrame()->Parent())) { - ResetLastInteractedElements(); - OnFormNoLongerSubmittable(); - SendPotentiallySubmittedFormToBrowser(); - return; - } - - if (source == SubmissionSource::FRAME_DETACHED) { + if (source == SubmissionSource::FRAME_DETACHED && + render_frame()->GetWebFrame()->IsOutermostMainFrame()) { + // No op. + } else if (source == SubmissionSource::SAME_DOCUMENT_NAVIGATION && + !render_frame()->GetWebFrame()->IsOutermostMainFrame()) { + // No op. + } else if (source == SubmissionSource::FRAME_DETACHED) { // Should not access the frame because it is now detached. Instead, use // |provisionally_saved_form_|. if (provisionally_saved_form_.has_value()) diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h index 4de7f06270c..c2f1e044490 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.h +++ b/chromium/components/autofill/content/renderer/autofill_agent.h @@ -257,9 +257,9 @@ class AutofillAgent : public content::RenderFrameObserver, void DoAcceptDataListSuggestion(FieldRendererId field_id, const std::u16string& suggested_value); - // Set |node| to display the given |value|. + // Set `element` to display the given `value`. void DoFillFieldWithValue(const std::u16string& value, - blink::WebInputElement& node, + blink::WebFormControlElement& element, blink::WebAutofillState autofill_state); // Set |node| to display the given |value| as a preview. The preview is diff --git a/chromium/components/autofill/content/renderer/autofill_agent_browsertest.cc b/chromium/components/autofill/content/renderer/autofill_agent_browsertest.cc index ecd2e892178..e70e58e345a 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent_browsertest.cc +++ b/chromium/components/autofill/content/renderer/autofill_agent_browsertest.cc @@ -92,10 +92,10 @@ class MockAutofillDriver : public mojom::AutofillDriver { (override)); MOCK_METHOD(void, AskForValuesToFill, - (int32_t query_id, - const FormData& form, + (const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, + int32_t query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible), (override)); diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc index d6a787ff5bc..43c4e11fb7f 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc @@ -20,6 +20,7 @@ #include "base/containers/flat_set.h" #include "base/i18n/case_conversion.h" #include "base/metrics/field_trial.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/notreached.h" @@ -253,34 +254,37 @@ std::u16string FindChildTextInner(const WebNode& node, return std::u16string(); // Ignore elements known not to contain inferable labels. + bool skip_node = false; if (node.IsElementNode()) { const WebElement element = node.To<WebElement>(); - if (IsOptionElement(element) || IsScriptElement(element) || - IsNoScriptElement(element) || + if (IsOptionElement(element) || + (element.HasHTMLTagName("div") && base::Contains(divs_to_skip, node)) || (element.IsFormControlElement() && IsAutofillableElement(element.To<WebFormControlElement>()))) { return std::u16string(); } - - if (element.HasHTMLTagName("div") && base::Contains(divs_to_skip, node)) - return std::u16string(); + skip_node = IsScriptElement(element) || IsNoScriptElement(element); } - // Extract the text exactly at this node. - std::u16string node_text = node.NodeValue().Utf16(); + std::u16string node_text; - // Recursively compute the children's text. - // Preserve inter-element whitespace separation. - std::u16string child_text = - FindChildTextInner(node.FirstChild(), depth - 1, divs_to_skip); - bool add_space = node.IsTextNode() && node_text.empty(); - node_text = CombineAndCollapseWhitespace(node_text, child_text, add_space); + if (!skip_node) { + // Extract the text exactly at this node. + node_text = node.NodeValue().Utf16(); + + // Recursively compute the children's text. + // Preserve inter-element whitespace separation. + std::u16string child_text = + FindChildTextInner(node.FirstChild(), depth - 1, divs_to_skip); + bool add_space = node.IsTextNode() && node_text.empty(); + node_text = CombineAndCollapseWhitespace(node_text, child_text, add_space); + } // Recursively compute the siblings' text. // Again, preserve inter-element whitespace separation. std::u16string sibling_text = FindChildTextInner(node.NextSibling(), depth - 1, divs_to_skip); - add_space = node.IsTextNode() && node_text.empty(); + bool add_space = node.IsTextNode() && node_text.empty(); node_text = CombineAndCollapseWhitespace(node_text, sibling_text, add_space); return node_text; @@ -304,21 +308,14 @@ std::u16string FindChildTextWithIgnoreList( bool IsLabelValid(base::StringPiece16 inferred_label) { // List of characters a label can't be entirely made of (this list can grow). - auto IsStopWord = [](char16_t c) { - switch (c) { - case u' ': - case u'*': - case u':': - case u'-': - case u'–': // U+2013 - case u'(': - case u')': - return true; - default: - return false; - } - }; - return !base::ranges::all_of(inferred_label, IsStopWord); + const base::StringPiece16 invalid_chars = + base::FeatureList::IsEnabled( + features::kAutofillConsiderPhoneNumberSeparatorsValidLabels) + ? u" *:" + : u" *:-–()"; // U+2013 dash + return !base::ranges::all_of(inferred_label, [&](char16_t c) { + return base::Contains(invalid_chars, c); + }); } // Shared function for InferLabelFromPrevious() and InferLabelFromNext(). @@ -358,8 +355,13 @@ bool InferLabelFromSibling(const WebFormControlElement& element, // A text node's value will be empty if it is for a line break. bool add_space = sibling.IsTextNode() && value.empty(); inferred_label_source = FormFieldData::LabelSource::kCombined; - inferred_label = - CombineAndCollapseWhitespace(value, inferred_label, add_space); + if (forward) { + inferred_label = + CombineAndCollapseWhitespace(inferred_label, value, add_space); + } else { + inferred_label = + CombineAndCollapseWhitespace(value, inferred_label, add_space); + } continue; } @@ -649,8 +651,14 @@ std::u16string InferLabelFromTableRow(const WebNode& cell) { // e.g. <div>Some Text<span><input ...></span></div> // e.g. <div>Some Text</div><div><input ...></div> // -// Because this is already traversing the <div> structure, if it finds a <label> -// sibling along the way, infer from that <label>. +// Contrary to the other InferLabelFrom* functions, this functions walks up +// the DOM tree from the original input, instead of down from the surrounding +// tag. While doing so, if a <label> or text node sibling are found along the +// way, a label is inferred from them directly. For example, <div>First +// name<div><input></div>Last name<div><input></div></div> infers "First name" +// and "Last name" for the two inputs, respectively, by picking up the text +// nodes on the way to the surrounding div. Without doing so, the label of both +// inputs becomes "First nameLast name". std::u16string InferLabelFromDivTable(const WebFormControlElement& element) { WebNode node = element.ParentNode(); bool looking_for_parent = true; @@ -679,11 +687,21 @@ std::u16string InferLabelFromDivTable(const WebFormControlElement& element) { } looking_for_parent = false; - } else if (!looking_for_parent && HasTagName(node, *kLabel)) { - WebLabelElement label_element = node.To<WebLabelElement>(); - if (label_element.CorrespondingControl().IsNull()) + } else if (!looking_for_parent) { + // Infer a label from text nodes and unassigned <label> siblings. + if (HasTagName(node, *kLabel) && + node.To<WebLabelElement>().CorrespondingControl().IsNull()) { inferred_label = FindChildText(node); - } else if (looking_for_parent && IsTraversableContainerElement(node)) { + } else if (node.IsTextNode()) { + // TODO(crbug.com/796918): Ideally `FindChildText()` should be used + // here as well. But because the function doesn't trim it's return + // value on every code path, the `NodeValue()` is explicitly extracted + // here. Trimming is necessary to skip indentation. + inferred_label = node.NodeValue().Utf16(); + base::TrimWhitespace(inferred_label, base::TrimPositions::TRIM_ALL, + &inferred_label); + } + } else if (IsTraversableContainerElement(node)) { // If the element is in a non-div container, its label most likely is too. break; } @@ -1034,7 +1052,8 @@ std::vector<WebFormControlElement> ForEachMatchingFormFieldCommon( WebFormControlElement& element = *it; - element.SetAutofillSection(WebString::FromUTF8(data.fields[i].section)); + element.SetAutofillSection( + WebString::FromUTF8(data.fields[i].section.ToString())); // Only autofill empty fields (or those with the field's default value // attribute) and the field that initiated the filling, i.e. the field the @@ -1285,7 +1304,8 @@ struct CompareByRendererId { FormFieldData* SearchForFormControlByName( const std::u16string& field_name, const base::flat_set<std::pair<FormFieldData*, ShadowFieldData>, - CompareByRendererId>& field_set) { + CompareByRendererId>& field_set, + AssignedLabelSource& label_source) { if (field_name.empty()) return nullptr; @@ -1299,6 +1319,14 @@ FormFieldData* SearchForFormControlByName( base::Contains(p.second.shadow_host_id_attributes, field_name); }; it = base::ranges::find_if(field_set, ShadowHostHasTargetName); + if (it != end) { + label_source = + base::Contains(it->second.shadow_host_name_attributes, field_name) + ? AssignedLabelSource::kShadowHostName + : AssignedLabelSource::kShadowHostId; + } + } else { + label_source = AssignedLabelSource::kName; } return it != end ? it->first : nullptr; } @@ -1323,12 +1351,13 @@ void MatchLabelsAndFields( WebLabelElement label = item.To<WebLabelElement>(); WebElement control = label.CorrespondingControl(); FormFieldData* field_data = nullptr; + auto label_source = AssignedLabelSource::kId; if (control.IsNull()) { // Sometimes site authors will incorrectly specify the corresponding // field element's name rather than its id, so we compensate here. field_data = SearchForFormControlByName(label.GetAttribute(*kFor).Utf16(), - field_set); + field_set, label_source); } else if (control.IsFormControlElement()) { WebFormControlElement form_control = control.To<WebFormControlElement>(); if (form_control.FormControlTypeForAutofill() == *kHidden) @@ -1350,6 +1379,7 @@ void MatchLabelsAndFields( if (!field_data->label.empty() && !label_text.empty()) field_data->label += u" "; field_data->label += label_text; + base::UmaHistogramEnumeration(kAssignedLabelSourceHistogram, label_source); } } @@ -1452,7 +1482,8 @@ bool FormOrFieldsetsToFormData( } // Extracts field labels from the <label for="..."> tags. - { + if (!base::FeatureList::IsEnabled( + features::kAutofillImprovedLabelForInference)) { std::vector<std::pair<FormFieldData*, ShadowFieldData>> items; DCHECK_EQ(form->fields.size(), shadow_fields.size()); for (size_t i = 0; i < form->fields.size(); i++) { @@ -1597,6 +1628,22 @@ std::string GetAutocompleteAttribute(const WebElement& element) { return autocomplete_attribute; } +// Returns the concatenated label text of all labels assigned to the `element` +// using <label for=`element.GetIdAttribute()`>, separated by a space. +std::u16string GetAssignedLabel(const WebFormControlElement& element) { + std::u16string concatenated_labels; + for (const auto& label : element.Labels()) { + if (auto label_text = FindChildText(label); !label_text.empty()) { + if (!concatenated_labels.empty()) + concatenated_labels.push_back(' '); + concatenated_labels.append(std::move(label_text)); + base::UmaHistogramEnumeration(kAssignedLabelSourceHistogram, + AssignedLabelSource::kId); + } + } + return concatenated_labels; +} + void FindFormElementUpShadowRoots(const WebElement& element, WebFormElement* found_form_element) { // If we are in shadowdom, then look to see if the host(s) are inside a form @@ -1636,7 +1683,7 @@ bool IsVisibleIframe(const WebElement& element) { bool IsAdIframe(const WebElement& element) { DCHECK(element.HasHTMLTagName("iframe")); WebFrame* iframe = WebFrame::FromFrameOwnerElement(element); - return iframe && iframe->IsAdSubframe(); + return iframe && iframe->IsAdFrame(); } // A necessary condition for an iframe to be added to FormData::child_frames. @@ -1796,6 +1843,11 @@ bool IsTextAreaElement(const WebFormControlElement& element) { element.FormControlTypeForAutofill() == "textarea"; } +bool IsTextAreaElementOrTextInput(const WebFormControlElement& element) { + return IsTextAreaElement(element) || + IsTextInput(element.DynamicTo<WebInputElement>()); +} + bool IsCheckableElement(const WebInputElement& element) { if (element.IsNull()) return false; @@ -1822,7 +1874,7 @@ bool IsWebElementFocusable(const blink::WebElement& element) { return element.IsFocusable(); } -bool IsWebElementVisible(blink::WebElement element) { +bool IsWebElementVisible(const blink::WebElement& element) { auto HasMinSize = [](auto size) { constexpr int kMinPixelSize = 10; return size.width() >= kMinPixelSize && size.height() >= kMinPixelSize; @@ -1906,6 +1958,10 @@ void WebFormControlElementToFormField( field->form_control_ax_id = element.GetAxId(); field->form_control_type = element.FormControlTypeForAutofill().Utf8(); field->autocomplete_attribute = GetAutocompleteAttribute(element); + if (base::FeatureList::IsEnabled( + features::kAutofillImprovedLabelForInference)) { + field->label = GetAssignedLabel(element); + } if (base::EqualsCaseInsensitiveASCII(element.GetAttribute(*kRole).Utf16(), "presentation")) { field->role = FormFieldData::RoleAttribute::kPresentation; diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h index c9ae9cee1bc..0999f1ea7c1 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.h +++ b/chromium/components/autofill/content/renderer/form_autofill_util.h @@ -73,6 +73,22 @@ enum ExtractMask { // kMaxDataLength. }; +// Autofill supports assigning <label for=x> tags to inputs if x its id/name, +// or the id/name of a shadow host element containing the input. +// This enum is used to track how often each case occurs in practise. +enum class AssignedLabelSource { + kId = 0, + kName = 1, + kShadowHostId = 2, + kShadowHostName = 3, + kMaxValue = kShadowHostName, +}; +// This temporary histogram is emitted inline, because browser files like +// AutofillMetrics cannot be included here. +// TODO(crbug.com/1339277): Remove. +inline constexpr char kAssignedLabelSourceHistogram[] = + "Autofill.LabelInference.AssignedLabelSource"; + // Indicates if an iframe |element| is considered actually visible to the user. // // This function is not intended to implement a perfect visibility check. It @@ -157,6 +173,9 @@ bool IsSelectElement(const blink::WebFormControlElement& element); // Returns true if |element| is a textarea element. bool IsTextAreaElement(const blink::WebFormControlElement& element); +// Returns true if `element` is a textarea element or a text input element. +bool IsTextAreaElementOrTextInput(const blink::WebFormControlElement& element); + // Returns true if |element| is a checkbox or a radio button element. bool IsCheckableElement(const blink::WebInputElement& element); @@ -194,7 +213,7 @@ bool IsWebElementFocusable(const blink::WebElement& element); // Exposed for testing purposes. // // TODO(crbug.com/1335257): Can input fields or iframes actually overflow? -bool IsWebElementVisible(blink::WebElement element); +bool IsWebElementVisible(const blink::WebElement& element); // Returns the form's |name| attribute if non-empty; otherwise the form's |id| // attribute. diff --git a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc index ba5a2e5c28b..562d0d2e709 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc @@ -16,7 +16,6 @@ #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" #include "components/autofill/core/common/unique_ids.h" #include "content/public/renderer/render_frame.h" -#include "content/public/renderer/render_view.h" #include "content/public/test/render_view_test.h" #include "content/public/test/test_utils.h" #include "testing/gmock/include/gmock/gmock.h" @@ -127,7 +126,12 @@ TEST_F(FormAutofillUtilsTest, FindChildTextTest) { "</div></div></div></div></div></div></div></div></div></div></div></" "div>", u"child0child1child2child3child4"}, - }; + {"Skip script tags", + "<div id='target'><script>alert('hello');</script>label</div>", + u"label"}, + {"Script tag whitespacing", + "<div id='target'>Auto<script>alert('hello');</script>fill</div>", + u"Autofill"}}; for (auto test_case : test_cases) { SCOPED_TRACE(test_case.description); LoadHTML(test_case.html); @@ -188,29 +192,29 @@ TEST_F(FormAutofillUtilsTest, InferLabelForElementTest) { <div><input id=target></div> </div>)", u"label"}, - // TODO(crbug.com/796918): Should be label {"DIV table test 4", R"( <div> <div>should be skipped<input></div> label <div><input id=target></div> </div>)", - u""}, - // TODO(crbug.com/796918): Should be label + u"label"}, {"DIV table test 5", "<div>" "<div>label<div><input id='target'/></div>behind</div>" "</div>", - u"labelbehind"}, + u"label"}, {"DIV table test 6", R"( <div> label - <div>-</div> + <div>*</div> <div><input id='target'></div> </div>)", - // TODO(crbug.com/796918): Should be "label" or "label-". This happens - // because "-" is inferred, but discarded because `!IsLabelValid()`. + // TODO(crbug.com/796918): Should be "label" or "label*". This happens + // because "*" is inferred, but discarded because `!IsLabelValid()`. u""}, + {"Infer from next sibling", + "<input id='target' type='checkbox'>hello <b>world</b>", u"hello world"}, }; for (auto test_case : test_cases) { SCOPED_TRACE(test_case.description); @@ -249,7 +253,11 @@ TEST_F(FormAutofillUtilsTest, InferLabelSourceTest) { FormFieldData::LabelSource::kAriaLabel}, {"<input id='target' value='label'/>", FormFieldData::LabelSource::kValue}, + // In the next test, the text node is picked up on the way up the DOM-tree + // by the div extraction logic. {"<li>label<div><input id='target'/></div></li>", + FormFieldData::LabelSource::kDivTable}, + {"<li><span>label</span><div><input id='target'/></div></li>", FormFieldData::LabelSource::kLiTag}, {"<table><tr><td>label</td><td><input id='target'/></td></tr></table>", FormFieldData::LabelSource::kTdTag}, diff --git a/chromium/components/autofill/content/renderer/form_cache.cc b/chromium/components/autofill/content/renderer/form_cache.cc index be8cf7d64e2..3f5bbf250a6 100644 --- a/chromium/components/autofill/content/renderer/form_cache.cc +++ b/chromium/components/autofill/content/renderer/form_cache.cc @@ -383,8 +383,10 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form, // If the flag is enabled, attach the prediction to the field. if (attach_predictions_to_dom) { constexpr size_t kMaxLabelSize = 100; - const std::u16string truncated_label = field_data.label.substr( - 0, std::min(field_data.label.length(), kMaxLabelSize)); + // TODO(crbug/1165780): Use `parseable_label()` once the feature is + // launched. + const std::u16string truncated_label = + field_data.label.substr(0, kMaxLabelSize); std::string form_id = base::NumberToString(form.data.unique_renderer_id.value()); @@ -420,6 +422,12 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form, "\nfield renderer id: ", field_id_str}); + WebString kAutocomplete = WebString::FromASCII("autocomplete"); + if (element.HasAttribute(kAutocomplete)) { + title += "\nautocomplete: " + + element.GetAttribute(kAutocomplete).Utf8().substr(0, 100); + } + // Set this debug string to the title so that a developer can easily debug // by hovering the mouse over the input field. element.SetAttribute("title", WebString::FromUTF8(title)); diff --git a/chromium/components/autofill/content/renderer/form_tracker.cc b/chromium/components/autofill/content/renderer/form_tracker.cc index 1cd65a47621..f7bc4bd9bba 100644 --- a/chromium/components/autofill/content/renderer/form_tracker.cc +++ b/chromium/components/autofill/content/renderer/form_tracker.cc @@ -130,8 +130,11 @@ void FormTracker::FormControlDidChangeImpl( const WebFormControlElement& element, Observer::ElementChangeSource change_source) { DCHECK_CALLED_ON_VALID_SEQUENCE(form_tracker_sequence_checker_); - // Render frame could be gone as this is the post task. - if (!render_frame()) return; + // The frame or document could be null because this function is called + // asynchronously. + const blink::WebDocument& doc = element.GetDocument(); + if (!render_frame() || doc.IsNull() || !doc.GetFrame()) + return; if (element.Form().IsNull()) { last_interacted_formless_element_ = element; @@ -159,8 +162,8 @@ void FormTracker::DidStartNavigation( absl::optional<blink::WebNavigationType> navigation_type) { DCHECK_CALLED_ON_VALID_SEQUENCE(form_tracker_sequence_checker_); blink::WebLocalFrame* navigated_frame = render_frame()->GetWebFrame(); - // Ony handle main frame. - if (navigated_frame->Parent()) + // Ony handle primary main frame. + if (!navigated_frame->IsOutermostMainFrame()) return; // Bug fix for crbug.com/368690. isProcessingUserGesture() is false when diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc index b302034d368..7fa1f1f1bf9 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc @@ -41,7 +41,6 @@ #include "components/password_manager/core/common/password_manager_features.h" #include "components/safe_browsing/buildflags.h" #include "content/public/renderer/render_frame.h" -#include "content/public/renderer/render_view.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" @@ -353,7 +352,7 @@ WebInputElement FindUsernameElementPrecedingPasswordElement( // Returns true if |element|'s frame origin is not PSL matched with the origin // of any parent frame. -bool IsInCrossOriginIframe(const WebInputElement& element) { +bool IsInCrossOriginIframeOrEmbeddedFrame(const WebInputElement& element) { WebFrame* cur_frame = element.GetDocument().GetFrame(); WebString bottom_frame_origin = cur_frame->GetSecurityOrigin().ToString(); @@ -367,6 +366,17 @@ bool IsInCrossOriginIframe(const WebInputElement& element) { return true; } } + // In MPArch, if we haven't reached the primary main frame, it means + // we are in a nested frame tree. Fenced Frames are always considered + // cross origin so we should return true here. Adding NOTREACHED for now + // for future nested inner frame trees. + if (!cur_frame->IsOutermostMainFrame()) { + if (element.GetDocument().GetFrame()->IsInFencedFrameTree()) { + return true; + } else { + NOTREACHED(); + } + } return false; } @@ -534,10 +544,10 @@ class PasswordAutofillAgent::DeferringPasswordManagerDriver void PasswordFormsParsed(const std::vector<FormData>& forms_data) override { DeferMsg(&mojom::PasswordManagerDriver::PasswordFormsParsed, forms_data); } - void PasswordFormsRendered(const std::vector<FormData>& visible_forms_data, - bool did_stop_loading) override { + void PasswordFormsRendered( + const std::vector<FormData>& visible_forms_data) override { DeferMsg(&mojom::PasswordManagerDriver::PasswordFormsRendered, - visible_forms_data, did_stop_loading); + visible_forms_data); } void PasswordFormSubmitted(const FormData& form_data) override { DeferMsg(&mojom::PasswordManagerDriver::PasswordFormSubmitted, form_data); @@ -616,7 +626,7 @@ PasswordAutofillAgent::PasswordAutofillAgent( checked_safe_browsing_reputation_(false), focus_state_notifier_(this), password_generation_agent_(nullptr) { - registry->AddInterface(base::BindRepeating( + registry->AddInterface<mojom::PasswordAutofillAgent>(base::BindRepeating( &PasswordAutofillAgent::BindPendingReceiver, base::Unretained(this))); } @@ -1319,10 +1329,7 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) { // Send the PasswordFormsRendered message regardless of whether // |password_forms_data| is empty. The empty |password_forms_data| are a // possible signal to the browser that a pending login attempt succeeded. - WebFrame* main_frame = render_frame()->GetWebFrame()->Top(); - bool did_stop_loading = !main_frame || !main_frame->IsLoading(); - GetPasswordManagerDriver().PasswordFormsRendered(password_forms_data, - did_stop_loading); + GetPasswordManagerDriver().PasswordFormsRendered(password_forms_data); } else { // If there is a password field, but the list of password forms is empty for // some reason, add a dummy form to the list. It will cause a request to the @@ -1369,7 +1376,9 @@ void PasswordAutofillAgent::OnFrameDetached() { // If a sub frame has been destroyed while the user was entering information // into a password form, try to save the data. See https://crbug.com/450806 // for examples of sites that perform login using this technique. - if (render_frame()->GetWebFrame()->Parent() && browser_has_form_to_process_) { + // We are treating primary main frame and the root of embedded frames the same + // on purpose. + if (browser_has_form_to_process_ && render_frame()->GetWebFrame()->Parent()) { DCHECK(FrameCanAccessPasswordManager()); GetPasswordManagerDriver().DynamicFormSubmission( SubmissionIndicatorEvent::FRAME_DETACHED); @@ -1397,12 +1406,12 @@ void PasswordAutofillAgent::ReadyToCommitNavigation( } WebLocalFrame* navigated_frame = render_frame()->GetWebFrame(); - if (navigated_frame->Parent()) { - LogMessage(logger.get(), Logger::STRING_FRAME_NOT_MAIN_FRAME); - } else { + if (navigated_frame->IsOutermostMainFrame()) { // This is a new navigation, so require a new user gesture before filling in // passwords. gatekeeper_.Reset(); + } else { + LogMessage(logger.get(), Logger::STRING_FRAME_NOT_MAIN_FRAME); } CleanupOnDocumentShutdown(); @@ -1776,7 +1785,7 @@ bool PasswordAutofillAgent::FillUserNameAndPassword( WebInputElement main_element = is_single_username_fill ? username_element : password_element; - if (IsInCrossOriginIframe(main_element)) { + if (IsInCrossOriginIframeOrEmbeddedFrame(main_element)) { LogMessage(logger, Logger::STRING_FAILED_TO_FILL_INTO_IFRAME); LogFirstFillingResult(fill_data, FillingResult::kBlockedByFrameHierarchy); return false; diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc index e780918243d..ce8c5c7606a 100644 --- a/chromium/components/autofill/content/renderer/password_generation_agent.cc +++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc @@ -23,7 +23,6 @@ #include "components/autofill/core/common/password_generation_util.h" #include "components/autofill/core/common/signatures.h" #include "content/public/renderer/render_frame.h" -#include "content/public/renderer/render_view.h" #include "google_apis/gaia/gaia_urls.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" #include "third_party/blink/public/common/features.h" @@ -206,7 +205,7 @@ struct PasswordGenerationAgent::GenerationItemInfo { // True when PasswordGenerationAgent updates other password fields on the page // due to the generated password being edited. It's used to suppress the fake // blur events coming from there. - bool updating_other_password_fileds_ = false; + bool updating_other_password_fields_ = false; }; PasswordGenerationAgent::PasswordGenerationAgent( @@ -218,7 +217,7 @@ PasswordGenerationAgent::PasswordGenerationAgent( base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kShowAutofillSignatures)), password_agent_(password_agent) { - registry->AddInterface(base::BindRepeating( + registry->AddInterface<mojom::PasswordGenerationAgent>(base::BindRepeating( &PasswordGenerationAgent::BindPendingReceiver, base::Unretained(this))); password_agent_->SetPasswordGenerationAgent(this); } @@ -233,8 +232,8 @@ void PasswordGenerationAgent::BindPendingReceiver( void PasswordGenerationAgent::DidCommitProvisionalLoad( ui::PageTransition transition) { - // Update stats for main frame navigation. - if (!render_frame()->GetWebFrame()->Parent()) { + // Update stats for primary main frame navigation. + if (render_frame()->GetWebFrame()->IsOutermostMainFrame()) { if (current_generation_item_) { if (current_generation_item_->password_edited_) { password_generation::LogPasswordGenerationEvent( @@ -281,7 +280,7 @@ void PasswordGenerationAgent::OnFieldAutofilled( bool PasswordGenerationAgent::ShouldIgnoreBlur() const { return current_generation_item_ && - current_generation_item_->updating_other_password_fileds_; + current_generation_item_->updating_other_password_fields_; } bool PasswordGenerationAgent::IsPrerendering() const { @@ -302,7 +301,7 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted( LogMessage(Logger::STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED); for (auto& password_element : current_generation_item_->password_elements_) { base::AutoReset<bool> auto_reset_update_confirmation_password( - ¤t_generation_item_->updating_other_password_fileds_, true); + ¤t_generation_item_->updating_other_password_fields_, true); password_element.SetAutofillValue(blink::WebString::FromUTF16(password)); // setAutofillValue() above may have resulted in JavaScript closing the // frame. @@ -555,7 +554,7 @@ bool PasswordGenerationAgent::TextDidChangeInTextField( } else if (current_generation_item_->password_is_generated_) { current_generation_item_->password_edited_ = true; base::AutoReset<bool> auto_reset_update_confirmation_password( - ¤t_generation_item_->updating_other_password_fileds_, true); + ¤t_generation_item_->updating_other_password_fields_, true); // Mirror edits to any confirmation password fields. CopyElementValueToOtherInputElements( &element, ¤t_generation_item_->password_elements_); @@ -650,7 +649,7 @@ void PasswordGenerationAgent::PasswordNoLongerGenerated() { for (WebInputElement& element : current_generation_item_->password_elements_) { base::AutoReset<bool> auto_reset_update_confirmation_password( - ¤t_generation_item_->updating_other_password_fileds_, true); + ¤t_generation_item_->updating_other_password_fields_, true); if (current_generation_item_->generation_element_ != element) element.SetAutofillValue(blink::WebString()); } 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 6475369839a..d7b89c20bb9 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 @@ -45,8 +45,7 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver { const std::vector<autofill::FormData>& form_data) override {} void PasswordFormsRendered( - const std::vector<autofill::FormData>& visible_forms_data, - bool did_stop_loading) override {} + const std::vector<autofill::FormData>& visible_forms_data) override {} void PasswordFormSubmitted(const autofill::FormData& form_data) override {} diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn index 04a0eb767c9..e7579184760 100644 --- a/chromium/components/autofill/core/browser/BUILD.gn +++ b/chromium/components/autofill/core/browser/BUILD.gn @@ -4,7 +4,7 @@ import("//build/buildflag_header.gni") import("//build/config/chrome_build.gni") -import("//build/util/version.gni") +import("//chrome/version.gni") import("//testing/libfuzzer/fuzzer_test.gni") import("//tools/grit/grit_rule.gni") if (is_android) { @@ -104,10 +104,6 @@ static_library("browser") { "autofill_profile_sync_util.h", "autofill_profile_update_strike_database.cc", "autofill_profile_update_strike_database.h", - "autofill_regex_constants.cc", - "autofill_regex_constants.h", - "autofill_regexes.cc", - "autofill_regexes.h", "autofill_subject.cc", "autofill_subject.h", "autofill_suggestion_generator.cc", @@ -159,6 +155,8 @@ static_library("browser") { "data_model/data_model_utils.h", "data_model/form_group.cc", "data_model/form_group.h", + "data_model/iban.cc", + "data_model/iban.h", "data_model/phone_number.cc", "data_model/phone_number.h", "data_model/test_data_creator.cc", @@ -169,11 +167,15 @@ static_library("browser") { "field_types.h", "form_data_importer.cc", "form_data_importer.h", + "form_data_importer_utils.cc", + "form_data_importer_utils.h", "form_parsing/address_field.cc", "form_parsing/address_field.h", "form_parsing/autofill_parsing_utils.h", "form_parsing/autofill_scanner.cc", "form_parsing/autofill_scanner.h", + "form_parsing/birthdate_field.cc", + "form_parsing/birthdate_field.h", "form_parsing/credit_card_field.cc", "form_parsing/credit_card_field.h", "form_parsing/email_field.cc", @@ -182,6 +184,8 @@ static_library("browser") { "form_parsing/field_candidates.h", "form_parsing/form_field.cc", "form_parsing/form_field.h", + "form_parsing/iban_field.cc", + "form_parsing/iban_field.h", "form_parsing/merchant_promo_code_field.cc", "form_parsing/merchant_promo_code_field.h", "form_parsing/name_field.cc", @@ -227,6 +231,8 @@ static_library("browser") { "geo/state_names.h", "geo/subkey_requester.cc", "geo/subkey_requester.h", + "iban_manager.cc", + "iban_manager.h", "logging/log_buffer_submitter.cc", "logging/log_buffer_submitter.h", "logging/log_manager.cc", @@ -248,6 +254,8 @@ static_library("browser") { "metrics/form_events/form_events.h", "metrics/form_interactions_counter.cc", "metrics/form_interactions_counter.h", + "metrics/payments/local_card_migration_metrics.cc", + "metrics/payments/local_card_migration_metrics.h", "metrics/payments/manage_cards_prompt_metrics.cc", "metrics/payments/manage_cards_prompt_metrics.h", "metrics/payments/offers_metrics.cc", @@ -344,8 +352,8 @@ static_library("browser") { "suggestions_context.cc", "suggestions_context.h", "sync_utils.h", - "touch_to_fill_delegate.cc", - "touch_to_fill_delegate.h", + "touch_to_fill_delegate_impl.cc", + "touch_to_fill_delegate_impl.h", "ui/accessory_sheet_data.cc", "ui/accessory_sheet_data.h", "ui/accessory_sheet_enums.h", @@ -384,6 +392,7 @@ static_library("browser") { "ui/suggestion.h", "ui/suggestion_selection.cc", "ui/suggestion_selection.h", + "ui/touch_to_fill_delegate.h", "validation.cc", "validation.h", "webdata/autocomplete_sync_bridge.cc", @@ -423,13 +432,6 @@ static_library("browser") { sources += get_target_outputs(":regex_patterns_inl_h") - if (is_win) { - sources += [ - "autofill_ie_toolbar_import_win.cc", - "autofill_ie_toolbar_import_win.h", - ] - } - if (is_ios) { sources += [ "autofill_save_update_address_profile_delegate_ios.cc", @@ -507,6 +509,7 @@ static_library("browser") { "//base:i18n", "//build:branding_buildflags", "//build:chromeos_buildflags", + "//components/autofill_assistant/core/public:public", "//components/feature_engagement", "//components/google/core/common", "//components/history/core/browser", @@ -600,6 +603,7 @@ static_library("test_support") { "autofill_test_utils.h", "data_model/test_autofill_data_model.cc", "data_model/test_autofill_data_model.h", + "form_structure_test_api.cc", "form_structure_test_api.h", "geo/alternative_state_name_map_test_utils.cc", "geo/alternative_state_name_map_test_utils.h", @@ -613,6 +617,8 @@ static_library("test_support") { "metrics/autofill_metrics_test_base.h", "mock_autocomplete_history_manager.cc", "mock_autocomplete_history_manager.h", + "mock_iban_manager.cc", + "mock_iban_manager.h", "mock_merchant_promo_code_manager.cc", "mock_merchant_promo_code_manager.h", "mock_single_field_form_fill_router.cc", @@ -646,6 +652,8 @@ static_library("test_support") { "test_autofill_driver.h", "test_autofill_external_delegate.cc", "test_autofill_external_delegate.h", + "test_autofill_manager_waiter.cc", + "test_autofill_manager_waiter.h", "test_autofill_tick_clock.cc", "test_autofill_tick_clock.h", "test_browser_autofill_manager.cc", @@ -653,8 +661,6 @@ static_library("test_support") { "test_event_waiter.h", "test_form_data_importer.cc", "test_form_data_importer.h", - "test_form_structure.cc", - "test_form_structure.h", "test_inmemory_strike_database.cc", "test_inmemory_strike_database.h", "test_personal_data_manager.cc", @@ -784,7 +790,6 @@ source_set("unit_tests") { "autofill_profile_save_strike_database_unittest.cc", "autofill_profile_sync_util_unittest.cc", "autofill_profile_update_strike_database_unittest.cc", - "autofill_regexes_unittest.cc", "autofill_subject_unittest.cc", "autofill_suggestion_generator_unittest.cc", "autofill_type_unittest.cc", @@ -804,14 +809,18 @@ source_set("unit_tests") { "data_model/borrowed_transliterator_unittest.cc", "data_model/contact_info_unittest.cc", "data_model/credit_card_unittest.cc", + "data_model/iban_unittest.cc", "data_model/phone_number_unittest.cc", "field_filler_unittest.cc", "field_types_unittest.cc", "form_data_importer_unittest.cc", + "form_data_importer_utils_unittest.cc", "form_parsing/address_field_unittest.cc", + "form_parsing/birthdate_field_unittest.cc", "form_parsing/credit_card_field_unittest.cc", "form_parsing/field_candidates_unittest.cc", "form_parsing/form_field_unittest.cc", + "form_parsing/iban_field_unittest.cc", "form_parsing/merchant_promo_code_field_unittest.cc", "form_parsing/name_field_unittest.cc", "form_parsing/parsing_test_utils.cc", @@ -831,6 +840,7 @@ source_set("unit_tests") { "geo/country_names_unittest.cc", "geo/phone_number_i18n_unittest.cc", "geo/subkey_requester_unittest.cc", + "iban_manager_unittest.cc", "logging/log_buffer_submitter_unittest.cc", "logging/log_manager_unittest.cc", "logging/log_router_unittest.cc", @@ -853,6 +863,9 @@ source_set("unit_tests") { "payments/virtual_card_enrollment_manager_unittest.cc", "payments/virtual_card_enrollment_strike_database_unittest.cc", "payments/wait_for_signal_or_timeout_unittest.cc", + "personal_data_manager_cleaner_unittest.cc", + "personal_data_manager_test_base.cc", + "personal_data_manager_test_base.h", "personal_data_manager_unittest.cc", "randomized_encoder_unittest.cc", "rationalization_util_unittest.cc", @@ -861,6 +874,7 @@ source_set("unit_tests") { "strike_database_unittest.cc", "test_utils/test_profiles.cc", "test_utils/test_profiles.h", + "touch_to_fill_delegate_impl_unittest.cc", "ui/address_combobox_model_unittest.cc", "ui/autofill_image_fetcher_unittest.cc", "ui/country_combobox_model_unittest.cc", @@ -879,10 +893,6 @@ source_set("unit_tests") { "webdata/web_data_service_unittest.cc", ] - if (is_win) { - sources += [ "autofill_ie_toolbar_import_win_unittest.cc" ] - } - if (is_ios) { sources += [ "autofill_save_update_address_profile_delegate_ios_unittest.cc" ] @@ -929,6 +939,7 @@ source_set("unit_tests") { "//base/test:test_support", "//build:chromeos_buildflags", "//components/autofill/core/common", + "//components/autofill_assistant/core/public:public", "//components/feature_engagement", "//components/image_fetcher/core:core", "//components/image_fetcher/core:test_support", @@ -942,8 +953,7 @@ source_set("unit_tests") { "//components/signin/public/identity_manager:test_support", "//components/strings", "//components/sync", - "//components/sync:test_support_model", - "//components/sync/driver:test_support", + "//components/sync:test_support", "//components/translate/core/browser", "//components/translate/core/common", "//components/ukm", diff --git a/chromium/components/autofill/core/browser/address_profile_save_manager_unittest.cc b/chromium/components/autofill/core/browser/address_profile_save_manager_unittest.cc index 99ed61bc8ec..08faa9eb532 100644 --- a/chromium/components/autofill/core/browser/address_profile_save_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/address_profile_save_manager_unittest.cc @@ -427,28 +427,23 @@ void AddressProfileSaveManagerTest::TestImportScenario( } if (is_confirmable_merge && - test_scenario.user_decision == UserDecision::kAccepted) { - histogram_tester.ExpectTotalCount( - kProfileUpdateAffectedTypesHistogram, - test_scenario.expected_affeceted_types_in_merge_for_metrics.size()); - + (test_scenario.user_decision == UserDecision::kAccepted || + test_scenario.user_decision == UserDecision::kDeclined)) { + std::string changed_histogram_suffix; + switch (test_scenario.user_decision) { + case UserDecision::kAccepted: + changed_histogram_suffix = ".Accepted"; + break; + + case UserDecision::kDeclined: + changed_histogram_suffix = ".Declined"; + break; + + default: + NOTREACHED() << "Decision not covered by test logic."; + } for (auto changed_type : test_scenario.expected_affeceted_types_in_merge_for_metrics) { - histogram_tester.ExpectBucketCount(kProfileUpdateAffectedTypesHistogram, - changed_type, 1); - std::string changed_histogram_suffix; - switch (test_scenario.user_decision) { - case UserDecision::kAccepted: - changed_histogram_suffix = ".Accepted"; - break; - - case UserDecision::kDeclined: - changed_histogram_suffix = ".Declined"; - break; - - default: - NOTREACHED() << "Decision not covered by test logic."; - } histogram_tester.ExpectBucketCount( base::StrCat({kProfileUpdateAffectedTypesHistogram, changed_histogram_suffix}), @@ -456,7 +451,8 @@ void AddressProfileSaveManagerTest::TestImportScenario( } histogram_tester.ExpectUniqueSample( - kProfileUpdateNumberOfAffectedTypesHistogram, + base::StrCat({kProfileUpdateNumberOfAffectedTypesHistogram, + changed_histogram_suffix}), test_scenario.expected_affeceted_types_in_merge_for_metrics.size(), 1); } @@ -907,7 +903,10 @@ TEST_P(AddressProfileSaveManagerTest, UserConfirmableMerge_Declined) { .is_profile_change_expected = false, .merge_candidate = mergeable_profile, .import_candidate = final_profile, - .expected_final_profiles = {mergeable_profile}}; + .expected_final_profiles = {mergeable_profile}, + .expected_affeceted_types_in_merge_for_metrics = { + AutofillMetrics::SettingsVisibleFieldTypeForMetrics::kZip, + AutofillMetrics::SettingsVisibleFieldTypeForMetrics::kCity}}; TestImportScenario(test_scenario); } @@ -1070,7 +1069,10 @@ TEST_P(AddressProfileSaveManagerTest, .merge_candidate = mergeable_profile, .import_candidate = merged_profile, .expected_final_profiles = {existing_duplicate, updated_profile, - mergeable_profile}}; + mergeable_profile}, + .expected_affeceted_types_in_merge_for_metrics = { + AutofillMetrics::SettingsVisibleFieldTypeForMetrics::kZip, + AutofillMetrics::SettingsVisibleFieldTypeForMetrics::kCity}}; TestImportScenario(test_scenario); } diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc index de40d0d5282..cc1ba6db9d2 100644 --- a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc +++ b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc @@ -11,10 +11,10 @@ #include "base/bind.h" #include "base/containers/cxx20_erase.h" +#include "base/debug/dump_without_crashing.h" #include "base/memory/weak_ptr.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_experiments.h" -#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" #include "components/autofill/core/browser/suggestions_context.h" #include "components/autofill/core/browser/ui/suggestion.h" @@ -23,6 +23,7 @@ #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_prefs.h" +#include "components/autofill/core/common/autofill_regexes.h" #include "components/autofill/core/common/form_data.h" #include "components/prefs/pref_service.h" #include "components/version_info/version_info.h" @@ -51,9 +52,9 @@ bool IsTextField(const FormFieldData& field) { // that a different website or different form uses the same field name for a // totally different purpose. bool IsMeaningfulFieldName(const std::u16string& name) { - return !MatchesPattern( - name, - u"^(((field|input)(_|-)?\\d+)|title|otp|tan)$|(cvc|cvn|cvv|captcha)"); + static constexpr char16_t kRegex[] = + u"^(((field|input)(_|-)?\\d+)|title|otp|tan)$|(cvc|cvn|cvv|captcha)"; + return !MatchesRegex<kRegex>(name); } } // namespace @@ -78,37 +79,44 @@ AutocompleteHistoryManager::~AutocompleteHistoryManager() { CancelAllPendingQueries(); } -void AutocompleteHistoryManager::OnGetSingleFieldSuggestions( +bool AutocompleteHistoryManager::OnGetSingleFieldSuggestions( int query_id, bool is_autocomplete_enabled, bool autoselect_first_suggestion, - const std::u16string& name, - const std::u16string& prefix, - const std::string& form_control_type, + const FormFieldData& field, base::WeakPtr<SuggestionsHandler> handler, const SuggestionsContext& context) { + if (!field.should_autocomplete) + return false; + CancelPendingQueries(handler.get()); - if (!IsMeaningfulFieldName(name) || !is_autocomplete_enabled || - form_control_type == "textarea" || + if (!IsMeaningfulFieldName(field.name) || !is_autocomplete_enabled || + field.form_control_type == "textarea" || IsInAutofillSuggestionsDisabledExperiment()) { SendSuggestions({}, QueryHandler(query_id, autoselect_first_suggestion, - prefix, handler)); - uma_recorder_.OnGetAutocompleteSuggestions(name, + field.value, handler)); + uma_recorder_.OnGetAutocompleteSuggestions(field.name, 0 /* pending_query_handle */); - return; + return true; } if (profile_database_) { auto query_handle = profile_database_->GetFormValuesForElementName( - name, prefix, kMaxAutocompleteMenuItems, this); - uma_recorder_.OnGetAutocompleteSuggestions(name, query_handle); + field.name, field.value, kMaxAutocompleteMenuItems, this); + uma_recorder_.OnGetAutocompleteSuggestions(field.name, query_handle); // We can simply insert, since |query_handle| is always unique. pending_queries_.insert( - {query_handle, - QueryHandler(query_id, autoselect_first_suggestion, prefix, handler)}); + {query_handle, QueryHandler(query_id, autoselect_first_suggestion, + field.value, handler)}); + return true; } + + // TODO(crbug.com/1190334): Remove this after ensuring that in practice + // |profile_database_| is never null. + base::debug::DumpWithoutCrashing(); + return false; } void AutocompleteHistoryManager::OnWillSubmitFormWithFields( diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager.h b/chromium/components/autofill/core/browser/autocomplete_history_manager.h index bfb446ce9ee..546bc670af8 100644 --- a/chromium/components/autofill/core/browser/autocomplete_history_manager.h +++ b/chromium/components/autofill/core/browser/autocomplete_history_manager.h @@ -44,14 +44,13 @@ class AutocompleteHistoryManager : public SingleFieldFormFiller, ~AutocompleteHistoryManager() override; // SingleFieldFormFiller overrides: - void OnGetSingleFieldSuggestions(int query_id, - bool is_autocomplete_enabled, - bool autoselect_first_suggestion, - const std::u16string& name, - const std::u16string& prefix, - const std::string& form_control_type, - base::WeakPtr<SuggestionsHandler> handler, - const SuggestionsContext& context) override; + [[nodiscard]] bool OnGetSingleFieldSuggestions( + int query_id, + bool is_autocomplete_enabled, + bool autoselect_first_suggestion, + const FormFieldData& field, + base::WeakPtr<SuggestionsHandler> handler, + const SuggestionsContext& context) override; void OnWillSubmitFormWithFields(const std::vector<FormFieldData>& fields, bool is_autocomplete_enabled) override; void CancelPendingQueries(const SuggestionsHandler* handler) override; diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc index aae2f13d31c..fb24001e6f9 100644 --- a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc @@ -101,6 +101,8 @@ class AutocompleteHistoryManagerTest : public testing::Test { web_data_service_ = base::MakeRefCounted<MockAutofillWebDataService>(); autocomplete_manager_ = std::make_unique<AutocompleteHistoryManager>(); autocomplete_manager_->Init(web_data_service_, prefs_.get(), false); + test::CreateTestFormField(/*label=*/"", "Some Field Name", "SomePrefix", + "Some Type", &test_field_); } void TearDown() override { @@ -144,6 +146,7 @@ class AutocompleteHistoryManagerTest : public testing::Test { scoped_refptr<MockAutofillWebDataService> web_data_service_; std::unique_ptr<AutocompleteHistoryManager> autocomplete_manager_; std::unique_ptr<PrefService> prefs_; + FormFieldData test_field_; TestAutofillClock test_clock; }; @@ -455,6 +458,27 @@ TEST_F(AutocompleteHistoryManagerTest, /*is_off_the_record=*/false); } +// Make sure suggestions are not returned if the field should not autocomplete. +TEST_F(AutocompleteHistoryManagerTest, + OnGetSingleFieldSuggestions_FieldShouldNotAutocomplete) { + test_field_.should_autocomplete = false; + + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + int test_query_id = 2; + + // Setting up mock to verify that call to the handler's OnSuggestionsReturned + // is not triggered. + EXPECT_CALL(*suggestions_handler, OnSuggestionsReturned).Times(0); + + EXPECT_CALL(*web_data_service_, GetFormValuesForElementName).Times(0); + + // Simulate request for suggestions. + EXPECT_FALSE(autocomplete_manager_->OnGetSingleFieldSuggestions( + test_query_id, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); +} + // Make sure our handler is called at the right time. TEST_F(AutocompleteHistoryManagerTest, SuggestionsReturned_InvokeHandler_Empty) { @@ -462,8 +486,6 @@ TEST_F(AutocompleteHistoryManagerTest, auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; std::vector<AutofillEntry> expected_values; @@ -471,15 +493,15 @@ TEST_F(AutocompleteHistoryManagerTest, GetMockedDbResults(expected_values); EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id)); // Simulate request for suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that DB response triggers a call to the handler's // OnSuggestionsReturned @@ -499,20 +521,20 @@ TEST_F(AutocompleteHistoryManagerTest, DoQuerySuggestionsForMeaninglessFieldNames_FilterSubStringName) { auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"payment_cvv_info"; - std::u16string test_prefix; + test::CreateTestFormField(/*label=*/"", "payment_cvv_info", /*value=*/"", + "Some Type", &test_field_); // Only expect a call when the name is not filtered out. EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .Times(0); // Simulate request for suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that DB response does not trigger a call to the // handler's OnSuggestionsReturned. @@ -528,20 +550,20 @@ TEST_F(AutocompleteHistoryManagerTest, DoQuerySuggestionsForMeaninglessFieldNames_FilterName) { auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"input_123"; - std::u16string test_prefix; + test::CreateTestFormField(/*label=*/"", "input_123", /*value=*/"", + "Some Type", &test_field_); // Only expect a call when the name is not filtered out. EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .Times(0); // Simulate request for suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that DB response does not trigger a call to the // handler's OnSuggestionsReturned. @@ -557,9 +579,9 @@ TEST_F(AutocompleteHistoryManagerTest, DoQuerySuggestionsForMeaninglessFieldNames_PassNameWithSubstring) { auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"foOTPace"; - std::u16string test_prefix; int mocked_db_query_id = 100; + test::CreateTestFormField(/*label=*/"", "foOTPace", /*value=*/"", "Some Type", + &test_field_); std::vector<AutofillEntry> expected_values; @@ -568,15 +590,15 @@ TEST_F(AutocompleteHistoryManagerTest, // Expect a call because the name is not filtered. EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id)); // Simulate request for suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that DB response triggers a call to the handler's EXPECT_CALL(*suggestions_handler.get(), @@ -592,9 +614,9 @@ TEST_F(AutocompleteHistoryManagerTest, DoQuerySuggestionsForMeaninglessFieldNames_PassName) { auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"addressline_1"; - std::u16string test_prefix; int mocked_db_query_id = 100; + test::CreateTestFormField(/*label=*/"", "addressline_1", /*value=*/"", + "Some Type", &test_field_); std::vector<AutofillEntry> expected_values; @@ -603,15 +625,15 @@ TEST_F(AutocompleteHistoryManagerTest, // Expect a call because the name is not filtered. EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id)); // Simulate request for suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that DB response triggers a call to the handler's EXPECT_CALL(*suggestions_handler.get(), @@ -628,25 +650,23 @@ TEST_F(AutocompleteHistoryManagerTest, auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; std::vector<AutofillEntry> expected_values = { - GetAutofillEntry(test_name, u"SomePrefixOne")}; + GetAutofillEntry(test_field_.name, u"SomePrefixOne")}; std::unique_ptr<WDTypedResult> mocked_results = GetMockedDbResults(expected_values); EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id)); // Simulate request for suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that DB response triggers a call to the handler's EXPECT_CALL(*suggestions_handler.get(), @@ -670,25 +690,23 @@ TEST_F(AutocompleteHistoryManagerTest, auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; std::vector<AutofillEntry> expected_values = { - GetAutofillEntry(test_name, u"SomePrefixOne")}; + GetAutofillEntry(test_field_.name, u"SomePrefixOne")}; std::unique_ptr<WDTypedResult> mocked_results = GetMockedDbResults(expected_values); EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id)); // Simulate request for suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/true, test_name, test_prefix, "Some Type", - suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/true, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that DB response triggers a call to the handler's EXPECT_CALL(*suggestions_handler.get(), @@ -712,25 +730,23 @@ TEST_F(AutocompleteHistoryManagerTest, auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; std::vector<AutofillEntry> expected_values = { - GetAutofillEntry(test_name, test_prefix)}; + GetAutofillEntry(test_field_.name, test_field_.value)}; std::unique_ptr<WDTypedResult> mocked_results = GetMockedDbResults(expected_values); EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id)); // Simulate request for suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that DB response triggers a call to the handler's EXPECT_CALL(*suggestions_handler.get(), @@ -751,25 +767,23 @@ TEST_F(AutocompleteHistoryManagerTest, auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; std::vector<AutofillEntry> expected_values = { - GetAutofillEntry(test_name, u"someprefix")}; + GetAutofillEntry(test_field_.name, u"someprefix")}; std::unique_ptr<WDTypedResult> mocked_results = GetMockedDbResults(expected_values); EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id)); // Simulate request for suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that DB response triggers a call to the handler's EXPECT_CALL(*suggestions_handler.get(), @@ -793,17 +807,15 @@ TEST_F(AutocompleteHistoryManagerTest, auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; std::u16string test_value = u"SomePrefixOne"; std::u16string other_test_value = u"SomePrefixOne"; int days_since_last_use = 10; std::vector<AutofillEntry> expected_values = { - GetAutofillEntry(test_name, test_value, + GetAutofillEntry(test_field_.name, test_value, AutofillClock::Now() - base::Days(30), AutofillClock::Now() - base::Days(days_since_last_use)), - GetAutofillEntry(test_name, other_test_value, + GetAutofillEntry(test_field_.name, other_test_value, AutofillClock::Now() - base::Days(30), AutofillClock::Now() - base::Days(days_since_last_use))}; @@ -811,17 +823,17 @@ TEST_F(AutocompleteHistoryManagerTest, GetMockedDbResults(expected_values); EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id)); EXPECT_CALL(*suggestions_handler.get(), OnSuggestionsReturned); // Simulate request for suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Simulate response from DB. autocomplete_manager_->OnWebDataServiceRequestDone(mocked_db_query_id, @@ -846,14 +858,12 @@ TEST_F(AutocompleteHistoryManagerTest, auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id_first = 2; int test_query_id_second = 3; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; std::vector<AutofillEntry> expected_values_first = { - GetAutofillEntry(test_name, u"SomePrefixOne")}; + GetAutofillEntry(test_field_.name, u"SomePrefixOne")}; std::vector<AutofillEntry> expected_values_second = { - GetAutofillEntry(test_name, u"SomePrefixTwo")}; + GetAutofillEntry(test_field_.name, u"SomePrefixTwo")}; std::unique_ptr<WDTypedResult> mocked_results_first = GetMockedDbResults(expected_values_first); @@ -862,25 +872,25 @@ TEST_F(AutocompleteHistoryManagerTest, GetMockedDbResults(expected_values_second); EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id_first)) .WillOnce(Return(mocked_db_query_id_second)); // Simulate request for the first suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id_first, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Simulate request for the second suggestions (this will cancel the first // one). EXPECT_CALL(*web_data_service_, CancelRequest(mocked_db_query_id_first)) .Times(1); - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id_second, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that we can get the second response first. EXPECT_CALL(*suggestions_handler.get(), @@ -916,14 +926,12 @@ TEST_F(AutocompleteHistoryManagerTest, auto suggestions_handler_second = std::make_unique<MockSuggestionsHandler>(); int test_query_id_first = 2; int test_query_id_second = 3; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; std::vector<AutofillEntry> expected_values_first = { - GetAutofillEntry(test_name, u"SomePrefixOne")}; + GetAutofillEntry(test_field_.name, u"SomePrefixOne")}; std::vector<AutofillEntry> expected_values_second = { - GetAutofillEntry(test_name, u"SomePrefixTwo")}; + GetAutofillEntry(test_field_.name, u"SomePrefixTwo")}; std::unique_ptr<WDTypedResult> mocked_results_first = GetMockedDbResults(expected_values_first); @@ -932,24 +940,22 @@ TEST_F(AutocompleteHistoryManagerTest, GetMockedDbResults(expected_values_second); EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id_first)) .WillOnce(Return(mocked_db_query_id_second)); // Simulate request for the first suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id_first, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler_first->GetWeakPtr(), - SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler_first->GetWeakPtr(), SuggestionsContext())); // Simulate request for the second suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id_second, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler_second->GetWeakPtr(), - SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler_second->GetWeakPtr(), SuggestionsContext())); // Setting up mock to verify that we get the second response first. EXPECT_CALL(*suggestions_handler_second.get(), @@ -980,16 +986,13 @@ TEST_F(AutocompleteHistoryManagerTest, TEST_F(AutocompleteHistoryManagerTest, SuggestionsReturned_CancelOne_ReturnOne) { - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; - // Initialize variables for the first handler, which is the one that will be // cancelled. auto suggestions_handler_one = std::make_unique<MockSuggestionsHandler>(); int mocked_db_query_id_one = 100; int test_query_id_one = 1; std::vector<AutofillEntry> expected_values_one = { - GetAutofillEntry(test_name, u"SomePrefixOne")}; + GetAutofillEntry(test_field_.name, u"SomePrefixOne")}; std::unique_ptr<WDTypedResult> mocked_results_one = GetMockedDbResults(expected_values_one); @@ -998,27 +1001,27 @@ TEST_F(AutocompleteHistoryManagerTest, int test_query_id_two = 2; int mocked_db_query_id_two = 101; std::vector<AutofillEntry> expected_values_two = { - GetAutofillEntry(test_name, u"SomePrefixTwo")}; + GetAutofillEntry(test_field_.name, u"SomePrefixTwo")}; std::unique_ptr<WDTypedResult> mocked_results_two = GetMockedDbResults(expected_values_two); // Simulate first handler request for autocomplete suggestions. EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id_one)) .WillOnce(Return(mocked_db_query_id_two)); - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id_one, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler_one->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler_one->GetWeakPtr(), SuggestionsContext())); // Simlate second handler request for autocomplete suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id_two, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler_two->GetWeakPtr(), SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler_two->GetWeakPtr(), SuggestionsContext())); // Simlate first handler cancelling its request. EXPECT_CALL(*web_data_service_, CancelRequest(mocked_db_query_id_one)) @@ -1063,11 +1066,10 @@ TEST_F(AutocompleteHistoryManagerTest, NoAutocompleteSuggestionsForTextarea) { base::HistogramTester histogram_tester; - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( 0, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, field.name, field.value, - field.form_control_type, suggestions_handler->GetWeakPtr(), - SuggestionsContext()); + /*autoselect_first_suggestion=*/false, field, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 0, 1); histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 1, 0); @@ -1093,11 +1095,10 @@ TEST_F(AutocompleteHistoryManagerTest, AutocompleteUMAQueryCreated) { EXPECT_CALL(*suggestions_handler.get(), OnSuggestionsReturned(0, /*autoselect_first_suggestion=*/false, testing::Truly(IsEmptySuggestionVector))); - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( 0, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, field.name, field.value, - field.form_control_type, suggestions_handler->GetWeakPtr(), - SuggestionsContext()); + /*autoselect_first_suggestion=*/false, field, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 1, 1); histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 0, 0); @@ -1124,11 +1125,10 @@ TEST_F(AutocompleteHistoryManagerTest, AutocompleteUMAQueryCreated) { EXPECT_CALL(*suggestions_handler.get(), OnSuggestionsReturned(0, /*autoselect_first_suggestion=*/false, testing::Truly(NonEmptySuggestionVector))); - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( 0, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, field.name, field.value, - field.form_control_type, suggestions_handler->GetWeakPtr(), - SuggestionsContext()); + /*autoselect_first_suggestion=*/false, field, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 1, 2); histogram_tester.ExpectBucketCount("Autofill.AutocompleteQuery", 0, 0); @@ -1151,28 +1151,24 @@ TEST_F(AutocompleteHistoryManagerTest, DestructorCancelsRequests) { auto suggestions_handler_second = std::make_unique<MockSuggestionsHandler>(); int test_query_id_first = 2; int test_query_id_second = 3; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; EXPECT_CALL(*web_data_service_, - GetFormValuesForElementName(test_name, test_prefix, _, - autocomplete_manager_.get())) + GetFormValuesForElementName(test_field_.name, test_field_.value, + _, autocomplete_manager_.get())) .WillOnce(Return(mocked_db_query_id_first)) .WillOnce(Return(mocked_db_query_id_second)); // Simulate request for the first suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id_first, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler_first->GetWeakPtr(), - SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler_first->GetWeakPtr(), SuggestionsContext())); // Simulate request for the second suggestions. - autocomplete_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(autocomplete_manager_->OnGetSingleFieldSuggestions( test_query_id_second, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, test_name, test_prefix, - "Some Type", suggestions_handler_second->GetWeakPtr(), - SuggestionsContext()); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler_second->GetWeakPtr(), SuggestionsContext())); // Expect cancel calls for both requests. EXPECT_CALL(*web_data_service_, CancelRequest(mocked_db_query_id_first)) diff --git a/chromium/components/autofill/core/browser/autofill_address_util.cc b/chromium/components/autofill/core/browser/autofill_address_util.cc index 3000d73edd8..72c04fe0d5f 100644 --- a/chromium/components/autofill/core/browser/autofill_address_util.cc +++ b/chromium/components/autofill/core/browser/autofill_address_util.cc @@ -23,7 +23,6 @@ #include "components/strings/grit/components_strings.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui_component.h" -#include "third_party/libaddressinput/src/cpp/include/libaddressinput/localization.h" #include "third_party/re2/src/re2/re2.h" #include "ui/base/l10n/l10n_util.h" @@ -35,39 +34,6 @@ namespace autofill { namespace { -// Extend `components` using Autofill's address format extensions. These are -// used to add support for fields that are not strictly required for a valid -// address, and thus not provided by libaddressinput, but still commonly appear -// in forms. -void ExtendAddressComponents(std::vector<AddressUiComponent>& components, - const std::string& country_code, - const Localization& localization) { - AutofillCountry country(country_code); - for (const AutofillCountry::AddressFormatExtension& rule : - country.address_format_extensions()) { - // Find the location of `rule.placed_after` in `components`. - // `components.field` is only valid if `components.literal.empty()`. - auto prev_component = base::ranges::find_if( - components, [&rule](const AddressUiComponent& component) { - return component.literal.empty() && - component.field == rule.placed_after; - }); - DCHECK(prev_component != components.end()); - - // Insert the separator and `rule.type` afterwards. - components.insert( - ++prev_component, - {AddressUiComponent{.literal = - std::string(rule.separator_before_label)}, - AddressUiComponent{ - .field = rule.type, - .name = localization.GetString(rule.label_id), - .length_hint = rule.large_sized - ? AddressUiComponent::HINT_LONG - : AddressUiComponent::HINT_SHORT}}); - } -} - // Returns a vector of AddressUiComponent for `country_code` when using // `ui_language_code`. If no components are available for `country_code`, it // defaults back to the US. If `ui_language_code` is not valid, the default @@ -86,7 +52,8 @@ std::vector<AddressUiComponent> GetAddressComponents( ::i18n::addressinput::BuildComponentsWithLiterals( country, localization, ui_language_code, components_language_code); if (!components.empty()) { - ExtendAddressComponents(components, country, localization); + ExtendAddressComponents(components, country, localization, + /*include_literals=*/true); return components; } } @@ -96,6 +63,39 @@ std::vector<AddressUiComponent> GetAddressComponents( } // namespace +void ExtendAddressComponents(std::vector<AddressUiComponent>& components, + const std::string& country_code, + const Localization& localization, + bool include_literals) { + AutofillCountry country(country_code); + for (const AutofillCountry::AddressFormatExtension& rule : + country.address_format_extensions()) { + // Find the location of `rule.placed_after` in `components`. + // `components.field` is only valid if `components.literal.empty()`. + auto prev_component = base::ranges::find_if( + components, [&rule](const AddressUiComponent& component) { + return component.literal.empty() && + component.field == rule.placed_after; + }); + DCHECK(prev_component != components.end()); + + // Insert the separator and `rule.type` after `prev_component`. + if (include_literals) { + prev_component = components.insert( + ++prev_component, + AddressUiComponent{.literal = + std::string(rule.separator_before_label)}); + } + components.insert( + ++prev_component, + AddressUiComponent{ + .field = rule.type, + .name = localization.GetString(rule.label_id), + .length_hint = rule.large_sized ? AddressUiComponent::HINT_LONG + : AddressUiComponent::HINT_SHORT}); + } +} + void GetAddressComponents( const std::string& country_code, const std::string& ui_language_code, diff --git a/chromium/components/autofill/core/browser/autofill_address_util.h b/chromium/components/autofill/core/browser/autofill_address_util.h index 31cdd6fe172..f04b8a510a7 100644 --- a/chromium/components/autofill/core/browser/autofill_address_util.h +++ b/chromium/components/autofill/core/browser/autofill_address_util.h @@ -10,12 +10,22 @@ #include "components/autofill/core/browser/data_model/autofill_profile_comparator.h" #include "components/autofill/core/browser/field_types.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui_component.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/localization.h" namespace autofill { class AutofillProfile; class PersonalDataManager; +// Extend `components` using Autofill's address format extensions. These are +// used make fields beyond libaddressinput's format available in Autofill's +// settings UI and import dialogs. +void ExtendAddressComponents( + std::vector<::i18n::addressinput::AddressUiComponent>& components, + const std::string& country_code, + const ::i18n::addressinput::Localization& localization, + bool include_literals); + // |address_components| is a 2D array for the address components in each line. // Fills |address_components| with the address UI components that should be used // to input an address for |country_code| when UI BCP 47 language code is diff --git a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/OWNERS b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/OWNERS new file mode 100644 index 00000000000..27291aac8ca --- /dev/null +++ b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/OWNERS @@ -0,0 +1,5 @@ +# For most changes: +file://components/autofill/OWNERS + +# For trivial or mechanical horizontal JS/CSS/HTML changes. +file://ui/webui/PLATFORM_OWNERS 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 f3e66bf71c5..5db94c45067 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 @@ -29,11 +29,13 @@ html { #control-panel { display: block; font-size: 120%; + line-height: calc(120% + 2ex); padding: 1ex; } #control-panel label { padding-inline-end: 1em; + white-space: nowrap; } .fake-button { @@ -274,7 +276,6 @@ html { position: absolute; right: 20px; } - </style> </head> <body> @@ -282,10 +283,13 @@ html { <h1 id="h1-title"></h1> <div id="control-panel"> <span id="marker-fake-button" class="fake-button">Add Marker</span> - <input type="checkbox" id="enable-autoscroll" checked><label for="enable-autoscroll">Enable autoscroll</label> + <label><input type="checkbox" id="enable-autoscroll" checked> Enable autoscroll</label> <span id="checkbox-placeholder"></span> <span id="reset-cache-fake-button" class="fake-button" style="display: none">Reset Cache</span> <span id="download-fake-button" class="fake-button">Download Log</span> + <label><input type="checkbox" id="currently-recording" checked> Record new events</label> + <label><input type="checkbox" id="automatically-stop-recording" checked> Automatically stop recording in <span id="stop-recording-time">M:SS</span></label> + <span id="reset-upm-eviction-fake-button" class="fake-button" style="display: none">Reset UPM eviction</span> </div> </div> <div id="logging-note"></div> diff --git a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js index 91503420ffe..d2e0451e4ee 100644 --- a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js +++ b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.js @@ -9,6 +9,14 @@ import 'chrome://resources/js/ios/web_ui.js'; import {addWebUIListener} from 'chrome://resources/js/cr.m.js'; import {$} from 'chrome://resources/js/util.m.js'; +// By default this page only records metrics for a given period of time in order +// to not waste too much memory. This constant defines the default period until +// recording ceases. +const kDefaultLoggingPeriodInSeconds = 300; + +// Indicates whether logs should be recorded at the moment. +let recordLogs = true; + // Renders a simple dialog with |text| as a message and a close button. function showModalDialog(text) { const dialog = document.createElement('div'); @@ -120,7 +128,10 @@ function nodeToDomNode(node) { return domNode; } -function addStructuredLog(node) { +function addStructuredLog(node, ignoreRecordLogs = false) { + if (!recordLogs && !ignoreRecordLogs) { + return; + } const logDiv = $('log-entries'); if (!logDiv) { return; @@ -141,6 +152,88 @@ function addStructuredLog(node) { } } +// Sets up a couple of event handlers and interval handlers for automatically +// stopping the recording of autofill events. We stop the recording because +// you may forget an internals page in some tab and don't want it to keep +// growing it's memory consumption forever. +// We have two checkboxes +// [x] Record new events +// [x] Automatically stop recording in 0:30 +// with the following behavior: +// - While the first checkbox is checked, log entries are recorded. +// - While both checkboxes are checked, the countdown decreases. +// - If the countdown reaches 0:00, the first checkbox gets unchecked. +// - If any checkbox is toggled, we reset the countdown time to +// kDefaultLoggingPeriodInSeconds. +function setUpStopRecording() { + // Timestamp (in ms after epoch), when the countdown to stop recording should + // happen. + let stopRecordingLogsAt = undefined; + // Interval ID generated by setInterval, which is called every second to + // update the remaining time. + let countdown = undefined; + + const currentlyRecordingChkBox = + document.getElementById('currently-recording'); + const autoStopRecordingChkBox = + document.getElementById('automatically-stop-recording'); + + // Formats a number of seconds into a [M]M:SS format. + const secondsToString = (seconds) => { + const minutes = Math.floor(seconds / 60); + seconds = seconds % 60; + return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; + }; + + // Updates the time label and reacts to the countdown reaching 0. + const countdownHandler = () => { + const remainingSeconds = Math.round( + Math.max((stopRecordingLogsAt - new Date().getTime()) / 1000, 0)); + document.getElementById('stop-recording-time').innerText = + secondsToString(remainingSeconds); + + if (remainingSeconds == 0) { + recordLogs = false; + currentlyRecordingChkBox.checked = false; + resetTimeout(); + } + }; + const startCountDown = () => { + if (!countdown) { + countdown = window.setInterval(countdownHandler, 1000); + } + }; + const stopCountDown = () => { + if (countdown) { + window.clearInterval(countdown); + countdown = undefined; + } + }; + const startOrStopCountDown = () => { + if (currentlyRecordingChkBox.checked && autoStopRecordingChkBox.checked) { + startCountDown(); + } else { + stopCountDown(); + } + }; + const resetTimeout = () => { + stopRecordingLogsAt = + new Date().getTime() + kDefaultLoggingPeriodInSeconds * 1000; + countdownHandler(); // Update the string shown to the user. + startOrStopCountDown(); + }; + + currentlyRecordingChkBox.addEventListener('click', () => { + recordLogs = currentlyRecordingChkBox.checked; + resetTimeout(); + }); + autoStopRecordingChkBox.addEventListener('click', () => { + resetTimeout(); + }); + + resetTimeout(); +} + function setUpAutofillInternals() { document.title = 'Autofill Internals'; document.getElementById('h1-title').textContent = 'Autofill Internals'; @@ -152,6 +245,7 @@ function setUpAutofillInternals() { setUpLogDisplayConfig(); setUpMarker(); setUpDownload('autofill'); + setUpStopRecording(); } function setUpPasswordManagerInternals() { @@ -165,11 +259,18 @@ function setUpPasswordManagerInternals() { 'Captured password manager logs are not available in Incognito.'; setUpMarker(); setUpDownload('password-manager'); + setUpStopRecording(); + addWebUIListener( + 'enable-reset-upm-eviction-button', enableResetUpmEvictionButton); } function enableResetCacheButton() { - document.getElementById('reset-cache-fake-button').style.display = - 'inline-block'; + document.getElementById('reset-cache-fake-button').style.display = 'inline'; +} + +function enableResetUpmEvictionButton(isEnabled) { + document.getElementById('reset-upm-eviction-fake-button').style.display = + isEnabled ? 'inline' : 'none'; } function notifyAboutIncognito(isIncognito) { @@ -195,12 +296,14 @@ function setUpMarker() { markerFakeButton.addEventListener('click', () => { ++markerCounter; const scrollAfterInsert = needsScrollDown(); - addStructuredLog({ - type: 'element', - value: 'div', - attributes: {'class': 'marker', 'contenteditable': 'true'}, - children: [{type: 'text', value: `#${markerCounter} `}] - }); + addStructuredLog( + { + type: 'element', + value: 'div', + attributes: {'class': 'marker', 'contenteditable': 'true'}, + children: [{type: 'text', value: `#${markerCounter} `}], + }, + /*ignoreRecordLogs=*/ true); if (scrollAfterInsert) { scrollDown(); // Focus marker div, set caret at end of line. @@ -294,9 +397,8 @@ function setUpLogDisplayConfig() { input.addEventListener('change', changeHandler); changeHandler(); // Call once to initialize |logDiv|'s classes. const label = document.createElement('label'); - label.setAttribute('for', `checkbox-${scope}`); - label.innerText = scope; - checkboxPlaceholder.appendChild(input); + label.appendChild(input); + label.appendChild(document.createTextNode(' ' + scope)); checkboxPlaceholder.appendChild(label); } } @@ -318,4 +420,11 @@ document.addEventListener('DOMContentLoaded', function(event) { resetCacheFakeButton.addEventListener('click', () => { chrome.send('resetCache'); }); + + const resetUpmEvictionButton = + document.getElementById('reset-upm-eviction-fake-button'); + resetUpmEvictionButton.addEventListener('click', () => { + chrome.send('resetUpmEviction'); + showModalDialog('UPM re-enabled. Please, restart Chrome.'); + }); }); diff --git a/chromium/components/autofill/core/browser/autofill_client.cc b/chromium/components/autofill/core/browser/autofill_client.cc index 1bb339fa7f1..76e714fd475 100644 --- a/chromium/components/autofill/core/browser/autofill_client.cc +++ b/chromium/components/autofill/core/browser/autofill_client.cc @@ -51,6 +51,14 @@ AutofillClient::GetSingleFieldFormFillRouter() { GetAutocompleteHistoryManager(), GetMerchantPromoCodeManager()); } +CreditCardCVCAuthenticator* AutofillClient::GetCVCAuthenticator() { + return nullptr; +} + +CreditCardOtpAuthenticator* AutofillClient::GetOtpAuthenticator() { + return nullptr; +} + AutofillOfferManager* AutofillClient::GetAutofillOfferManager() { return nullptr; } @@ -104,8 +112,7 @@ void AutofillClient::HideVirtualCardEnrollBubbleAndIconIfVisible() { #if !BUILDFLAG(IS_IOS) std::unique_ptr<webauthn::InternalAuthenticator> -AutofillClient::CreateCreditCardInternalAuthenticator( - content::RenderFrameHost* rfh) { +AutofillClient::CreateCreditCardInternalAuthenticator(AutofillDriver* driver) { return nullptr; } #endif @@ -149,6 +156,7 @@ void AutofillClient::ShowVirtualCardErrorDialog(bool is_permanent_error) { } void AutofillClient::ShowAutofillProgressDialog( + AutofillProgressDialogType autofill_progress_dialog_type, base::OnceClosure cancel_callback) { // This is overridden by platform subclasses. Currently only // ChromeAutofillClient (Chrome Desktop & Android) implements this. diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h index d7994f5f4a4..5dee1a18199 100644 --- a/chromium/components/autofill/core/browser/autofill_client.h +++ b/chromium/components/autofill/core/browser/autofill_client.h @@ -20,6 +20,7 @@ #include "components/autofill/core/browser/payments/risk_data_loader.h" #include "components/autofill/core/browser/ui/popup_item_ids.h" #include "components/autofill/core/browser/ui/popup_types.h" +#include "components/autofill/core/browser/ui/touch_to_fill_delegate.h" #include "components/profile_metrics/browser_profile_type.h" #include "components/security_state/core/security_state.h" #include "components/translate/core/browser/language_state.h" @@ -35,10 +36,6 @@ class PrefService; -namespace content { -class RenderFrameHost; -} - namespace signin { class IdentityManager; } @@ -65,10 +62,13 @@ class AutofillProfile; class AutocompleteHistoryManager; class AutofillOfferManager; class AutofillPopupDelegate; +enum class AutofillProgressDialogType; struct CardUnmaskChallengeOption; class CardUnmaskDelegate; class CreditCard; +class CreditCardCVCAuthenticator; enum class CreditCardFetchResult; +class CreditCardOtpAuthenticator; class FormDataImporter; class FormStructure; class LogManager; @@ -325,6 +325,10 @@ class AutofillClient : public RiskDataLoader { // client (can be null for unsupported platforms). virtual MerchantPromoCodeManager* GetMerchantPromoCodeManager(); + // Can be null on unsupported platforms. + virtual CreditCardCVCAuthenticator* GetCVCAuthenticator(); + virtual CreditCardOtpAuthenticator* GetOtpAuthenticator(); + // Creates and returns a SingleFieldFormFillRouter using the // AutocompleteHistoryManager instance associated with the client. std::unique_ptr<SingleFieldFormFillRouter> GetSingleFieldFormFillRouter(); @@ -388,7 +392,7 @@ class AutofillClient : public RiskDataLoader { // null for platforms that don't support this, in which case standard CVC // authentication will be used instead. virtual std::unique_ptr<webauthn::InternalAuthenticator> - CreateCreditCardInternalAuthenticator(content::RenderFrameHost* rfh); + CreateCreditCardInternalAuthenticator(AutofillDriver* driver); #endif // Causes the Autofill settings UI to be shown. If |show_credit_card_settings| @@ -553,6 +557,8 @@ class AutofillClient : public RiskDataLoader { // Called after credit card upload is finished. Will show upload result to // users. |card_saved| indicates if the card is successfully saved. + // TODO(crbug.com/932818): This function is overridden in iOS codebase. + // Ideally should remove it if iOS is not using it to do anything. virtual void CreditCardUploadCompleted(bool card_saved) = 0; // Will show an infobar to get user consent for Credit Card assistive filling. @@ -581,6 +587,23 @@ class AutofillClient : public RiskDataLoader { // HasCreditCardScanFeature() returns true. virtual void ScanCreditCard(CreditCardScanCallback callback) = 0; + // Returns true if the Touch To Fill feature is both supported by platform and + // enabled. Should be called before |ShowTouchToFillCreditCard| or + // |HideTouchToFillCreditCard|. + virtual bool IsTouchToFillCreditCardSupported() = 0; + + // Shows the Touch To Fill surface for filling credit card information, if + // possible, and returns |true| on success. |delegate| will be notified of + // events. Should be called only if |IsTouchToFillCreditCardSupported| + // returns true. + virtual bool ShowTouchToFillCreditCard( + base::WeakPtr<TouchToFillDelegate> delegate) = 0; + + // Hides the Touch To Fill surface for filling credit card information + // if one is currently shown. Should be called only if + // |IsTouchToFillCreditCardSupported| returns true. + virtual void HideTouchToFillCreditCard() = 0; + // Shows an Autofill popup with the given |values|, |labels|, |icons|, and // |identifiers| for the element at |element_bounds|. |delegate| will be // notified of popup events. @@ -643,7 +666,9 @@ class AutofillClient : public RiskDataLoader { // Show/dismiss the progress dialog which contains a throbber and a text // message indicating that something is in progress. - virtual void ShowAutofillProgressDialog(base::OnceClosure cancel_callback); + virtual void ShowAutofillProgressDialog( + AutofillProgressDialogType autofill_progress_dialog_type, + base::OnceClosure cancel_callback); virtual void CloseAutofillProgressDialog( bool show_confirmation_before_closing); diff --git a/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc b/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc index f8f2f399c8d..c4942eeed27 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc @@ -40,16 +40,6 @@ TEST(AutofillDataUtilTest, DetermineGroupsForHomeNameAndAddress) { EXPECT_EQ(expected_group_bitmask, group_bitmask); } -TEST(AutofillDataUtilTest, DetermineGroupsForBillingNameAndAddress) { - const std::vector<ServerFieldType> field_types{ - NAME_BILLING_FULL, ADDRESS_BILLING_LINE1, ADDRESS_BILLING_CITY, - ADDRESS_BILLING_STATE, ADDRESS_BILLING_ZIP}; - - const uint32_t expected_group_bitmask = kName | kAddress; - const uint32_t group_bitmask = data_util::DetermineGroups(field_types); - EXPECT_EQ(expected_group_bitmask, group_bitmask); -} - TEST(AutofillDataUtilTest, DetermineGroupsForHomeNamePhoneAndEmail) { const std::vector<ServerFieldType> field_types{ NAME_FULL, PHONE_HOME_CITY_AND_NUMBER, EMAIL_ADDRESS}; @@ -59,15 +49,6 @@ TEST(AutofillDataUtilTest, DetermineGroupsForHomeNamePhoneAndEmail) { EXPECT_EQ(expected_group_bitmask, group_bitmask); } -TEST(AutofillDataUtilTest, DetermineGroupsForBillingNamePhoneAndEmail) { - const std::vector<ServerFieldType> field_types{ - NAME_BILLING_FULL, PHONE_BILLING_WHOLE_NUMBER, EMAIL_ADDRESS}; - - const uint32_t expected_group_bitmask = kName | kPhone | kEmail; - const uint32_t group_bitmask = data_util::DetermineGroups(field_types); - EXPECT_EQ(expected_group_bitmask, group_bitmask); -} - TEST(AutofillDataUtilTest, DetermineGroupsForUnknownServerFieldType) { const std::vector<ServerFieldType> field_types{UNKNOWN_TYPE, NAME_FULL, ADDRESS_HOME_ZIP}; diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.cc b/chromium/components/autofill/core/browser/autofill_download_manager.cc index 611d90111bb..1d5504c01fa 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager.cc @@ -406,7 +406,7 @@ LogBuffer& operator<<(LogBuffer& out, const AutofillUploadContents& upload) { out << Tr{} << "has_form_tag:" << upload.has_form_tag(); if (upload.has_single_username_data()) { - LogBuffer single_username_data; + LogBuffer single_username_data(LogBuffer::IsActive(true)); single_username_data << Tag{"span"} << "["; single_username_data << Tr{} << "username_form_signature:" @@ -476,10 +476,9 @@ bool CanThrottleUpload(const FormStructure& form, std::string key = base::StringPrintf( "%03X", static_cast<int>(form.form_signature().value() % kNumUploadBuckets)); - auto* upload_events = - pref_service->GetDictionary(prefs::kAutofillUploadEvents); - auto* found = upload_events->FindKeyOfType(key, base::Value::Type::INTEGER); - int value = found ? found->GetInt() : 0; + const auto& upload_events = + pref_service->GetValueDict(prefs::kAutofillUploadEvents); + int value = upload_events.FindInt(key).value_or(0); // Calculate the mask we expect to be set for the form's upload bucket. const int bit = static_cast<int>(form.submission_source()); @@ -736,12 +735,10 @@ bool AutofillDownloadManager::StartUploadRequest( request_data.payload = std::move(payload); DVLOG(1) << "Sending Autofill Upload Request:\n" << upload; - if (log_manager_) { - log_manager_->Log() << LoggingScope::kAutofillServer - << LogMessage::kSendAutofillUpload << Br{} - << "Allow upload?: " << allow_upload << Br{} - << "Data: " << Br{} << upload; - } + LOG_AF(log_manager_) << LoggingScope::kAutofillServer + << LogMessage::kSendAutofillUpload << Br{} + << "Allow upload?: " << allow_upload << Br{} + << "Data: " << Br{} << upload; if (!allow_upload) return false; 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 9f53d43dc35..2a2549fd8f3 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc @@ -345,8 +345,8 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) { form_structures.push_back(std::make_unique<FormStructure>(form)); for (auto& form_structure : form_structures) { - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); } // Make download manager. @@ -726,8 +726,8 @@ TEST_F(AutofillDownloadManagerTest, UploadToAPITest) { FormStructure form_structure(form); form_structure.set_submission_source(SubmissionSource::FORM_SUBMISSION); - for (auto& field : form_structure) - field->host_form_signature = form_structure.form_signature(); + for (auto& fs_field : form_structure) + fs_field->host_form_signature = form_structure.form_signature(); std::unique_ptr<PrefService> pref_service = test::PrefServiceForTesting(); AutofillDownloadManager download_manager( @@ -805,8 +805,8 @@ TEST_F(AutofillDownloadManagerTest, UploadWithRawMetadata) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.set_submission_source(SubmissionSource::FORM_SUBMISSION); - for (auto& field : form_structure) - field->host_form_signature = form_structure.form_signature(); + for (auto& fs_field : form_structure) + fs_field->host_form_signature = form_structure.form_signature(); std::unique_ptr<PrefService> pref_service = test::PrefServiceForTesting(); AutofillDownloadManager download_manager( @@ -881,8 +881,8 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Query) { std::vector<std::unique_ptr<FormStructure>> form_structures; form_structures.push_back(std::make_unique<FormStructure>(form)); for (auto& form_structure : form_structures) { - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); } // Request with id 0. @@ -954,8 +954,8 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Upload) { auto form_structure = std::make_unique<FormStructure>(form); form_structure->set_submission_source(SubmissionSource::FORM_SUBMISSION); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); // Request with id 0. EXPECT_TRUE(download_manager_.StartUploadRequest( @@ -1123,8 +1123,8 @@ TEST_F(AutofillDownloadManagerTest, RetryLimit_Upload) { auto form_structure = std::make_unique<FormStructure>(form); form_structure->set_submission_source(SubmissionSource::FORM_SUBMISSION); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); // Request with id 0. EXPECT_TRUE(download_manager_.StartUploadRequest( @@ -1956,8 +1956,8 @@ TEST_P(AutofillUploadTest, RichMetadata) { AutofillDownloadManager download_manager(driver_.get(), this); FormStructure form_structure(form); form_structure.set_current_page_language(LanguageCode("fr")); - for (auto& field : form_structure) - field->host_form_signature = form_structure.form_signature(); + for (auto& fs_field : form_structure) + fs_field->host_form_signature = form_structure.form_signature(); pref_service_->SetBoolean( RandomizedEncoder::kUrlKeyedAnonymizedDataCollectionEnabled, true); @@ -2096,10 +2096,10 @@ TEST_P(AutofillUploadTest, ThrottlingDisabled) { AutofillDownloadManager download_manager(driver_.get(), this); FormStructure form_structure(form); FormStructure small_form_structure(small_form); - for (auto& field : form_structure) - field->host_form_signature = form_structure.form_signature(); - for (auto& field : small_form_structure) - field->host_form_signature = small_form_structure.form_signature(); + for (auto& fs_field : form_structure) + fs_field->host_form_signature = form_structure.form_signature(); + for (auto& fs_field : small_form_structure) + fs_field->host_form_signature = small_form_structure.form_signature(); for (int i = 0; i <= static_cast<int>(SubmissionSource::kMaxValue); ++i) { base::HistogramTester histogram_tester; diff --git a/chromium/components/autofill/core/browser/autofill_driver.h b/chromium/components/autofill/core/browser/autofill_driver.h index 59815c14e1a..7d4b9b62fdd 100644 --- a/chromium/components/autofill/core/browser/autofill_driver.h +++ b/chromium/components/autofill/core/browser/autofill_driver.h @@ -17,10 +17,6 @@ #include "ui/accessibility/ax_tree_id.h" #include "url/origin.h" -#if !BUILDFLAG(IS_IOS) -#include "components/webauthn/core/browser/internal_authenticator.h" -#endif - namespace network { class SharedURLLoaderFactory; } @@ -63,8 +59,13 @@ class AutofillDriver { // Returns whether the user is currently operating in an incognito context. virtual bool IsIncognito() const = 0; - // Returns whether AutofillDriver instance is associated with a main frame, - // and this can be a primary or non-primary main frame. + // Returns whether the AutofillDriver instance is associated with an active + // frame in the MPArch sense. + virtual bool IsInActiveFrame() const = 0; + + // Returns whether the AutofillDriver instance is associated with a main + // frame, in the MPArch sense. This can be a primary or non-primary main + // frame. virtual bool IsInAnyMainFrame() const = 0; // Returns whether the AutofillDriver instance is associated with a @@ -85,12 +86,6 @@ class AutofillDriver { // Returns true iff the renderer is available for communication. virtual bool RendererIsAvailable() = 0; -#if !BUILDFLAG(IS_IOS) - // Gets or creates a pointer to an implementation of InternalAuthenticator. - virtual webauthn::InternalAuthenticator* - GetOrCreateCreditCardInternalAuthenticator() = 0; -#endif - // Forwards |data| to the renderer which shall preview or fill the values of // |data|'s fields into the relevant DOM elements. // @@ -117,7 +112,7 @@ class AutofillDriver { const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map) = 0; // Forwards parsed |forms| to the embedder. - virtual void HandleParsedForms(const std::vector<const FormData*>& forms) = 0; + virtual void HandleParsedForms(const std::vector<FormData>& forms) = 0; // Sends the field type predictions specified in |forms| to the renderer. This // method is a no-op if the renderer is not available or the appropriate diff --git a/chromium/components/autofill/core/browser/autofill_experiments.cc b/chromium/components/autofill/core/browser/autofill_experiments.cc index 710f91c976a..dc2274c5166 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments.cc +++ b/chromium/components/autofill/core/browser/autofill_experiments.cc @@ -43,19 +43,15 @@ namespace autofill { namespace { -void LogCardUploadDisabled(LogManager* log_manager, std::string context) { - if (log_manager) { - log_manager->Log() << LoggingScope::kCreditCardUploadStatus - << LogMessage::kCreditCardUploadDisabled << context - << CTag{}; - } +void LogCardUploadDisabled(LogManager* log_manager, base::StringPiece context) { + LOG_AF(log_manager) << LoggingScope::kCreditCardUploadStatus + << LogMessage::kCreditCardUploadDisabled << context + << CTag{}; } void LogCardUploadEnabled(LogManager* log_manager) { - if (log_manager) { - log_manager->Log() << LoggingScope::kCreditCardUploadStatus - << LogMessage::kCreditCardUploadEnabled << CTag{}; - } + LOG_AF(log_manager) << LoggingScope::kCreditCardUploadStatus + << LogMessage::kCreditCardUploadEnabled << CTag{}; } // Given an email account domain, returns the contents before the first dot. diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc index 1423b9902fe..d86f0181645 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc @@ -368,8 +368,8 @@ base::WeakPtr<AutofillExternalDelegate> AutofillExternalDelegate::GetWeakPtr() { } void AutofillExternalDelegate::OnCreditCardScanned(const CreditCard& card) { - manager_->FillCreditCardForm(query_id_, query_form_, query_field_, card, - std::u16string()); + manager_->FillCreditCardFormImpl(query_form_, query_field_, card, + std::u16string(), query_id_); } void AutofillExternalDelegate::FillAutofillFormData(int unique_id, diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.h b/chromium/components/autofill/core/browser/autofill_external_delegate.h index c6af1bb013f..40dce87f18a 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate.h +++ b/chromium/components/autofill/core/browser/autofill_external_delegate.h @@ -40,7 +40,7 @@ class AutofillExternalDelegate : public AutofillPopupDelegate { AutofillExternalDelegate(const AutofillExternalDelegate&) = delete; AutofillExternalDelegate& operator=(const AutofillExternalDelegate&) = delete; - virtual ~AutofillExternalDelegate(); + ~AutofillExternalDelegate() override; // AutofillPopupDelegate implementation. void OnPopupShown() override; @@ -117,7 +117,7 @@ class AutofillExternalDelegate : public AutofillPopupDelegate { private: FRIEND_TEST_ALL_PREFIXES(AutofillExternalDelegateUnitTest, - FillCreditCardForm); + FillCreditCardFormImpl); // Called when a credit card is scanned using device camera. void OnCreditCardScanned(const CreditCard& card); 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 3a3a5a102a8..45cdd7a3307 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc @@ -30,6 +30,7 @@ #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/password_form_fill_data.h" +#include "components/autofill_assistant/core/public/autofill_assistant_intent.h" #include "components/strings/grit/components_strings.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -157,12 +158,12 @@ class MockBrowserAutofillManager : public BrowserAutofillManager { int unique_id), (override)); MOCK_METHOD(void, - FillCreditCardForm, - (int query_id, - const FormData& form, + FillCreditCardFormImpl, + (const FormData& form, const FormFieldData& field, const CreditCard& credit_card, - const std::u16string& cvc), + const std::u16string& cvc, + int query_id), (override)); private: @@ -213,7 +214,7 @@ class AutofillExternalDelegateUnitTest : public testing::Test { query_id, suggestions, /*autoselect_first_suggestion=*/false); } - base::test::SingleThreadTaskEnvironment task_environment_; + base::test::TaskEnvironment task_environment_; NiceMock<MockAutofillClient> autofill_client_; std::unique_ptr<NiceMock<MockAutofillDriver>> autofill_driver_; @@ -842,12 +843,12 @@ MATCHER_P(CreditCardMatches, card, "") { // Test that autofill manager will fill the credit card form after user scans a // credit card. -TEST_F(AutofillExternalDelegateUnitTest, FillCreditCardForm) { +TEST_F(AutofillExternalDelegateUnitTest, FillCreditCardFormImpl) { CreditCard card; test::SetCreditCardInfo(&card, "Alice", "4111", "1", "3000", "1"); - EXPECT_CALL( - *browser_autofill_manager_, - FillCreditCardForm(_, _, _, CreditCardMatches(card), std::u16string())); + EXPECT_CALL(*browser_autofill_manager_, + FillCreditCardFormImpl(_, _, CreditCardMatches(card), + std::u16string(), _)); external_delegate_->OnCreditCardScanned(card); } diff --git a/chromium/components/autofill/core/browser/autofill_field.cc b/chromium/components/autofill/core/browser/autofill_field.cc index d53d042400a..67f1f694864 100644 --- a/chromium/components/autofill/core/browser/autofill_field.cc +++ b/chromium/components/autofill/core/browser/autofill_field.cc @@ -111,13 +111,6 @@ void AutofillField::SetHtmlType(HtmlFieldType type, HtmlFieldMode mode) { html_type_ = type; html_mode_ = mode; overall_type_ = AutofillType(NO_SERVER_DATA); - - if (type == HTML_TYPE_TEL_LOCAL_PREFIX) - phone_part_ = PHONE_PREFIX; - else if (type == HTML_TYPE_TEL_LOCAL_SUFFIX) - phone_part_ = PHONE_SUFFIX; - else - phone_part_ = IGNORED; } void AutofillField::SetTypeTo(const AutofillType& type) { @@ -211,8 +204,14 @@ AutofillType AutofillField::ComputedType() const { (heuristic_type() == ADDRESS_HOME_STREET_NAME || heuristic_type() == ADDRESS_HOME_HOUSE_NUMBER)); + // For merchant promo code fields the heuristic predictions get precedence + // over the server predictions. believe_server = - believe_server && !(heuristic_type() == MERCHANT_PROMO_CODE); + believe_server && (heuristic_type() != MERCHANT_PROMO_CODE); + + // For international bank account number (IBAN) fields the heuristic + // predictions get precedence over the server predictions. + believe_server = believe_server && (heuristic_type() != IBAN_VALUE); if (believe_server) return AutofillType(server_type()); @@ -251,9 +250,6 @@ std::string AutofillField::FieldSignatureAsStr() const { } bool AutofillField::IsFieldFillable() const { - if (!base::FeatureList::IsEnabled(features::kAutofillFixFillableFieldTypes)) - return !Type().IsUnknown(); - ServerFieldType field_type = Type().GetStorableType(); return IsFillableFieldType(field_type); } diff --git a/chromium/components/autofill/core/browser/autofill_field.h b/chromium/components/autofill/core/browser/autofill_field.h index 920b0959882..15ee399ca60 100644 --- a/chromium/components/autofill/core/browser/autofill_field.h +++ b/chromium/components/autofill/core/browser/autofill_field.h @@ -32,12 +32,6 @@ typedef std::map<ServerFieldType, AutofillDataModel::ValidityState> class AutofillField : public FormFieldData { public: - enum PhonePart { - IGNORED = 0, - PHONE_PREFIX = 1, - PHONE_SUFFIX = 2, - }; - AutofillField(); explicit AutofillField(const FormFieldData& field); @@ -70,7 +64,6 @@ class AutofillField : public FormFieldData { const ServerFieldTypeValidityStatesMap& possible_types_validities() const { return possible_types_validities_; } - PhonePart phone_part() const { return phone_part_; } bool previously_autofilled() const { return previously_autofilled_; } const std::u16string& parseable_name() const { return parseable_name_; } const std::u16string& parseable_label() const { return parseable_label_; } @@ -128,9 +121,9 @@ class AutofillField : public FormFieldData { // (i.e. overall_type_ != NO_SERVER_DATA ? overall_type_ : ComputedType()) AutofillType Type() const; - // This function automatically chooses between server and heuristic autofill - // type, depending on the data available for this field alone. - // This type does not take into account the rationalization involving the + // This function automatically chooses among the Autofill server, heuristic + // and html type, depending on the data available for this field alone. This + // type does not take into account the rationalization involving the // surrounding fields. AutofillType ComputedType() const; @@ -275,9 +268,6 @@ class AutofillField : public FormFieldData { // The set of possible types and their validity for this field. ServerFieldTypeValidityStatesMap possible_types_validities_; - // Used to track whether this field is a phone prefix or suffix. - PhonePart phone_part_ = IGNORED; - // A low-entropy hash of the field's initial value before user-interactions or // automatic fillings. This field is used to detect static placeholders. absl::optional<uint32_t> initial_value_hash_; 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 3acab8e9ef3..92f5273ca2a 100644 --- a/chromium/components/autofill/core/browser/autofill_form_test_utils.cc +++ b/chromium/components/autofill/core/browser/autofill_form_test_utils.cc @@ -115,6 +115,7 @@ FormData GetFormData(const FormDataDescription& d) { ff.is_autofilled = dd.is_autofilled.value_or(false); ff.origin = dd.origin.value_or(f.main_frame_origin); ff.should_autocomplete = dd.should_autocomplete; + ff.properties_mask = dd.properties_mask; f.fields.push_back(ff); } return f; @@ -159,7 +160,7 @@ void FormStructureTest::CheckFormStructureTestData( static_cast<int>(form_structure->autofill_count())); } if (test_case.form_flags.section_count) { - std::set<std::string> section_names; + std::set<Section> section_names; for (const auto& field : *form_structure) section_names.insert(field->section); EXPECT_EQ(*test_case.form_flags.section_count, @@ -172,11 +173,6 @@ void FormStructureTest::CheckFormStructureTestData( form_structure->field(i)->html_type()); } for (size_t i = 0; - i < test_case.expected_field_types.expected_phone_part.size(); i++) { - EXPECT_EQ(test_case.expected_field_types.expected_phone_part[i], - form_structure->field(i)->phone_part()); - } - for (size_t i = 0; i < test_case.expected_field_types.expected_heuristic_type.size(); i++) { EXPECT_EQ(test_case.expected_field_types.expected_heuristic_type[i], 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 8da4714b0f9..076711dc928 100644 --- a/chromium/components/autofill/core/browser/autofill_form_test_utils.h +++ b/chromium/components/autofill/core/browser/autofill_form_test_utils.h @@ -47,6 +47,7 @@ struct FieldDataDescription { absl::optional<bool> is_autofilled; absl::optional<url::Origin> origin; std::vector<SelectOption> select_options = {}; + FieldPropertiesMask properties_mask = 0; }; // Attributes provided to the test form. @@ -89,7 +90,6 @@ struct TestFormFlags { template <typename = void> struct ExpectedFieldTypeValues { std::vector<HtmlFieldType> expected_html_type = {}; - std::vector<AutofillField::PhonePart> expected_phone_part = {}; std::vector<ServerFieldType> expected_heuristic_type = {}; std::vector<ServerFieldType> expected_overall_type = {}; }; diff --git a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc deleted file mode 100644 index bf3af43c9fa..00000000000 --- a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc +++ /dev/null @@ -1,306 +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/browser/autofill_ie_toolbar_import_win.h" - -#include <stddef.h> -#include <stdint.h> -#include <map> -#include <string> -#include <vector> - -#include "base/check.h" -#include "base/compiler_specific.h" -#include "base/memory/raw_ptr.h" -#include "base/win/registry.h" -#include "components/autofill/core/browser/crypto/rc4_decryptor.h" -#include "components/autofill/core/browser/data_model/autofill_profile.h" -#include "components/autofill/core/browser/data_model/credit_card.h" -#include "components/autofill/core/browser/data_model/form_group.h" -#include "components/autofill/core/browser/data_model/phone_number.h" -#include "components/autofill/core/browser/field_types.h" -#include "components/autofill/core/browser/geo/autofill_country.h" -#include "components/autofill/core/browser/geo/phone_number_i18n.h" -#include "components/autofill/core/browser/personal_data_manager.h" -#include "components/autofill/core/browser/personal_data_manager_observer.h" -#include "components/os_crypt/os_crypt.h" - -using base::win::RegKey; - -namespace autofill { - -// Forward declaration. This function is not in unnamed namespace as it -// is referenced in the unittest. -bool ImportCurrentUserProfiles(const std::string& app_locale, - std::vector<AutofillProfile>* profiles, - std::vector<CreditCard>* credit_cards); -namespace { - -const wchar_t* const kProfileKey = - L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Profiles"; -const wchar_t* const kCreditCardKey = - L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Credit Cards"; -const wchar_t* const kPasswordHashValue = L"password_hash"; -const wchar_t* const kSaltValue = L"salt"; - -// This string is stored along with saved addresses and credit cards in the -// WebDB, and hence should not be modified, so that it remains consistent over -// time. -const char kIEToolbarImportOrigin[] = "Imported from Internet Explorer"; - -// This is RC4 decryption for Toolbar credit card data. This is necessary -// because it is not standard, so Crypto API cannot be used. -std::wstring DecryptCCNumber(const std::wstring& data) { - const wchar_t* kEmptyKey = - L"\x3605\xCEE5\xCE49\x44F7\xCF4E\xF6CC\x604B\xFCBE\xC70A\x08FD"; - const size_t kMacLen = 10; - - if (data.length() <= kMacLen) - return std::wstring(); - - RC4Decryptor rc4_algorithm(kEmptyKey); - return rc4_algorithm.Run(data.substr(kMacLen)); -} - -bool IsEmptySalt(std::wstring const& salt) { - // Empty salt in IE Toolbar is \x1\x2...\x14 - if (salt.length() != 20) - return false; - for (size_t i = 0; i < salt.length(); ++i) { - if (salt[i] != i + 1) - return false; - } - return true; -} - -std::wstring ReadAndDecryptValue(const RegKey& key, const wchar_t* value_name) { - DWORD data_type = REG_BINARY; - DWORD data_size = 0; - LONG result = key.ReadValue(value_name, nullptr, &data_size, &data_type); - if ((result != ERROR_SUCCESS) || !data_size || data_type != REG_BINARY) - return std::wstring(); - std::string data; - data.resize(data_size); - result = key.ReadValue(value_name, &(data[0]), &data_size, &data_type); - if (result == ERROR_SUCCESS) { - std::string out_data; - if (OSCrypt::DecryptString(data, &out_data)) { - // The actual data is in UTF16 already. - if (!(out_data.size() & 1) && (out_data.size() > 2) && - !out_data[out_data.size() - 1] && !out_data[out_data.size() - 2]) { - return reinterpret_cast<const wchar_t*>(out_data.c_str()); - } - } - } - return std::wstring(); -} - -struct { - ServerFieldType field_type; - const wchar_t *reg_value_name; -} profile_reg_values[] = { - {NAME_FIRST, L"name_first"}, - {NAME_MIDDLE, L"name_middle"}, - {NAME_LAST, L"name_last"}, - {NAME_SUFFIX, L"name_suffix"}, - {EMAIL_ADDRESS, L"email"}, - {COMPANY_NAME, L"company_name"}, - {PHONE_HOME_NUMBER, L"phone_home_number"}, - {PHONE_HOME_CITY_CODE, L"phone_home_city_code"}, - {PHONE_HOME_COUNTRY_CODE, L"phone_home_country_code"}, - {ADDRESS_HOME_LINE1, L"address_home_line1"}, - {ADDRESS_HOME_LINE2, L"address_home_line2"}, - {ADDRESS_HOME_CITY, L"address_home_city"}, - {ADDRESS_HOME_STATE, L"address_home_state"}, - {ADDRESS_HOME_ZIP, L"address_home_zip"}, - {ADDRESS_HOME_COUNTRY, L"address_home_country"}, - {ADDRESS_BILLING_LINE1, L"address_billing_line1"}, - {ADDRESS_BILLING_LINE2, L"address_billing_line2"}, - {ADDRESS_BILLING_CITY, L"address_billing_city"}, - {ADDRESS_BILLING_STATE, L"address_billing_state"}, - {ADDRESS_BILLING_ZIP, L"address_billing_zip"}, - {ADDRESS_BILLING_COUNTRY, L"address_billing_country"}, - {CREDIT_CARD_NAME_FULL, L"credit_card_name_full"}, - {CREDIT_CARD_NUMBER, L"credit_card_number"}, - {CREDIT_CARD_EXP_MONTH, L"credit_card_exp_month"}, - {CREDIT_CARD_EXP_4_DIGIT_YEAR, L"credit_card_exp_4_digit_year"}, - {CREDIT_CARD_TYPE, L"credit_card_type"}, - // We do not import verification code. -}; - -typedef std::map<std::wstring, ServerFieldType> RegToFieldMap; - -// Imports address or credit card data from the given registry |key| into the -// given |form_group|, with the help of |reg_to_field|. When importing address -// data, writes the phone data into |phone|; otherwise, |phone| should be null. -// Returns true if any fields were set, false otherwise. -bool ImportSingleFormGroup(const RegKey& key, - const RegToFieldMap& reg_to_field, - const std::string& app_locale, - FormGroup* form_group, - PhoneNumber::PhoneCombineHelper* phone) { - if (!key.Valid()) - return false; - - bool has_non_empty_fields = false; - - for (uint32_t i = 0; i < key.GetValueCount(); ++i) { - std::wstring value_name; - if (key.GetValueNameAt(i, &value_name) != ERROR_SUCCESS) - continue; - - RegToFieldMap::const_iterator it = reg_to_field.find(value_name); - if (it == reg_to_field.end()) - continue; // This field is not imported. - - std::wstring field_value = ReadAndDecryptValue(key, value_name.c_str()); - if (!field_value.empty()) { - if (it->second == CREDIT_CARD_NUMBER) - field_value = DecryptCCNumber(field_value); - - // Phone numbers are stored piece-by-piece, and then reconstructed from - // the pieces. The rest of the fields are set "as is". - if (!phone || !phone->SetInfo(AutofillType(it->second), - base::WideToUTF16(field_value))) { - has_non_empty_fields = true; - form_group->SetInfo(AutofillType(it->second), - base::WideToUTF16(field_value), app_locale); - } - } - } - - return has_non_empty_fields; -} - -// Imports address data from the given registry |key| into the given |profile|, -// with the help of |reg_to_field|. Returns true if any fields were set, false -// otherwise. -bool ImportSingleProfile(const std::string& app_locale, - const RegKey& key, - const RegToFieldMap& reg_to_field, - AutofillProfile* profile) { - PhoneNumber::PhoneCombineHelper phone; - bool has_non_empty_fields = - ImportSingleFormGroup(key, reg_to_field, app_locale, profile, &phone); - - // Now re-construct the phones if needed. - std::u16string constructed_number; - if (phone.ParseNumber(*profile, app_locale, &constructed_number)) { - has_non_empty_fields = true; - profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, constructed_number); - } - - return has_non_empty_fields; -} - -// Imports profiles from the IE toolbar and stores them. Asynchronous -// if PersonalDataManager has not been loaded yet. Deletes itself on completion. -class AutofillImporter : public PersonalDataManagerObserver { - public: - explicit AutofillImporter(PersonalDataManager* personal_data_manager) - : personal_data_manager_(personal_data_manager) { - personal_data_manager_->AddObserver(this); - } - - bool ImportProfiles() { - if (!ImportCurrentUserProfiles(personal_data_manager_->app_locale(), - &profiles_, - &credit_cards_)) { - delete this; - return false; - } - if (personal_data_manager_->IsDataLoaded()) - OnPersonalDataChanged(); - return true; - } - - // PersonalDataManagerObserver: - void OnPersonalDataChanged() override { - for (const AutofillProfile& it : profiles_) - personal_data_manager_->AddProfile(it); - for (const CreditCard& it : credit_cards_) - personal_data_manager_->AddCreditCard(it); - delete this; - } - - private: - ~AutofillImporter() override { personal_data_manager_->RemoveObserver(this); } - - raw_ptr<PersonalDataManager> personal_data_manager_; - std::vector<AutofillProfile> profiles_; - std::vector<CreditCard> credit_cards_; -}; - -} // namespace - -// Imports Autofill profiles and credit cards from IE Toolbar if present and not -// password protected. Returns true if data is successfully retrieved. False if -// there is no data, data is password protected or error occurred. -bool ImportCurrentUserProfiles(const std::string& app_locale, - std::vector<AutofillProfile>* profiles, - std::vector<CreditCard>* credit_cards) { - DCHECK(profiles); - DCHECK(credit_cards); - - // Create a map of possible fields for a quick access. - RegToFieldMap reg_to_field; - for (const auto& profile_reg_value : profile_reg_values) { - reg_to_field[std::wstring(profile_reg_value.reg_value_name)] = - profile_reg_value.field_type; - } - - base::win::RegistryKeyIterator iterator_profiles(HKEY_CURRENT_USER, - kProfileKey); - for (; iterator_profiles.Valid(); ++iterator_profiles) { - std::wstring key_name(kProfileKey); - key_name.append(L"\\"); - key_name.append(iterator_profiles.Name()); - RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ); - AutofillProfile profile; - profile.set_origin(kIEToolbarImportOrigin); - if (ImportSingleProfile(app_locale, key, reg_to_field, &profile)) { - // Combine phones into whole phone #. - profiles->push_back(profile); - } - } - std::wstring password_hash; - std::wstring salt; - RegKey cc_key(HKEY_CURRENT_USER, kCreditCardKey, KEY_READ); - if (cc_key.Valid()) { - password_hash = ReadAndDecryptValue(cc_key, kPasswordHashValue); - salt = ReadAndDecryptValue(cc_key, kSaltValue); - } - - // We import CC profiles only if they are not password protected. - if (password_hash.empty() && IsEmptySalt(salt)) { - base::win::RegistryKeyIterator iterator_cc(HKEY_CURRENT_USER, - kCreditCardKey); - for (; iterator_cc.Valid(); ++iterator_cc) { - std::wstring key_name(kCreditCardKey); - key_name.append(L"\\"); - key_name.append(iterator_cc.Name()); - RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ); - CreditCard credit_card; - credit_card.set_origin(kIEToolbarImportOrigin); - if (ImportSingleFormGroup(key, reg_to_field, app_locale, &credit_card, - nullptr)) { - std::u16string cc_number = credit_card.GetRawInfo(CREDIT_CARD_NUMBER); - if (!cc_number.empty()) - credit_cards->push_back(credit_card); - } - } - } - return (profiles->size() + credit_cards->size()) > 0; -} - -bool ImportAutofillDataWin(PersonalDataManager* pdm) { - // In incognito mode we do not have PDM - and we should not import anything. - if (!pdm) - return false; - AutofillImporter* importer = new AutofillImporter(pdm); - // importer will self delete. - return importer->ImportProfiles(); -} - -} // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.h b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.h deleted file mode 100644 index 830aac2555f..00000000000 --- a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.h +++ /dev/null @@ -1,24 +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_BROWSER_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_ - -namespace autofill { - -// This importer is here and not in chrome/browser/importer/toolbar_importer.cc -// because of the following: -// 1. The data is not saved in profile, but rather in registry, thus it is -// accessed without going through toolbar front end. -// 2. This applies to IE (thus Windows) toolbar only. -// 3. The functionality relevant only to and completely encapsulated in the -// autofill. -// 4. This is completely automated as opposed to Importers, which are explicit. -class PersonalDataManager; - -bool ImportAutofillDataWin(PersonalDataManager* pdm); - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_ diff --git a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc deleted file mode 100644 index 22051ec9175..00000000000 --- a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc +++ /dev/null @@ -1,231 +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/browser/autofill_ie_toolbar_import_win.h" - -#include <stddef.h> -#include <windows.h> - -#include <string> - -#include "base/strings/utf_string_conversions.h" -#include "base/win/registry.h" -#include "components/autofill/core/browser/data_model/autofill_profile.h" -#include "components/autofill/core/browser/data_model/credit_card.h" -#include "components/autofill/core/browser/field_types.h" -#include "components/os_crypt/os_crypt.h" -#include "components/os_crypt/os_crypt_mocker.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::win::RegKey; - -namespace autofill { - -// Defined in autofill_ie_toolbar_import_win.cc. Not exposed in the header file. -bool ImportCurrentUserProfiles(const std::string& app_locale, - std::vector<AutofillProfile>* profiles, - std::vector<CreditCard>* credit_cards); - -namespace { - -const wchar_t kUnitTestRegistrySubKey[] = L"SOFTWARE\\Chromium Unit Tests"; -const wchar_t kUnitTestUserOverrideSubKey[] = - L"SOFTWARE\\Chromium Unit Tests\\HKCU Override"; - -const wchar_t kProfileKey[] = - L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Profiles"; -const wchar_t kCreditCardKey[] = - L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Credit Cards"; -const wchar_t kPasswordHashValue[] = L"password_hash"; -const wchar_t kSaltValue[] = L"salt"; - -struct ValueDescription { - wchar_t const* const value_name; - wchar_t const* const value; -}; - -ValueDescription profile1[] = { - { L"name_first", L"John" }, - { L"name_middle", L"Herman" }, - { L"name_last", L"Doe" }, - { L"email", L"jdoe@test.com" }, - { L"company_name", L"Testcompany" }, - { L"phone_home_number", L"555-5555" }, - { L"phone_home_city_code", L"650" }, - { L"phone_home_country_code", L"1" }, -}; - -ValueDescription profile2[] = { - { L"name_first", L"Jane" }, - { L"name_last", L"Doe" }, - { L"email", L"janedoe@test.com" }, - { L"company_name", L"Testcompany" }, -}; - -ValueDescription credit_card[] = { - {L"credit_card_name_full", L"Tommy Gun"}, - // "4111111111111111" encrypted: - {L"credit_card_number", - L"\xE53F\x19AB\xC1BF\xC9EB\xECCC\x9BDA\x8515" - L"\xE14D\x6852\x80A8\x50A3\x4375\xFD9F\x1E07" - L"\x790E\x7336\xB773\xAF33\x93EA\xB846\xEC89" - L"\x265C\xD0E6\x4E23\xB75F\x7983"}, - {L"credit_card_exp_month", L"11"}, - {L"credit_card_exp_4_digit_year", L"2011"}, -}; - -ValueDescription empty_salt = { - kSaltValue, - L"\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA\xB\xC\xD\xE\xF\x10\x11\x12\x13\x14" -}; - -ValueDescription empty_password = { - kPasswordHashValue, L"" -}; - -ValueDescription protected_salt = { - kSaltValue, L"\x4854\xB906\x9C7C\x50A6\x4376\xFD9D\x1E02" -}; - -ValueDescription protected_password = { - kPasswordHashValue, L"\x18B7\xE586\x459B\x7457\xA066\x3842\x71DA" -}; - -void EncryptAndWrite(RegKey* key, const ValueDescription* value) { - std::string data; - size_t data_size = (lstrlen(value->value) + 1) * sizeof(wchar_t); - data.resize(data_size); - memcpy(&data[0], value->value, data_size); - - std::string encrypted_data; - OSCrypt::EncryptString(data, &encrypted_data); - EXPECT_EQ(ERROR_SUCCESS, key->WriteValue(value->value_name, - &encrypted_data[0], encrypted_data.size(), REG_BINARY)); -} - -void CreateSubkey(RegKey* key, wchar_t const* subkey_name, - const ValueDescription* values, size_t values_size) { - RegKey subkey; - subkey.Create(key->Handle(), subkey_name, KEY_ALL_ACCESS); - EXPECT_TRUE(subkey.Valid()); - for (size_t i = 0; i < values_size; ++i) - EncryptAndWrite(&subkey, values + i); -} - -} // namespace - -class AutofillIeToolbarImportTest : public testing::Test { - public: - AutofillIeToolbarImportTest(); - - AutofillIeToolbarImportTest(const AutofillIeToolbarImportTest&) = delete; - AutofillIeToolbarImportTest& operator=(const AutofillIeToolbarImportTest&) = - delete; - - // testing::Test method overrides: - void SetUp() override; - void TearDown() override; - - private: - RegKey temp_hkcu_hive_key_; -}; - -AutofillIeToolbarImportTest::AutofillIeToolbarImportTest() { -} - -void AutofillIeToolbarImportTest::SetUp() { - OSCryptMocker::SetUp(); - temp_hkcu_hive_key_.Create(HKEY_CURRENT_USER, - kUnitTestUserOverrideSubKey, - KEY_ALL_ACCESS); - EXPECT_TRUE(temp_hkcu_hive_key_.Valid()); - EXPECT_EQ(ERROR_SUCCESS, RegOverridePredefKey(HKEY_CURRENT_USER, - temp_hkcu_hive_key_.Handle())); -} - -void AutofillIeToolbarImportTest::TearDown() { - EXPECT_EQ(ERROR_SUCCESS, RegOverridePredefKey(HKEY_CURRENT_USER, nullptr)); - temp_hkcu_hive_key_.Close(); - RegKey key(HKEY_CURRENT_USER, kUnitTestRegistrySubKey, KEY_ALL_ACCESS); - key.DeleteKey(L""); - OSCryptMocker::TearDown(); -} - -TEST_F(AutofillIeToolbarImportTest, TestAutofillImport) { - RegKey profile_key; - profile_key.Create(HKEY_CURRENT_USER, kProfileKey, KEY_ALL_ACCESS); - EXPECT_TRUE(profile_key.Valid()); - - CreateSubkey(&profile_key, L"0", profile1, std::size(profile1)); - CreateSubkey(&profile_key, L"1", profile2, std::size(profile2)); - - RegKey cc_key; - cc_key.Create(HKEY_CURRENT_USER, kCreditCardKey, KEY_ALL_ACCESS); - EXPECT_TRUE(cc_key.Valid()); - CreateSubkey(&cc_key, L"0", credit_card, std::size(credit_card)); - EncryptAndWrite(&cc_key, &empty_password); - EncryptAndWrite(&cc_key, &empty_salt); - - profile_key.Close(); - cc_key.Close(); - - std::vector<AutofillProfile> profiles; - std::vector<CreditCard> credit_cards; - EXPECT_TRUE(ImportCurrentUserProfiles("en-US", &profiles, &credit_cards)); - ASSERT_EQ(2U, profiles.size()); - // The profiles are read in reverse order. - EXPECT_EQ(base::WideToUTF16(profile1[0].value), - profiles[1].GetRawInfo(NAME_FIRST)); - EXPECT_EQ(base::WideToUTF16(profile1[1].value), - profiles[1].GetRawInfo(NAME_MIDDLE)); - EXPECT_EQ(base::WideToUTF16(profile1[2].value), - profiles[1].GetRawInfo(NAME_LAST)); - EXPECT_EQ(base::WideToUTF16(profile1[3].value), - profiles[1].GetRawInfo(EMAIL_ADDRESS)); - EXPECT_EQ(base::WideToUTF16(profile1[4].value), - profiles[1].GetRawInfo(COMPANY_NAME)); - EXPECT_EQ(base::WideToUTF16(profile1[7].value), - profiles[1].GetInfo(AutofillType(PHONE_HOME_COUNTRY_CODE), "US")); - EXPECT_EQ(base::WideToUTF16(profile1[6].value), - profiles[1].GetInfo(AutofillType(PHONE_HOME_CITY_CODE), "US")); - EXPECT_EQ(u"5555555", - profiles[1].GetInfo(AutofillType(PHONE_HOME_NUMBER), "US")); - EXPECT_EQ(u"1 650-555-5555", profiles[1].GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); - - EXPECT_EQ(base::WideToUTF16(profile2[0].value), - profiles[0].GetRawInfo(NAME_FIRST)); - EXPECT_EQ(base::WideToUTF16(profile2[1].value), - profiles[0].GetRawInfo(NAME_LAST)); - EXPECT_EQ(base::WideToUTF16(profile2[2].value), - profiles[0].GetRawInfo(EMAIL_ADDRESS)); - EXPECT_EQ(base::WideToUTF16(profile2[3].value), - profiles[0].GetRawInfo(COMPANY_NAME)); - - ASSERT_EQ(1U, credit_cards.size()); - EXPECT_EQ(base::WideToUTF16(credit_card[0].value), - credit_cards[0].GetRawInfo(CREDIT_CARD_NAME_FULL)); - EXPECT_EQ(u"4111111111111111", - credit_cards[0].GetRawInfo(CREDIT_CARD_NUMBER)); - EXPECT_EQ(base::WideToUTF16(credit_card[2].value), - credit_cards[0].GetRawInfo(CREDIT_CARD_EXP_MONTH)); - EXPECT_EQ(base::WideToUTF16(credit_card[3].value), - credit_cards[0].GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR)); - - // Mock password encrypted cc. - cc_key.Open(HKEY_CURRENT_USER, kCreditCardKey, KEY_ALL_ACCESS); - EXPECT_TRUE(cc_key.Valid()); - EncryptAndWrite(&cc_key, &protected_password); - EncryptAndWrite(&cc_key, &protected_salt); - cc_key.Close(); - - profiles.clear(); - credit_cards.clear(); - EXPECT_TRUE(ImportCurrentUserProfiles("en-US", &profiles, &credit_cards)); - // Profiles are not protected. - EXPECT_EQ(2U, profiles.size()); - // Credit cards are. - EXPECT_EQ(0U, credit_cards.size()); -} - -} // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc index 858cec0d810..54ae9d91a5e 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_manager.cc @@ -4,9 +4,14 @@ #include "components/autofill/core/browser/autofill_manager.h" +#include "base/bind.h" #include "base/command_line.h" #include "base/containers/adapters.h" +#include "base/containers/contains.h" #include "base/feature_list.h" +#include "base/metrics/histogram_macros.h" +#include "base/task/thread_pool.h" +#include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/common/autofill_constants.h" @@ -18,6 +23,7 @@ #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/autofill_tick_clock.h" #include "components/translate/core/common/language_detection_details.h" +#include "components/translate/core/common/translate_constants.h" #include "google_apis/google_api_keys.h" #include "ui/gfx/geometry/rect_f.h" @@ -25,6 +31,37 @@ namespace autofill { namespace { +// Creates a reply callback for ParseFormAsync(). +// +// An event +// AutofillManager::OnFoo(const FormData& form, args...) +// is handled by +// asynchronously parsing the form and then calling +// AutofillManager::OnFooImpl(const FormData& form, args...) +// unless the AutofillManager has been destructed or reset in the meantime. +// +// ParsingCallback(&AutofillManager::OnFooImpl, args...) creates the +// corresponding callback to be passed to ParseFormAsync(). +// +// The `after_event` is supposed to be &Observer::OnAfterFoo (or nullptr if no +// such event exists). The callback notifies the observers of `after_event`. +template <typename Functor, typename... Args> +base::OnceCallback<void(AutofillManager&, const FormData&)> ParsingCallback( + Functor&& functor, + void (AutofillManager::Observer::*after_event)(), + Args&&... args) { + return base::BindOnce( + [](Functor&& functor, void (AutofillManager::Observer::*after_event)(), + std::remove_reference_t<Args&&>... args, AutofillManager& self, + const FormData& form) { + base::invoke(std::forward<Functor>(functor), self, form, + std::forward<Args>(args)...); + if (after_event) + self.NotifyObservers(after_event); + }, + std::forward<Functor>(functor), after_event, std::forward<Args>(args)...); +} + // Returns the AutofillField* corresponding to |field| in |form| or nullptr, // if not found. AutofillField* FindAutofillFillField(const FormStructure& form, @@ -42,6 +79,7 @@ AutofillField* FindAutofillFillField(const FormStructure& form, } // Returns true if |live_form| does not match |cached_form|. +// TODO(crbug.com/1211834): This should be some form of FormData::DeepEqual(). bool CachedFormNeedsUpdate(const FormData& live_form, const FormStructure& cached_form) { if (cached_form.version() > live_form.version) @@ -73,8 +111,6 @@ std::string GetAPIKeyForUrl(version_info::Channel channel) { } // namespace -using base::TimeTicks; - // static void AutofillManager::LogAutofillTypePredictionsAvailable( LogManager* log_manager, @@ -85,15 +121,12 @@ void AutofillManager::LogAutofillTypePredictionsAvailable( VLOG(1) << *form; } - if (!log_manager || !log_manager->IsLoggingActive()) - return; - - LogBuffer buffer; + LogBuffer buffer(IsLoggingActive(log_manager)); for (FormStructure* form : forms) - buffer << *form; + LOG_AF(buffer) << *form; - log_manager->Log() << LoggingScope::kParsing << LogMessage::kParsedForms - << std::move(buffer); + LOG_AF(log_manager) << LoggingScope::kParsing << LogMessage::kParsedForms + << std::move(buffer); } // static @@ -127,20 +160,69 @@ AutofillManager::AutofillManager(AutofillDriver* driver, } AutofillManager::~AutofillManager() { + NotifyObservers(&Observer::OnAutofillManagerDestroyed); translate_observation_.Reset(); } void AutofillManager::OnLanguageDetermined( const translate::LanguageDetectionDetails& details) { - if (!base::FeatureList::IsEnabled(features::kAutofillPageLanguageDetection)) { + if (!base::FeatureList::IsEnabled(features::kAutofillPageLanguageDetection)) + return; + if (details.adopted_language == translate::kUnknownLanguageCode || + !driver_->IsInActiveFrame()) { return; } - for (auto& [form_id, form_structure] : form_structures_) { - form_structure->set_current_page_language( - LanguageCode(details.adopted_language)); - form_structure->DetermineHeuristicTypes(form_interactions_ukm_logger(), - log_manager_); + + NotifyObservers(&Observer::OnBeforeLanguageDetermined); + LanguageCode lang(details.adopted_language); + for (auto& [form_id, form_structure] : form_structures_) + form_structure->set_current_page_language(lang); + + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + for (auto& [form_id, form_structure] : form_structures_) { + form_structure->DetermineHeuristicTypes(form_interactions_ukm_logger(), + log_manager_); + } + NotifyObservers(&Observer::OnAfterLanguageDetermined); + return; } + + // To be run on a different task (must not access global or member + // variables). + // TODO(crbug.com/1309848): We can't pass a UKM logger and a LogManager + // because they're member variables. To be fixed. + auto RunHeuristics = [](std::map<FormGlobalId, std::unique_ptr<FormStructure>> + form_structures) { + SCOPED_UMA_HISTOGRAM_TIMER( + "Autofill.Timing.OnLanguageDetermined.RunHeuristics"); + for (auto& [id, form_structure] : form_structures) { + form_structure->DetermineHeuristicTypes( + /*form_interactions_ukm_logger=*/nullptr, + /*log_manager=*/nullptr); + } + return form_structures; + }; + + // To be run on the main thread (accesses member variables). + auto UpdateCache = [](base::WeakPtr<AutofillManager> self, + std::map<FormGlobalId, std::unique_ptr<FormStructure>> + form_structures) { + SCOPED_UMA_HISTOGRAM_TIMER( + "Autofill.Timing.OnLanguageDetermined.UpdateCache"); + if (!self) + return; + for (auto& [id, form_structure] : form_structures) + self->form_structures_[id] = std::move(form_structure); + self->NotifyObservers(&Observer::OnAfterLanguageDetermined); + }; + + // Transfers the cached `form_structures_` to the worker task, which will + // eventually move them back into `form_structures_`. This means + // AutofillManager knows no forms for a brief period of time. + parsing_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce(RunHeuristics, std::move(form_structures_)), + base::BindOnce(UpdateCache, parsing_weak_ptr_factory_.GetWeakPtr())); + form_structures_.clear(); } void AutofillManager::OnTranslateDriverDestroyed( @@ -156,14 +238,58 @@ LanguageCode AutofillManager::GetCurrentPageLanguage() { return LanguageCode(language_state->current_language()); } +void AutofillManager::FillCreditCardForm(int query_id, + const FormData& form, + const FormFieldData& field, + const CreditCard& credit_card, + const std::u16string& cvc) { + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + FillCreditCardFormImpl(form, field, credit_card, cvc, query_id); + return; + } + ParseFormAsync(form, ParsingCallback(&AutofillManager::FillCreditCardFormImpl, + /*after_event=*/nullptr, field, + credit_card, cvc, query_id)); +} + +void AutofillManager::FillProfileForm(const AutofillProfile& profile, + const FormData& form, + const FormFieldData& field) { + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + FillProfileFormImpl(form, field, profile); + return; + } + ParseFormAsync(form, + ParsingCallback(&AutofillManager::FillProfileFormImpl, + /*after_event=*/nullptr, field, profile)); +} + +void AutofillManager::OnDidFillAutofillFormData( + const FormData& form, + const base::TimeTicks timestamp) { + if (!IsValidFormData(form)) + return; + + NotifyObservers(&Observer::OnBeforeDidFillAutofillFormData); + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + OnDidFillAutofillFormDataImpl(form, timestamp); + NotifyObservers(&Observer::OnAfterDidFillAutofillFormData); + return; + } + ParseFormAsync( + form, + ParsingCallback(&AutofillManager::OnDidFillAutofillFormDataImpl, + &Observer::OnAfterDidFillAutofillFormData, timestamp)); +} + void AutofillManager::OnFormSubmitted(const FormData& form, - bool known_success, - mojom::SubmissionSource source) { - if (IsValidFormData(form)) - OnFormSubmittedImpl(form, known_success, source); + const bool known_success, + const mojom::SubmissionSource source) { + if (!IsValidFormData(form)) + return; - for (Observer& observer : observers_) - observer.OnFormSubmitted(); + NotifyObservers(&Observer::OnFormSubmitted); + OnFormSubmittedImpl(form, known_success, source); } void AutofillManager::OnFormsSeen( @@ -183,41 +309,52 @@ void AutofillManager::OnFormsSeen( if (!ShouldParseForms(updated_forms)) return; - std::vector<const FormData*> new_forms; - for (const FormData& form : updated_forms) { - const auto parse_form_start_time = AutofillTickClock::NowTicks(); - FormStructure* cached_form_structure = - FindCachedFormByRendererId(form.global_id()); - - // Not updating signatures of credit card forms is legacy behaviour. We - // believe that the signatures are kept stable for voting purposes. - bool update_form_signature = false; - if (cached_form_structure) { - const DenseSet<FormType>& form_types = - cached_form_structure->GetFormTypes(); - update_form_signature = - form_types.size() > form_types.count(FormType::kCreditCardForm); + NotifyObservers(&Observer::OnBeforeFormsSeen); + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + std::vector<FormData> parsed_forms; + for (const FormData& form : updated_forms) { + const auto parse_form_start_time = AutofillTickClock::NowTicks(); + FormStructure* cached_form_structure = + FindCachedFormByRendererId(form.global_id()); + + // Not updating signatures of credit card forms is legacy behaviour. We + // believe that the signatures are kept stable for voting purposes. + bool update_form_signature = false; + if (cached_form_structure) { + const DenseSet<FormType>& form_types = + cached_form_structure->GetFormTypes(); + update_form_signature = + form_types.size() > form_types.count(FormType::kCreditCardForm); + } + + FormStructure* form_structure = ParseForm(form, cached_form_structure); + if (!form_structure) + continue; + DCHECK(form_structure); + + if (update_form_signature) + form_structure->set_form_signature(CalculateFormSignature(form)); + + parsed_forms.push_back(form); + AutofillMetrics::LogParseFormTiming(AutofillTickClock::NowTicks() - + parse_form_start_time); } - - FormStructure* form_structure = ParseForm(form, cached_form_structure); - if (!form_structure) - continue; - DCHECK(form_structure); - - if (update_form_signature) - form_structure->set_form_signature(CalculateFormSignature(form)); - - new_forms.push_back(&form); - AutofillMetrics::LogParseFormTiming(AutofillTickClock::NowTicks() - - parse_form_start_time); - } - - if (new_forms.empty()) + if (!parsed_forms.empty()) + OnFormsParsed(parsed_forms); + NotifyObservers(&Observer::OnAfterFormsSeen); return; - OnFormsParsed(new_forms); + } + DCHECK(base::FeatureList::IsEnabled(features::kAutofillParseAsync)); + auto ProcessParsedForms = [](AutofillManager& self, + const std::vector<FormData>& parsed_forms) { + if (!parsed_forms.empty()) + self.OnFormsParsed(parsed_forms); + self.NotifyObservers(&Observer::OnAfterFormsSeen); + }; + ParseFormsAsync(updated_forms, base::BindOnce(ProcessParsedForms)); } -void AutofillManager::OnFormsParsed(const std::vector<const FormData*>& forms) { +void AutofillManager::OnFormsParsed(const std::vector<FormData>& forms) { DCHECK(!forms.empty()); OnBeforeProcessParsedForms(); @@ -226,9 +363,9 @@ void AutofillManager::OnFormsParsed(const std::vector<const FormData*>& forms) { std::vector<FormStructure*> non_queryable_forms; std::vector<FormStructure*> queryable_forms; DenseSet<FormType> form_types; - for (const FormData* form : forms) { + for (const FormData& form : forms) { FormStructure* form_structure = - FindCachedFormByRendererId(form->global_id()); + FindCachedFormByRendererId(form.global_id()); if (!form_structure) { NOTREACHED(); continue; @@ -238,12 +375,13 @@ void AutofillManager::OnFormsParsed(const std::vector<const FormData*>& forms) { // Configure the query encoding for this form and add it to the appropriate // collection of forms: queryable vs non-queryable. - if (form_structure->ShouldBeQueried()) + if (form_structure->ShouldBeQueried()) { queryable_forms.push_back(form_structure); - else + } else { non_queryable_forms.push_back(form_structure); + } - OnFormProcessed(*form, *form_structure); + OnFormProcessed(form, *form_structure); } if (!queryable_forms.empty() || !non_queryable_forms.empty()) { @@ -274,15 +412,21 @@ void AutofillManager::OnFormsParsed(const std::vector<const FormData*>& forms) { void AutofillManager::OnTextFieldDidChange(const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, - const TimeTicks timestamp) { + const base::TimeTicks timestamp) { if (!IsValidFormData(form) || !IsValidFormFieldData(field)) return; - OnTextFieldDidChangeImpl(form, field, bounding_box, timestamp); - - for (Observer& observer : observers_) { - observer.OnTextFieldDidChange(); + NotifyObservers(&Observer::OnBeforeTextFieldDidChange); + NotifyObservers(&Observer::OnTextFieldDidChange); + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + OnTextFieldDidChangeImpl(form, field, bounding_box, timestamp); + NotifyObservers(&Observer::OnAfterTextFieldDidChange); + return; } + ParseFormAsync(form, + ParsingCallback(&AutofillManager::OnTextFieldDidChangeImpl, + &Observer::OnAfterTextFieldDidChange, field, + bounding_box, timestamp)); } void AutofillManager::OnTextFieldDidScroll(const FormData& form, @@ -291,10 +435,14 @@ void AutofillManager::OnTextFieldDidScroll(const FormData& form, if (!IsValidFormData(form) || !IsValidFormFieldData(field)) return; - OnTextFieldDidScrollImpl(form, field, bounding_box); - - for (Observer& observer : observers_) - observer.OnTextFieldDidScroll(); + NotifyObservers(&Observer::OnTextFieldDidScroll); + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + OnTextFieldDidScrollImpl(form, field, bounding_box); + return; + } + ParseFormAsync(form, + ParsingCallback(&AutofillManager::OnTextFieldDidScrollImpl, + /*after_event=*/nullptr, field, bounding_box)); } void AutofillManager::OnSelectControlDidChange(const FormData& form, @@ -303,24 +451,39 @@ void AutofillManager::OnSelectControlDidChange(const FormData& form, if (!IsValidFormData(form) || !IsValidFormFieldData(field)) return; - OnSelectControlDidChangeImpl(form, field, bounding_box); - - for (Observer& observer : observers_) - observer.OnSelectControlDidChange(); + NotifyObservers(&Observer::OnSelectControlDidChange); + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + OnSelectControlDidChangeImpl(form, field, bounding_box); + return; + } + ParseFormAsync(form, + ParsingCallback(&AutofillManager::OnSelectControlDidChangeImpl, + /*after_event=*/nullptr, field, bounding_box)); } void AutofillManager::OnAskForValuesToFill( - int query_id, const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, + int query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible) { if (!IsValidFormData(form) || !IsValidFormFieldData(field)) return; - OnAskForValuesToFillImpl(query_id, form, field, bounding_box, - autoselect_first_suggestion, touch_to_fill_eligible); + NotifyObservers(&Observer::OnBeforeAskForValuesToFill); + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + OnAskForValuesToFillImpl(form, field, bounding_box, query_id, + autoselect_first_suggestion, + touch_to_fill_eligible); + NotifyObservers(&Observer::OnAfterAskForValuesToFill); + return; + } + ParseFormAsync( + form, ParsingCallback(&AutofillManager::OnAskForValuesToFillImpl, + &Observer::OnAfterAskForValuesToFill, field, + bounding_box, query_id, autoselect_first_suggestion, + touch_to_fill_eligible)); } void AutofillManager::OnFocusOnFormField(const FormData& form, @@ -329,7 +492,61 @@ void AutofillManager::OnFocusOnFormField(const FormData& form, if (!IsValidFormData(form) || !IsValidFormFieldData(field)) return; - OnFocusOnFormFieldImpl(form, field, bounding_box); + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + OnFocusOnFormFieldImpl(form, field, bounding_box); + return; + } + ParseFormAsync(form, + ParsingCallback(&AutofillManager::OnFocusOnFormFieldImpl, + /*after_event=*/nullptr, field, bounding_box)); +} + +void AutofillManager::OnFocusNoLongerOnForm(bool had_interacted_form) { + OnFocusNoLongerOnFormImpl(had_interacted_form); +} + +void AutofillManager::OnDidPreviewAutofillFormData() { + OnDidPreviewAutofillFormDataImpl(); +} + +void AutofillManager::OnDidEndTextFieldEditing() { + OnDidEndTextFieldEditingImpl(); +} + +void AutofillManager::OnHidePopup() { + OnHidePopupImpl(); +} + +void AutofillManager::OnSelectFieldOptionsDidChange(const FormData& form) { + if (!IsValidFormData(form)) + return; + + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + OnSelectFieldOptionsDidChangeImpl(form); + return; + } + ParseFormAsync( + form, ParsingCallback(&AutofillManager::OnSelectFieldOptionsDidChangeImpl, + /*after_event=*/nullptr)); +} + +void AutofillManager::OnJavaScriptChangedAutofilledValue( + const FormData& form, + const FormFieldData& field, + const std::u16string& old_value) { + if (!IsValidFormData(form)) + return; + + NotifyObservers(&Observer::OnBeforeAskForValuesToFill); + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + OnJavaScriptChangedAutofilledValueImpl(form, field, old_value); + NotifyObservers(&Observer::OnAfterAskForValuesToFill); + return; + } + ParseFormAsync( + form, + ParsingCallback(&AutofillManager::OnJavaScriptChangedAutofilledValueImpl, + &Observer::OnAfterAskForValuesToFill, field, old_value)); } // Returns true if |live_form| does not match |cached_form|. @@ -340,7 +557,8 @@ bool AutofillManager::GetCachedFormAndField(const FormData& form, // Maybe find an existing FormStructure that corresponds to |form|. FormStructure* cached_form = FindCachedFormByRendererId(form.global_id()); if (cached_form) { - if (!CachedFormNeedsUpdate(form, *cached_form)) { + if (base::FeatureList::IsEnabled(features::kAutofillParseAsync) || + !CachedFormNeedsUpdate(form, *cached_form)) { // There is no data to return if there are no auto-fillable fields. if (!cached_form->autofill_count()) return false; @@ -352,6 +570,9 @@ bool AutofillManager::GetCachedFormAndField(const FormData& form, } } + if (base::FeatureList::IsEnabled(features::kAutofillParseAsync)) + return false; + // The form is new or updated, parse it and discard |cached_form|. // i.e., |cached_form| is no longer valid after this call. *form_structure = ParseForm(form, cached_form); @@ -402,13 +623,197 @@ FormStructure* AutofillManager::FindCachedFormByRendererId( return it != form_structures_.end() ? it->second.get() : nullptr; } +void AutofillManager::ParseFormsAsync( + const std::vector<FormData>& forms, + base::OnceCallback<void(AutofillManager&, const std::vector<FormData>&)> + callback) { + SCOPED_UMA_HISTOGRAM_TIMER("Autofill.Timing.ParseFormsAsync"); + DCHECK(base::FeatureList::IsEnabled(features::kAutofillParseAsync)); + + // `num_managed_forms` is the number of forms that will be managed by this + // AutofillManager after ParseFormsAsync() and its asynchronous callees have + // finished. + size_t num_managed_forms = form_structures_.size(); + + // To be run on the main thread (accesses member variables). + std::vector<FormData> parsed_forms; + std::vector<std::unique_ptr<FormStructure>> form_structures; + for (const FormData& form_data : forms) { + bool is_new_form = !base::Contains(form_structures_, form_data.global_id()); + if (num_managed_forms + is_new_form > kAutofillManagerMaxFormCacheSize) { + LOG_AF(log_manager_) << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingTooManyForms + << form_data; + continue; + } + + auto form_structure = std::make_unique<FormStructure>(form_data); + form_structure->ParseFieldTypesFromAutocompleteAttributes(); + if (!form_structure->ShouldBeParsed(log_manager_)) + continue; + + num_managed_forms += is_new_form; + DCHECK_LE(num_managed_forms, kAutofillManagerMaxFormCacheSize); + + if (FormStructure* cached_form_structure = + FindCachedFormByRendererId(form_data.global_id())) { + // We need to keep the server data if available. We need to use them while + // determining the heuristics. + form_structure->RetrieveFromCache( + *cached_form_structure, + /*should_keep_cached_value=*/true, + /*only_server_and_autofill_state=*/true); + if (form_structure->value_from_dynamic_change_form()) + value_from_dynamic_change_form_ = true; + + // Not updating signatures of credit card forms is legacy behaviour. We + // believe that the signatures are kept stable for voting purposes. + DenseSet<FormType> form_types = cached_form_structure->GetFormTypes(); + if (form_types.size() > form_types.count(FormType::kCreditCardForm)) + form_structure->set_form_signature(CalculateFormSignature(form_data)); + } + + form_structure->set_current_page_language(GetCurrentPageLanguage()); + form_structures.push_back(std::move(form_structure)); + parsed_forms.push_back(form_data); + } + + // Remove duplicates by their FormGlobalId. Otherwise, after moving the forms + // into `form_structures_`, duplicates may be destroyed and we'd end up with + // dangling pointers. + base::ranges::sort(form_structures, {}, &FormStructure::global_id); + form_structures.erase( + base::ranges::unique(form_structures, {}, &FormStructure::global_id), + form_structures.end()); + + // To be run on a different task (must not access global or member + // variables). + // TODO(crbug.com/1309848): We can't pass a UKM logger and a LogManager + // because they're member variables. To be fixed. + auto RunHeuristics = + [](std::vector<std::unique_ptr<FormStructure>> form_structures) { + SCOPED_UMA_HISTOGRAM_TIMER( + "Autofill.Timing.ParseFormsAsync.RunHeuristics"); + for (auto& form_structure : form_structures) { + form_structure->DetermineHeuristicTypes( + /*form_interactions_ukm_logger=*/nullptr, + /*log_manager=*/nullptr); + } + return form_structures; + }; + + // To be run on the main thread (accesses member variables). + auto UpdateCache = + [](base::WeakPtr<AutofillManager> self, + base::OnceCallback<void(AutofillManager&, + const std::vector<FormData>&)> callback, + const std::vector<FormData>& parsed_forms, + std::vector<std::unique_ptr<FormStructure>> form_structures) { + SCOPED_UMA_HISTOGRAM_TIMER( + "Autofill.Timing.ParseFormsAsync.UpdateCache"); + if (!self) + return; + for (auto& form_structure : form_structures) { + FormGlobalId id = form_structure->global_id(); + self->form_structures_[id] = std::move(form_structure); + } + self->NotifyObservers(&Observer::OnFormParsed); + std::move(callback).Run(*self, parsed_forms); + }; + + parsing_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce(RunHeuristics, std::move(form_structures)), + base::BindOnce(UpdateCache, parsing_weak_ptr_factory_.GetWeakPtr(), + std::move(callback), parsed_forms)); +} + +void AutofillManager::ParseFormAsync( + const FormData& form_data, + base::OnceCallback<void(AutofillManager&, const FormData&)> callback) { + SCOPED_UMA_HISTOGRAM_TIMER("Autofill.Timing.ParseFormAsync"); + DCHECK(base::FeatureList::IsEnabled(features::kAutofillParseAsync)); + + bool is_new_form = !base::Contains(form_structures_, form_data.global_id()); + if (form_structures_.size() + is_new_form > + kAutofillManagerMaxFormCacheSize) { + LOG_AF(log_manager_) << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingTooManyForms << form_data; + return; + } + + auto form_structure = std::make_unique<FormStructure>(form_data); + form_structure->ParseFieldTypesFromAutocompleteAttributes(); + if (!form_structure->ShouldBeParsed(log_manager_)) { + // For Autocomplete, events need to be handled even for forms that cannot be + // parsed. + std::move(callback).Run(*this, form_data); + return; + } + + if (FormStructure* cached_form_structure = + FindCachedFormByRendererId(form_data.global_id())) { + if (!CachedFormNeedsUpdate(form_data, *cached_form_structure)) { + std::move(callback).Run(*this, form_data); + return; + } + + // We need to keep the server data if available. We need to use them while + // determining the heuristics. + form_structure->RetrieveFromCache(*cached_form_structure, + /*should_keep_cached_value=*/true, + /*only_server_and_autofill_state=*/true); + if (form_structure->value_from_dynamic_change_form()) + value_from_dynamic_change_form_ = true; + } + form_structure->set_current_page_language(GetCurrentPageLanguage()); + + // To be run on a different task (must not access global or member + // variables). + // TODO(crbug.com/1309848): We can't pass a UKM logger and a LogManager + // because they're member variables. To be fixed. + auto RunHeuristics = [](std::unique_ptr<FormStructure> form_structure) { + SCOPED_UMA_HISTOGRAM_TIMER("Autofill.Timing.ParseFormAsync.RunHeuristics"); + form_structure->DetermineHeuristicTypes( + /*form_interactions_ukm_logger=*/nullptr, + /*log_manager=*/nullptr); + return form_structure; + }; + + // To be run on the main thread (accesses member variables). + // The reason this takes both `form_data` and `form_structure` is that they + // may disagree on the form's values: if the form is seen for the second time, + // RetrieveFromCache() resets the `form_structure`'s fields. + // TODO(crbug/1345089): Make FormStructure's and FormData's fields correspond, + // migrate all event handlers in BrowserAutofillManager take a FormStructure, + // and drop the FormData from UpdateCache(). + auto UpdateCache = + [](base::WeakPtr<AutofillManager> self, + base::OnceCallback<void(AutofillManager&, const FormData&)> callback, + const FormData& form_data, + std::unique_ptr<FormStructure> form_structure) { + SCOPED_UMA_HISTOGRAM_TIMER( + "Autofill.Timing.ParseFormAsync.UpdateCache"); + if (!self) + return; + FormGlobalId id = form_structure->global_id(); + self->form_structures_[id] = std::move(form_structure); + self->NotifyObservers(&Observer::OnFormParsed); + std::move(callback).Run(*self, form_data); + }; + + parsing_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce(RunHeuristics, std::move(form_structure)), + base::BindOnce(UpdateCache, parsing_weak_ptr_factory_.GetWeakPtr(), + std::move(callback), form_data)); +} + FormStructure* AutofillManager::ParseForm(const FormData& form, const FormStructure* cached_form) { + DCHECK(!base::FeatureList::IsEnabled(features::kAutofillParseAsync)); + if (form_structures_.size() >= kAutofillManagerMaxFormCacheSize) { - if (log_manager_) { - log_manager_->Log() << LoggingScope::kAbortParsing - << LogMessage::kAbortParsingTooManyForms << form; - } + LOG_AF(log_manager_) << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingTooManyForms << form; return nullptr; } @@ -424,9 +829,7 @@ FormStructure* AutofillManager::ParseForm(const FormData& form, /*should_keep_cached_value=*/true, /*only_server_and_autofill_state=*/true); - for (Observer& observer : observers_) - observer.OnFormParsed(); - + NotifyObservers(&Observer::OnFormParsed); if (form_structure.get()->value_from_dynamic_change_form()) value_from_dynamic_change_form_ = true; } @@ -452,6 +855,8 @@ FormStructure* AutofillManager::ParseForm(const FormData& form, } void AutofillManager::Reset() { + parsing_weak_ptr_factory_.InvalidateWeakPtrs(); + NotifyObservers(&Observer::OnAutofillManagerReset); form_structures_.clear(); form_interactions_ukm_logger_ = CreateFormInteractionsUkmLogger(); } diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h index 9997b3401c0..2354a2fa121 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.h +++ b/chromium/components/autofill/core/browser/autofill_manager.h @@ -10,10 +10,12 @@ #include <string> #include <vector> -#include "base/cancelable_callback.h" +#include "base/bind.h" #include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/scoped_observation.h" +#include "base/task/thread_pool.h" #include "base/time/time.h" #include "base/types/strong_alias.h" #include "build/build_config.h" @@ -26,6 +28,7 @@ #include "components/autofill/core/common/mojom/autofill_types.mojom.h" #include "components/autofill/core/common/signatures.h" #include "components/autofill/core/common/unique_ids.h" +#include "components/autofill_assistant/core/public/autofill_assistant_intent.h" #include "components/translate/core/browser/translate_driver.h" #include "components/version_info/channel.h" @@ -55,23 +58,46 @@ class AutofillManager : public AutofillDownloadManager::Observer, public translate::TranslateDriver::LanguageDetectionObserver { public: - // An observer class used by browsertests that gets notified whenever - // particular actions occur. + // Observer of AutofillManager events. + // + // OnAfterFoo() is called, perhaps asynchronously (but on the UI thread), + // after OnBeforeFoo(). The only exceptions where OnBeforeFoo() may be called + // without a corresponding OnAfterFoo() call are: + // - if the number of cached forms exceeds `kAutofillManagerMaxFormCacheSize`; + // - if this AutofillManager has been destroyed or reset in the meantime. + // + // The main purpose are unit tests. New pairs of events may be added as + // needed. class Observer : public base::CheckedObserver { public: - virtual void OnFormParsed(){}; + virtual void OnAutofillManagerDestroyed() {} + virtual void OnAutofillManagerReset() {} - // See |AutofillManager::OnTextFieldDidChange|. - virtual void OnTextFieldDidChange(){}; + virtual void OnBeforeLanguageDetermined() {} + virtual void OnAfterLanguageDetermined() {} - // See |AutofillManager::OnTextFieldDidScroll|. - virtual void OnTextFieldDidScroll(){}; + virtual void OnBeforeFormsSeen() {} + virtual void OnAfterFormsSeen() {} - // See |AutofillManager::OnSelectControlDidChange|. - virtual void OnSelectControlDidChange(){}; + virtual void OnBeforeTextFieldDidChange() {} + virtual void OnAfterTextFieldDidChange() {} - // See |AutofillManager::OnFormSubmitted|. - virtual void OnFormSubmitted(){}; + virtual void OnBeforeDidFillAutofillFormData() {} + virtual void OnAfterDidFillAutofillFormData() {} + + virtual void OnBeforeAskForValuesToFill() {} + virtual void OnAfterAskForValuesToFill() {} + + virtual void OnBeforeJavaScriptChangedAutofilledValue() {} + virtual void OnAfterJavaScriptChangedAutofilledValue() {} + + // TODO(crbug.com/1330105): Clean up API: delete the events that don't + // follow the OnBeforeFoo() / OnAfterFoo() pattern. + virtual void OnFormParsed() {} + virtual void OnTextFieldDidChange() {} + virtual void OnTextFieldDidScroll() {} + virtual void OnSelectControlDidChange() {} + virtual void OnFormSubmitted() {} }; using EnableDownloadManager = @@ -104,21 +130,27 @@ class AutofillManager return client_; } + // Returns a WeakPtr to the leaf class. + virtual base::WeakPtr<AutofillManager> GetWeakPtr() = 0; + // May return nullptr. virtual AutofillOfferManager* GetOfferManager() = 0; // May return nullptr. virtual CreditCardAccessManager* GetCreditCardAccessManager() = 0; + // Events triggered by the renderer. + // Returns true only if the previewed form should be cleared. virtual bool ShouldClearPreviewedForm() = 0; // Invoked when the value of textfield is changed. // |bounding_box| are viewport coordinates. - void OnTextFieldDidChange(const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box, - const base::TimeTicks timestamp); + // Virtual for testing. + virtual void OnTextFieldDidChange(const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + const base::TimeTicks timestamp); // Invoked when the textfield is scrolled. // |bounding_box| are viewport coordinates. @@ -138,12 +170,13 @@ class AutofillManager // |touch_to_fill_eligible| indicates if the Touch To Fill surface could be // used for showing suggestion. Note that it doesn't guarantee the given form // input field is eligible for autofilling. - void OnAskForValuesToFill(int query_id, - const FormData& form, - const FormFieldData& field, - const gfx::RectF& bounding_box, - bool autoselect_first_suggestion, - TouchToFillEligible touch_to_fill_eligible); + // Virtual for testing. + virtual void OnAskForValuesToFill(const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + int query_id, + bool autoselect_first_suggestion, + TouchToFillEligible touch_to_fill_eligible); // Invoked when |form|'s |field| has focus. // |bounding_box| are viewport coordinates. @@ -154,56 +187,72 @@ class AutofillManager // Invoked when |form| has been submitted. // Processes the submitted |form|, saving any new Autofill data to the user's // personal profile. - void OnFormSubmitted(const FormData& form, - bool known_success, - mojom::SubmissionSource source); - - virtual void FillCreditCardForm(int query_id, - const FormData& form, - const FormFieldData& field, - const CreditCard& credit_card, - const std::u16string& cvc) = 0; - virtual void FillProfileForm(const AutofillProfile& profile, - const FormData& form, - const FormFieldData& field) = 0; + // Virtual for testing. + virtual void OnFormSubmitted(const FormData& form, + bool known_success, + mojom::SubmissionSource source); + + void FillCreditCardForm(int query_id, + const FormData& form, + const FormFieldData& field, + const CreditCard& credit_card, + const std::u16string& cvc); + + void FillProfileForm(const AutofillProfile& profile, + const FormData& form, + const FormFieldData& field); + + // Invoked when |form| has been filled with the value given by + // FillOrPreviewForm. + // Virtual for testing. + virtual void OnDidFillAutofillFormData(const FormData& form, + const base::TimeTicks timestamp); + + // Profile Autofill was triggered by assistant's |intent|. This only affects + // metrics logging. + virtual void SetProfileFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) = 0; + + // Credit Card Autofill was triggered by assistant's |intent|. This only + // affects metrics logging. + virtual void SetCreditCardFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) = 0; // Invoked when changes of the forms have been detected: the forms in // |updated_forms| are either new or have changed, and the forms in // |removed_forms| have been removed from the DOM (but may be re-added to the // DOM later). + // Virtual for testing. virtual void OnFormsSeen(const std::vector<FormData>& updated_forms, const std::vector<FormGlobalId>& removed_forms); // Invoked when focus is no longer on form. |had_interacted_form| indicates // whether focus was previously on a form with which the user had interacted. - virtual void OnFocusNoLongerOnForm(bool had_interacted_form) = 0; - - // Invoked when |form| has been filled with the value given by - // FillOrPreviewForm. - virtual void OnDidFillAutofillFormData(const FormData& form, - const base::TimeTicks timestamp) = 0; + void OnFocusNoLongerOnForm(bool had_interacted_form); // Invoked when preview autofill value has been shown. - virtual void OnDidPreviewAutofillFormData() = 0; + void OnDidPreviewAutofillFormData(); // Invoked when textfeild editing ended - virtual void OnDidEndTextFieldEditing() = 0; + void OnDidEndTextFieldEditing(); // Invoked when popup window should be hidden. - virtual void OnHidePopup() = 0; + void OnHidePopup(); // Invoked when the options of a select element in the |form| changed. - virtual void SelectFieldOptionsDidChange(const FormData& form) = 0; + void OnSelectFieldOptionsDidChange(const FormData& form); // Invoked after JavaScript set the value of |field| in |form|. Only called // if |field| was in autofilled state. Note that from a renderer's // perspective, modifying the value with JavaScript leads to a state where // the field is not considered autofilled anymore. So this notification won't // be sent again until the field gets autofilled again. - virtual void JavaScriptChangedAutofilledValue( + virtual void OnJavaScriptChangedAutofilledValue( const FormData& form, const FormFieldData& field, - const std::u16string& old_value) = 0; + const std::u16string& old_value); + + // Other events. // Invoked when the field type predictions are downloaded from the autofill // server. @@ -220,8 +269,8 @@ class AutofillManager translate::TranslateDriver* translate_driver) override; // Invoked when the language has been detected by the Translate component. // As this usually happens after Autofill has parsed the forms for the first - // time, the heuristics need to be re-run by this function in order to run - // use language-specific patterns. + // time, the heuristics need to be re-run by this function in order to use + // language-specific patterns. void OnLanguageDetermined( const translate::LanguageDetectionDetails& details) override; @@ -247,6 +296,11 @@ class AutofillManager observers_.RemoveObserver(observer); } + void NotifyObservers(void (Observer::*event)()) { + for (Observer& observer : observers_) + base::invoke(event, observer); + } + // Returns the present form structures seen by Autofill handler. const std::map<FormGlobalId, std::unique_ptr<FormStructure>>& form_structures() const { @@ -328,10 +382,10 @@ class AutofillManager const gfx::RectF& bounding_box) = 0; virtual void OnAskForValuesToFillImpl( - int query_id, const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, + int query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible) = 0; @@ -343,16 +397,46 @@ class AutofillManager const FormFieldData& field, const gfx::RectF& bounding_box) = 0; + virtual void OnDidFillAutofillFormDataImpl( + const FormData& form, + const base::TimeTicks timestamp) = 0; + + virtual void FillCreditCardFormImpl(const FormData& form, + const FormFieldData& field, + const CreditCard& credit_card, + const std::u16string& cvc, + int query_id) = 0; + virtual void FillProfileFormImpl(const FormData& form, + const FormFieldData& field, + const AutofillProfile& profile) = 0; + + virtual void OnFocusNoLongerOnFormImpl(bool had_interacted_form) = 0; + + virtual void OnDidPreviewAutofillFormDataImpl() = 0; + + virtual void OnDidEndTextFieldEditingImpl() = 0; + + virtual void OnHidePopupImpl() = 0; + + virtual void OnSelectFieldOptionsDidChangeImpl(const FormData& form) = 0; + + virtual void OnJavaScriptChangedAutofilledValueImpl( + const FormData& form, + const FormFieldData& field, + const std::u16string& old_value) = 0; + // Return whether the |forms| from OnFormSeen() should be parsed to // form_structures. virtual bool ShouldParseForms(const std::vector<FormData>& forms) = 0; // Invoked before parsing the forms. + // TODO(crbug.com/1309848): Rename to some consistent scheme, e.g., + // OnBeforeParsedForm(). virtual void OnBeforeProcessParsedForms() = 0; // Invoked when the given |form| has been processed to the given // |form_structure|. - virtual void OnFormProcessed(const FormData& form, + virtual void OnFormProcessed(const FormData& form_data, const FormStructure& form_structure) = 0; // Invoked after all forms have been processed, |form_types| is a set of // FormType found. @@ -365,6 +449,40 @@ class AutofillManager FormSignature form_signature, std::vector<FormStructure*>* form_structures) const; + // Parses multiple forms in one go. The function proceeds in three stages: + // + // 1. Turn (almost) every FormData into a FormStructure. + // 2. Run DetermineHeuristicTypes() on all FormStructures. + // 3. Update the cache member variable `form_structures_` and call `callback`. + // + // Step 1 runs synchronously on the main thread. + // Step 2 runs asynchronously on a worker task. + // Step 3 runs again on the main thread. + // + // There are two conditions under which a FormData is skipped in Step 1: + // - if the overall number exceeds `kAutofillManagerMaxFormCacheSize`; + // - if the form should not be parsed according to ShouldParseForms(). + // + // TODO(crbug.com/1309848): Add unit tests. + // TODO(crbug.com/1345089): Eliminate either the ParseFormsAsync() or + // ParseFormAsync(). There are a few possible directions: + // - Let ParseFormAync() wrap the FormData in a vector, call + // ParseFormsAsync(), and then unwrap the vector again. + // - Let OnFormsSeen() take a single FormData. That simplifies also + // ContentAutofillDriver and ContentAutofillRouter a bit, but then the + // AutofillDownloadManager needs to collect forms to send a batch query. + // - Let all other events take a FormGlobalId instead of a FormData and fire + // OnFormsSeen() before these events if necessary. + void ParseFormsAsync( + const std::vector<FormData>& forms, + base::OnceCallback<void(AutofillManager&, const std::vector<FormData>&)> + callback); + + // Parses a single form analogously to ParseFormsAsync(). + void ParseFormAsync( + const FormData& form, + base::OnceCallback<void(AutofillManager&, const FormData&)> callback); + // Parses the |form| with the server data retrieved from the |cached_form| // (if any). Returns nullptr if the form should not be parsed. Otherwise, adds // the returned form structure to the |form_structures_|. @@ -397,7 +515,7 @@ class AutofillManager // Invoked when forms from OnFormsSeen() have been parsed to // |form_structures|. - void OnFormsParsed(const std::vector<const FormData*>& forms); + void OnFormsParsed(const std::vector<FormData>& forms); std::unique_ptr<AutofillMetrics::FormInteractionsUkmLogger> CreateFormInteractionsUkmLogger(); @@ -434,6 +552,15 @@ class AutofillManager // Observers that listen to updates of this instance. base::ObserverList<Observer> observers_; + + // DetermineHeuristicTypes() should only be run on the `parsing_task_runner_`. + // The reply will be called on the main thread and should be a no-op if this + // AutofillManager has been destroyed or reset; to detect this, the reply + // should take a WeakPtr from `parsing_weak_ptr_factory_`. + scoped_refptr<base::SequencedTaskRunner> parsing_task_runner_ = + base::ThreadPool::CreateSequencedTaskRunner( + {base::TaskPriority::USER_VISIBLE}); + base::WeakPtrFactory<AutofillManager> parsing_weak_ptr_factory_{this}; }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc index dd63441f56a..21c00f619c4 100644 --- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc @@ -16,6 +16,7 @@ #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/browser/test_autofill_driver.h" +#include "components/autofill/core/browser/test_autofill_manager_waiter.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_tick_clock.h" @@ -55,6 +56,11 @@ class MockAutofillManager : public AutofillManager { client, client->GetChannel(), EnableDownloadManager(false)) {} + + base::WeakPtr<AutofillManager> GetWeakPtr() override { + return weak_ptr_factory_.GetWeakPtr(); + } + MOCK_METHOD(bool, ShouldClearPreviewedForm, (), (override)); MOCK_METHOD(AutofillOfferManager*, GetOfferManager, (), (override)); MOCK_METHOD(CreditCardAccessManager*, @@ -62,36 +68,44 @@ class MockAutofillManager : public AutofillManager { (), (override)); MOCK_METHOD(void, - FillCreditCardForm, - (int query_id, - const FormData& form, + FillCreditCardFormImpl, + (const FormData& form, const FormFieldData& field, const CreditCard& credit_card, - const std::u16string& cvc), + const std::u16string& cvc, + int query_id), + (override)); + MOCK_METHOD(void, + FillProfileFormImpl, + (const FormData& form, + const FormFieldData& field, + const AutofillProfile& profile), + (override)); + MOCK_METHOD(void, + SetProfileFillViaAutofillAssistantIntent, + (const autofill_assistant::AutofillAssistantIntent intent), (override)); MOCK_METHOD(void, - FillProfileForm, - (const autofill::AutofillProfile& profile, - const FormData& form, - const FormFieldData& field), + SetCreditCardFillViaAutofillAssistantIntent, + (const autofill_assistant::AutofillAssistantIntent intent), (override)); MOCK_METHOD(void, - OnFocusNoLongerOnForm, + OnFocusNoLongerOnFormImpl, (bool had_interacted_form), (override)); MOCK_METHOD(void, - OnDidFillAutofillFormData, + OnDidFillAutofillFormDataImpl, (const FormData& form, const base::TimeTicks timestamp), (override)); - MOCK_METHOD(void, OnDidPreviewAutofillFormData, (), (override)); - MOCK_METHOD(void, OnDidEndTextFieldEditing, (), (override)); - MOCK_METHOD(void, OnHidePopup, (), (override)); + MOCK_METHOD(void, OnDidPreviewAutofillFormDataImpl, (), (override)); + MOCK_METHOD(void, OnDidEndTextFieldEditingImpl, (), (override)); + MOCK_METHOD(void, OnHidePopupImpl, (), (override)); MOCK_METHOD(void, - SelectFieldOptionsDidChange, + OnSelectFieldOptionsDidChangeImpl, (const FormData& form), (override)); MOCK_METHOD(void, - JavaScriptChangedAutofilledValue, + OnJavaScriptChangedAutofilledValueImpl, (const FormData& form, const FormFieldData& field, const std::u16string& old_value), @@ -121,10 +135,10 @@ class MockAutofillManager : public AutofillManager { (override)); MOCK_METHOD(void, OnAskForValuesToFillImpl, - (int query_id, - const FormData& form, + (const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box, + int query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible), (override)); @@ -147,7 +161,7 @@ class MockAutofillManager : public AutofillManager { MOCK_METHOD(void, OnBeforeProcessParsedForms, (), (override)); MOCK_METHOD(void, OnFormProcessed, - (const FormData& form, const FormStructure& form_structure), + (const FormData& form_data, const FormStructure& form_structure), (override)); MOCK_METHOD(void, OnAfterProcessParsedForms, @@ -157,6 +171,9 @@ class MockAutofillManager : public AutofillManager { ReportAutofillWebOTPMetrics, (bool used_web_otp), (override)); + + private: + base::WeakPtrFactory<MockAutofillManager> weak_ptr_factory_{this}; }; class MockAutofillObserver : public AutofillManager::Observer { @@ -225,7 +242,10 @@ void OnFormsSeenWithExpectations(MockAutofillManager& manager, EXPECT_CALL(manager, OnBeforeProcessParsedForms()).Times(num > 0); EXPECT_CALL(manager, OnFormProcessed(_, _)).Times(num); EXPECT_CALL(manager, OnAfterProcessParsedForms(_)).Times(num > 0); + TestAutofillManagerWaiter waiter( + manager, {&AutofillManager::Observer::OnAfterFormsSeen}); manager.OnFormsSeen(updated_forms, removed_forms); + ASSERT_TRUE(waiter.Wait()); EXPECT_THAT(manager.form_structures(), HaveSameFormIdsAs(expectation)); } 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 dedac9690a7..c76723c09cf 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc @@ -237,13 +237,9 @@ std::unique_ptr<EntityData> CreateEntityDataFromAutofillProfile( entry.GetVerificationStatus(ADDRESS_HOME_HOUSE_NUMBER))); // Set birthdate-related values. - if (base::FeatureList::IsEnabled( - features::kAutofillEnableCompatibilitySupportForBirthdates)) { - specifics->set_birthdate_day(entry.GetRawInfoAsInt(BIRTHDATE_DAY)); - specifics->set_birthdate_month(entry.GetRawInfoAsInt(BIRTHDATE_MONTH)); - specifics->set_birthdate_year( - entry.GetRawInfoAsInt(BIRTHDATE_YEAR_4_DIGITS)); - } + specifics->set_birthdate_day(entry.GetRawInfoAsInt(BIRTHDATE_DAY)); + specifics->set_birthdate_month(entry.GetRawInfoAsInt(BIRTHDATE_MONTH)); + specifics->set_birthdate_year(entry.GetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR)); return entity_data; } @@ -468,13 +464,9 @@ std::unique_ptr<AutofillProfile> CreateAutofillProfileFromSpecifics( specifics.address_home_subpremise_name_status())); // Set birthdate-related fields. - if (base::FeatureList::IsEnabled( - features::kAutofillEnableCompatibilitySupportForBirthdates)) { - profile->SetRawInfoAsInt(BIRTHDATE_DAY, specifics.birthdate_day()); - profile->SetRawInfoAsInt(BIRTHDATE_MONTH, specifics.birthdate_month()); - profile->SetRawInfoAsInt(BIRTHDATE_YEAR_4_DIGITS, - specifics.birthdate_year()); - } + profile->SetRawInfoAsInt(BIRTHDATE_DAY, specifics.birthdate_day()); + profile->SetRawInfoAsInt(BIRTHDATE_MONTH, specifics.birthdate_month()); + profile->SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, specifics.birthdate_year()); // The profile may be in a legacy state. By calling |FinalizeAfterImport()| // * The profile is migrated if the name structure is in legacy state. diff --git a/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc index c9f74d78433..ee4962d3067 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc @@ -126,7 +126,7 @@ AutofillProfile ConstructCompleteProfile() { // Set testing values for the birthdate. profile.SetRawInfoAsInt(BIRTHDATE_DAY, 14); profile.SetRawInfoAsInt(BIRTHDATE_MONTH, 3); - profile.SetRawInfoAsInt(BIRTHDATE_YEAR_4_DIGITS, 1997); + profile.SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, 1997); return profile; } @@ -292,8 +292,7 @@ TEST_F(AutofillProfileSyncUtilTest, CreateEntityDataFromAutofillProfile) { structured_names_feature.InitWithFeatures( {features::kAutofillEnableSupportForMoreStructureInAddresses, features::kAutofillEnableSupportForMoreStructureInNames, - features::kAutofillEnableSupportForHonorificPrefixes, - features::kAutofillEnableCompatibilitySupportForBirthdates}, + features::kAutofillEnableSupportForHonorificPrefixes}, {}); AutofillProfile profile = ConstructCompleteProfile(); diff --git a/chromium/components/autofill/core/browser/autofill_progress_dialog_type.h b/chromium/components/autofill/core/browser/autofill_progress_dialog_type.h new file mode 100644 index 00000000000..4b622ac66fd --- /dev/null +++ b/chromium/components/autofill/core/browser/autofill_progress_dialog_type.h @@ -0,0 +1,23 @@ +// Copyright 2022 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_AUTOFILL_PROGRESS_DIALOG_TYPE_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROGRESS_DIALOG_TYPE_H_ + +namespace autofill { + +// The type of autofill progress dialog to show. +enum class AutofillProgressDialogType { + // Unspecified progress dialog type. + kUnspecified = 0, + // Used when authenticating with FIDO. + // This progress dialog type applies to Android only. + kAndroidFIDOProgressDialog = 1, + // Used when unmasking virtual cards. + kVirtualCardUnmaskProgressDialog = 2 +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROGRESS_DIALOG_TYPE_H_ diff --git a/chromium/components/autofill/core/browser/autofill_regex_constants.h b/chromium/components/autofill/core/browser/autofill_regex_constants.h deleted file mode 100644 index 639a7680f39..00000000000 --- a/chromium/components/autofill/core/browser/autofill_regex_constants.h +++ /dev/null @@ -1,107 +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_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_ - -namespace autofill { - -extern const char16_t kAttentionIgnoredRe[]; -extern const char16_t kRegionIgnoredRe[]; -extern const char16_t kAddressNameIgnoredRe[]; -extern const char16_t kCompanyRe[]; -extern const char16_t kHouseNumberRe[]; -extern const char16_t kApartmentNumberRe[]; -extern const char16_t kStreetNameRe[]; -extern const char16_t kAddressLine1Re[]; -extern const char16_t kAddressLine1LabelRe[]; -extern const char16_t kAddressLine2Re[]; -extern const char16_t kAddressLine2LabelRe[]; -extern const char16_t kAddressLinesExtraRe[]; -extern const char16_t kAddressLookupRe[]; -extern const char16_t kCountryRe[]; -extern const char16_t kDependentLocality[]; -extern const char16_t kCountryLocationRe[]; -extern const char16_t kZipCodeRe[]; -extern const char16_t kZip4Re[]; -extern const char16_t kDependentLocalityRe[]; -extern const char16_t kCityRe[]; -extern const char16_t kStateRe[]; -extern const char16_t kNameOnCardRe[]; -extern const char16_t kNameOnCardContextualRe[]; -extern const char16_t kCardNumberRe[]; -extern const char16_t kCardCvcRe[]; -extern const char16_t kCardTypeRe[]; -extern const char16_t kExpirationMonthRe[]; -extern const char16_t kExpirationYearRe[]; -extern const char16_t kExpirationDate2DigitYearRe[]; -extern const char16_t kExpirationDate4DigitYearRe[]; -extern const char16_t kExpirationDateRe[]; -extern const char16_t kCardIgnoredRe[]; -extern const char16_t kGiftCardRe[]; -extern const char16_t kDebitGiftCardRe[]; -extern const char16_t kDebitCardRe[]; -extern const char16_t kDayRe[]; -extern const char16_t kEmailRe[]; -extern const char16_t kNameIgnoredRe[]; -extern const char16_t kFullNameRe[]; -extern const char16_t kNameGenericRe[]; -extern const char16_t kFirstNameRe[]; -extern const char16_t kMiddleInitialRe[]; -extern const char16_t kMiddleNameRe[]; -extern const char16_t kLastNameRe[]; -extern const char16_t kHonorificPrefixRe[]; -extern const char16_t kNameLastFirstRe[]; -extern const char16_t kNameLastSecondRe[]; -extern const char16_t kPhoneRe[]; -extern const char16_t kAugmentedPhoneCountryCodeRe[]; -extern const char16_t kCountryCodeRe[]; -extern const char16_t kAreaCodeNotextRe[]; -extern const char16_t kAreaCodeRe[]; -extern const char16_t kFaxRe[]; -extern const char16_t kPhonePrefixSeparatorRe[]; -extern const char16_t kPhoneSuffixSeparatorRe[]; -extern const char16_t kPhonePrefixRe[]; -extern const char16_t kPhoneSuffixRe[]; -extern const char16_t kPhoneExtensionRe[]; -extern const char16_t kSearchTermRe[]; -extern const char16_t kPassportRe[]; -extern const char16_t kTravelOriginRe[]; -extern const char16_t kTravelDestinationRe[]; -extern const char16_t kFlightRe[]; -extern const char16_t kPriceRe[]; -extern const char16_t kCreditCardCVCPattern[]; -extern const char16_t kCreditCard4DigitExpYearPattern[]; -extern const char16_t kSocialSecurityRe[]; -extern const char16_t kOneTimePwdRe[]; -extern const char16_t kHiddenValueRe[]; -extern const char16_t kMerchantPromoCodeRe[]; -extern const char16_t kEmailValueRe[]; -extern const char16_t kPhoneValueRe[]; -extern const char16_t kUsernameLikeValueRe[]; - -// Used to match field data that might be a UPI Virtual Payment Address. -// See: -// - http://crbug.com/702220 -// - https://upipayments.co.in/virtual-payment-address-vpa/ -extern const char16_t kUPIVirtualPaymentAddressRe[]; - -// Used to match field data that might be an International Bank Account Number. -// TODO(crbug.com/977377): The regex doesn't match IBANs for Saint Lucia (LC), -// Kazakhstan (KZ) and Romania (RO). Consider replace the regex with something -// like "(?:IT|SM)\d{2}[A-Z]\d{22}|CY\d{2}[A-Z]\d{23}...". For reference: -// - https://www.swift.com/resource/iban-registry-pdf -extern const char16_t kInternationalBankAccountNumberRe[]; - -// Match the path values for form actions that look like generic search: -// e.g. /search -// /search/ -// /search/products... -// /products/search/ -// /blah/search_all.jsp -extern const char16_t kUrlSearchActionRe[]; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_ diff --git a/chromium/components/autofill/core/browser/autofill_regexes.cc b/chromium/components/autofill/core/browser/autofill_regexes.cc deleted file mode 100644 index f5aa6a9b59b..00000000000 --- a/chromium/components/autofill/core/browser/autofill_regexes.cc +++ /dev/null @@ -1,102 +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/browser/autofill_regexes.h" - -#include <map> -#include <memory> -#include <string> -#include <utility> - -#include "base/check.h" -#include "base/i18n/unicodestring.h" -#include "base/no_destructor.h" -#include "base/synchronization/lock.h" -#include "third_party/icu/source/i18n/unicode/regex.h" - -namespace { - -// Maximum length of the string to match to avoid causing an icu::RegexMatcher -// stack overflow. (crbug.com/1198219) -constexpr int kMaxStringLength = 5000; - -// A thread-local class that serves as a cache of compiled regex patterns. -// -// The regexp state can be accessed from multiple threads in single process -// mode, and this class offers per-thread instance instead of per-process -// singleton instance (https://crbug.com/812182). -class AutofillRegexes { - public: - AutofillRegexes() = default; - - AutofillRegexes(const AutofillRegexes&) = delete; - AutofillRegexes& operator=(const AutofillRegexes&) = delete; - - // Returns the compiled regex matcher corresponding to |pattern|. - icu::RegexMatcher* GetMatcher(const base::StringPiece16& pattern); - - private: - ~AutofillRegexes() = default; - - // Maps patterns to their corresponding regex matchers. - std::map<std::u16string, std::unique_ptr<icu::RegexMatcher>, std::less<>> - matchers_; -}; - -icu::RegexMatcher* AutofillRegexes::GetMatcher( - const base::StringPiece16& pattern) { - auto it = matchers_.find(pattern); - if (it == matchers_.end()) { - const icu::UnicodeString icu_pattern(false, pattern.data(), - pattern.length()); - - UErrorCode status = U_ZERO_ERROR; - auto matcher = std::make_unique<icu::RegexMatcher>( - icu_pattern, UREGEX_CASE_INSENSITIVE, status); - DCHECK(U_SUCCESS(status)); - - auto result = matchers_.insert(std::make_pair(pattern, std::move(matcher))); - DCHECK(result.second); - it = result.first; - } - return it->second.get(); -} - -} // namespace - -namespace autofill { - -bool MatchesPattern(const base::StringPiece16& input, - const base::StringPiece16& pattern, - std::vector<std::u16string>* groups) { - if (input.size() > kMaxStringLength) - return false; - - static base::NoDestructor<AutofillRegexes> g_autofill_regexes; - static base::NoDestructor<base::Lock> g_lock; - base::AutoLock lock(*g_lock); - - icu::RegexMatcher* matcher = g_autofill_regexes->GetMatcher(pattern); - 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 && groups) { - int32_t matched_groups = matcher->groupCount(); - groups->resize(matched_groups + 1); - - for (int32_t i = 0; i < matched_groups + 1; ++i) { - icu::UnicodeString match_unicode = matcher->group(i, status); - DCHECK(U_SUCCESS(status)); - (*groups)[i] = base::i18n::UnicodeStringToString16(match_unicode); - } - } - - return matched; -} - -} // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_regexes.h b/chromium/components/autofill/core/browser/autofill_regexes.h deleted file mode 100644 index 75d72047e8f..00000000000 --- a/chromium/components/autofill/core/browser/autofill_regexes.h +++ /dev/null @@ -1,26 +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_BROWSER_AUTOFILL_REGEXES_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEXES_H_ - -#include <string> -#include <vector> - -#include "base/strings/string_piece.h" - -// Parsing utilities. -namespace autofill { - -// Case-insensitive regular expression matching. -// Returns true if |pattern| is found in |input|. -// If |groups| is non-null, it gets resized and the found capture groups -// are written into it. -bool MatchesPattern(const base::StringPiece16& input, - const base::StringPiece16& pattern, - std::vector<std::u16string>* groups = nullptr); - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEXES_H_ diff --git a/chromium/components/autofill/core/browser/autofill_suggestion_generator.cc b/chromium/components/autofill/core/browser/autofill_suggestion_generator.cc index 0dbe739c53f..20c60866af0 100644 --- a/chromium/components/autofill/core/browser/autofill_suggestion_generator.cc +++ b/chromium/components/autofill/core/browser/autofill_suggestion_generator.cc @@ -14,6 +14,7 @@ #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/data_model/autofill_offer_data.h" #include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/browser/data_model/iban.h" #include "components/autofill/core/browser/field_filler.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" @@ -180,6 +181,21 @@ AutofillSuggestionGenerator::GetSuggestionsForCreditCards( } // static +std::vector<Suggestion> AutofillSuggestionGenerator::GetSuggestionsForIBANs( + const std::vector<IBAN*>& ibans) { + std::vector<Suggestion> suggestions; + for (const IBAN* iban : ibans) { + Suggestion& suggestion = suggestions.emplace_back(iban->value()); + suggestion.frontend_id = POPUP_ITEM_ID_IBAN_ENTRY; + suggestion.payload = iban->guid(); + suggestion.main_text.value = iban->GetIdentifierStringForAutofillDisplay(); + if (!iban->nickname().empty()) + suggestion.label = iban->nickname(); + } + return suggestions; +} + +// static std::vector<Suggestion> AutofillSuggestionGenerator::GetPromoCodeSuggestionsFromPromoCodeOffers( const std::vector<const AutofillOfferData*>& promo_code_offers) { @@ -209,6 +225,13 @@ AutofillSuggestionGenerator::GetPromoCodeSuggestionsFromPromoCodeOffers( // one suggestion with a valid offer details url before adding the footer. DCHECK(suggestions.size() > 0); if (!footer_offer_details_url.is_empty()) { + // Add the footer separator since we will now have a footer in the offers + // suggestions popup. + suggestions.emplace_back(); + suggestions.back().frontend_id = POPUP_ITEM_ID_SEPARATOR; + + // Add the footer suggestion that navigates the user to the promo code + // details page in the offers suggestions popup. suggestions.emplace_back(l10n_util::GetStringUTF16( IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT)); Suggestion& suggestion = suggestions.back(); @@ -315,25 +338,10 @@ Suggestion AutofillSuggestionGenerator::CreateCreditCardSuggestion( suggestion.main_text = Suggestion::Text(credit_card.GetInfo(type, app_locale), Suggestion::Text::IsPrimary(true)); suggestion.icon = credit_card.CardIconStringForAutofillSuggestion(); - std::string backend_id = credit_card.guid(); + suggestion.payload = credit_card.guid(); suggestion.match = prefix_matched_suggestion ? Suggestion::PREFIX_MATCH : Suggestion::SUBSTRING_MATCH; - GURL card_art_url_for_virtual_card_option; - if (virtual_card_option && - credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) { - card_art_url_for_virtual_card_option = credit_card.card_art_url(); - } else if (virtual_card_option && - credit_card.record_type() == CreditCard::LOCAL_CARD) { - const CreditCard* server_duplicate_card = - GetServerCardForLocalCard(&credit_card); - DCHECK(server_duplicate_card); - card_art_url_for_virtual_card_option = - server_duplicate_card->card_art_url(); - backend_id = server_duplicate_card->guid(); - } - suggestion.payload = backend_id; - // Get the nickname for the card suggestion, which may not be the same as // the card's nickname if there are duplicates of the card on file. std::u16string suggestion_nickname = @@ -345,8 +353,8 @@ Suggestion AutofillSuggestionGenerator::CreateCreditCardSuggestion( base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory) ? 2 : 4; // If the value is the card number, the label is the expiration date. - // Otherwise the label is the card number, or if that is empty the - // cardholder name. The label should never repeat the value. + // Otherwise the label is the card number, or if that is empty the cardholder + // name. The label should never repeat the value. if (type.GetStorableType() == CREDIT_CARD_NUMBER) { suggestion.main_text = Suggestion::Text(credit_card.CardIdentifierStringForAutofillDisplay( @@ -391,40 +399,59 @@ Suggestion AutofillSuggestionGenerator::CreateCreditCardSuggestion( #endif } + // For virtual cards, use issuer's card art icon instead of network icon. if (virtual_card_option) { -#if BUILDFLAG(IS_ANDROID) - suggestion.custom_icon_url = credit_card.card_art_url(); -#endif // BUILDFLAG(IS_ANDROID) + GURL card_art_url_for_virtual_card_option; + if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) { + card_art_url_for_virtual_card_option = credit_card.card_art_url(); + } else if (credit_card.record_type() == CreditCard::LOCAL_CARD) { + const CreditCard* server_duplicate_card = + GetServerCardForLocalCard(&credit_card); + DCHECK(server_duplicate_card); + card_art_url_for_virtual_card_option = + server_duplicate_card->card_art_url(); + suggestion.payload = server_duplicate_card->guid(); + } suggestion.frontend_id = POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY; - suggestion.minor_text.value = suggestion.main_text.value; - suggestion.main_text.value = l10n_util::GetStringUTF16( - IDS_AUTOFILL_VIRTUAL_CARD_SUGGESTION_OPTION_VALUE); - suggestion.feature_for_iph = feature_engagement::kIPHAutofillVirtualCardSuggestionFeature.name; + // TODO(crbug.com/1344629): Update "Virtual card" label for other fields. + // For virtual cards, prefix "Virtual card" label to field suggestions. For + // card number field in a dropdown, show the "Virtual card" label below the + // card number for Metadata experiment. + if (!base::FeatureList::IsEnabled( + features::kAutofillEnableVirtualCardMetadata) || + type.GetStorableType() != CREDIT_CARD_NUMBER || + base::FeatureList::IsEnabled(features::kAutofillKeyboardAccessory)) { + suggestion.minor_text.value = suggestion.main_text.value; + suggestion.main_text.value = l10n_util::GetStringUTF16( + IDS_AUTOFILL_VIRTUAL_CARD_SUGGESTION_OPTION_VALUE); + } else { + suggestion.label = l10n_util::GetStringUTF16( + IDS_AUTOFILL_VIRTUAL_CARD_SUGGESTION_OPTION_VALUE); + } + +#if BUILDFLAG(IS_ANDROID) + suggestion.custom_icon_url = card_art_url_for_virtual_card_option; +#else gfx::Image* image = personal_data_->GetCreditCardArtImageForUrl( card_art_url_for_virtual_card_option); if (image) suggestion.custom_icon = *image; +#endif // BUILDFLAG(IS_ANDROID) } - +#if BUILDFLAG(IS_ANDROID) + // The card art icon should always be shown at the start of the suggestion. + suggestion.is_icon_at_start = true; +#endif // BUILDFLAG(IS_ANDROID) return suggestion; } bool AutofillSuggestionGenerator::ShouldShowVirtualCardOption( const CreditCard* candidate_card, const FormStructure& form_structure) const { - // If the form is an incomplete form and the incomplete form experiment is - // disabled, do not offer a virtual card option. We will likely not be able to - // fill in all information, and the user doesn't have the info either. - if (!IsCompleteCreditCardFormIncludingCvcField(form_structure) && - !base::FeatureList::IsEnabled( - features::kAutofillSuggestVirtualCardsOnIncompleteForm)) { - return false; - } - switch (candidate_card->record_type()) { case CreditCard::MASKED_SERVER_CARD: return candidate_card->virtual_card_enrollment_state() == diff --git a/chromium/components/autofill/core/browser/autofill_suggestion_generator.h b/chromium/components/autofill/core/browser/autofill_suggestion_generator.h index d2caa220b73..94a39c653b7 100644 --- a/chromium/components/autofill/core/browser/autofill_suggestion_generator.h +++ b/chromium/components/autofill/core/browser/autofill_suggestion_generator.h @@ -26,6 +26,7 @@ class AutofillType; class CreditCard; struct FormFieldData; class FormStructure; +class IBAN; class PersonalDataManager; struct Suggestion; @@ -57,6 +58,10 @@ class AutofillSuggestionGenerator { const std::string& app_locale, bool* should_display_gpay_logo); + // Generates suggestions for all available IBANs. + static std::vector<Suggestion> GetSuggestionsForIBANs( + const std::vector<IBAN*>& ibans); + // Converts the vector of promo code offers that is passed in to a vector of // suggestions that can be displayed to the user for a promo code field. static std::vector<Suggestion> GetPromoCodeSuggestionsFromPromoCodeOffers( @@ -89,6 +94,18 @@ class AutofillSuggestionGenerator { CreateCreditCardSuggestion_LocalCard); FRIEND_TEST_ALL_PREFIXES(AutofillSuggestionGeneratorTest, CreateCreditCardSuggestion_ServerCard); + FRIEND_TEST_ALL_PREFIXES( + AutofillSuggestionGeneratorTest, + CreateCreditCardSuggestion_PopupWithMetadata_VirtualCardNameField); + FRIEND_TEST_ALL_PREFIXES( + AutofillSuggestionGeneratorTest, + CreateCreditCardSuggestion_PopupWithMetadata_VirtualCardNumberField); + FRIEND_TEST_ALL_PREFIXES( + AutofillSuggestionGeneratorTest, + CreateCreditCardSuggestion_PopupWithMetadata_NonVirtualCardNameField); + FRIEND_TEST_ALL_PREFIXES( + AutofillSuggestionGeneratorTest, + CreateCreditCardSuggestion_PopupWithMetadata_NonVirtualCardNumberField); FRIEND_TEST_ALL_PREFIXES(AutofillSuggestionGeneratorTest, GetServerCardForLocalCard); FRIEND_TEST_ALL_PREFIXES(AutofillSuggestionGeneratorTest, diff --git a/chromium/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc b/chromium/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc index 222d4f876b6..dbc694e0c18 100644 --- a/chromium/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc @@ -6,6 +6,7 @@ #include "base/guid.h" #include "base/rand_util.h" +#include "base/strings/strcat.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" @@ -13,8 +14,9 @@ #include "components/autofill/core/browser/autofill_suggestion_generator.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/browser/data_model/iban.h" +#include "components/autofill/core/browser/form_structure_test_api.h" #include "components/autofill/core/browser/test_autofill_client.h" -#include "components/autofill/core/browser/test_form_structure.h" #include "components/autofill/core/browser/test_personal_data_manager.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/common/autofill_clock.h" @@ -55,6 +57,9 @@ class AutofillSuggestionGeneratorTest : public testing::Test { TestPersonalDataManager* personal_data() { return &personal_data_; } + protected: + base::test::ScopedFeatureList scoped_feature_list_; + private: base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::SYSTEM_TIME}; @@ -62,7 +67,6 @@ class AutofillSuggestionGeneratorTest : public testing::Test { TestAutofillClient autofill_client_; scoped_refptr<AutofillWebDataService> database_; TestPersonalDataManager personal_data_; - base::test::ScopedFeatureList scoped_feature_list_; }; TEST_F(AutofillSuggestionGeneratorTest, @@ -341,11 +345,180 @@ TEST_F(AutofillSuggestionGeneratorTest, CreateCreditCardSuggestion_LocalCard) { EXPECT_TRUE(real_card_suggestion.custom_icon.IsEmpty()); } +// Credit card name field suggestion with metadata for virtual cards in Autofill +// popup. +TEST_F(AutofillSuggestionGeneratorTest, + CreateCreditCardSuggestion_PopupWithMetadata_VirtualCardNameField) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillEnableVirtualCardMetadata); + + // Create a server card. + CreditCard server_card = test::GetMaskedServerCard(); + server_card.set_server_id("server_id1"); + server_card.set_guid("00000000-0000-0000-0000-000000000001"); + test::SetCreditCardInfo(&server_card, "Mojo Jojo", "4111111111111111", "04", + test::NextYear().c_str(), "1"); + server_card.SetNetworkForMaskedCard(kVisaCard); + + // Name field suggestion for virtual cards. + Suggestion virtual_card_name_field_suggestion = + suggestion_generator()->CreateCreditCardSuggestion( + server_card, AutofillType(CREDIT_CARD_NAME_FULL), + /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true, + ""); + + // "Virtual card" text is prefixed to the name. + EXPECT_EQ(virtual_card_name_field_suggestion.main_text.value, + u"Virtual card"); + EXPECT_EQ(virtual_card_name_field_suggestion.minor_text.value, u"Mojo Jojo"); + +#if BUILDFLAG(IS_ANDROID) + // For Android, the label is "Network ....1234". + EXPECT_EQ(virtual_card_name_field_suggestion.label, + base::StrCat({u"Visa ", internal::GetObfuscatedStringForCardDigits( + u"1111", 4)})); +#elif BUILDFLAG(IS_IOS) + // For IOS, the label is "....1234". + EXPECT_EQ(virtual_card_name_field_suggestion.label, + internal::GetObfuscatedStringForCardDigits(u"1111", 4)); +#else + // For Desktop, the label is the descriptive expiration date formatted as + // "Network ....1234, expires on mm/yy". + EXPECT_EQ( + virtual_card_name_field_suggestion.label, + base::StrCat({u"Visa ", + internal::GetObfuscatedStringForCardDigits(u"1111", 4), + u", expires on 04/", + base::UTF8ToUTF16(test::NextYear().substr(2))})); +#endif +} + +// Credit card number field suggestion with metadata for virtual cards in +// Autofill popup. +TEST_F(AutofillSuggestionGeneratorTest, + CreateCreditCardSuggestion_PopupWithMetadata_VirtualCardNumberField) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillEnableVirtualCardMetadata); + + // Create a server card. + CreditCard server_card = test::GetMaskedServerCard(); + server_card.set_server_id("server_id1"); + server_card.set_guid("00000000-0000-0000-0000-000000000001"); + test::SetCreditCardInfo(&server_card, "Mojo Jojo", "4111111111111111", "04", + test::NextYear().c_str(), "1"); + server_card.SetNetworkForMaskedCard(kVisaCard); + + // Card number field suggestion for virtual cards. + Suggestion virtual_card_number_field_suggestion = + suggestion_generator()->CreateCreditCardSuggestion( + server_card, AutofillType(CREDIT_CARD_NUMBER), + /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/true, + ""); + + // Only card number is displayed on the first line. + EXPECT_EQ(virtual_card_number_field_suggestion.main_text.value, + base::StrCat({u"Visa ", internal::GetObfuscatedStringForCardDigits( + u"1111", 4)})); + EXPECT_EQ(virtual_card_number_field_suggestion.minor_text.value, u""); + + // "Virtual card" is the label. + EXPECT_EQ(virtual_card_number_field_suggestion.label, u"Virtual card"); +} + +// Credit card name field suggestion with metadata for non-virtual cards in +// Autofill popup. +TEST_F(AutofillSuggestionGeneratorTest, + CreateCreditCardSuggestion_PopupWithMetadata_NonVirtualCardNameField) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillEnableVirtualCardMetadata); + + // Create a server card. + CreditCard server_card = test::GetMaskedServerCard(); + server_card.set_server_id("server_id1"); + server_card.set_guid("00000000-0000-0000-0000-000000000001"); + test::SetCreditCardInfo(&server_card, "Mojo Jojo", "4111111111111111", "04", + test::NextYear().c_str(), "1"); + server_card.SetNetworkForMaskedCard(kVisaCard); + + // Name field suggestion for non-virtual cards. + Suggestion real_card_name_field_suggestion = + suggestion_generator()->CreateCreditCardSuggestion( + server_card, AutofillType(CREDIT_CARD_NAME_FULL), + /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/false, + ""); + + // Only the name is displayed on the first line. + EXPECT_EQ(real_card_name_field_suggestion.main_text.value, u"Mojo Jojo"); + EXPECT_EQ(real_card_name_field_suggestion.minor_text.value, u""); + +#if BUILDFLAG(IS_ANDROID) + // For Android, the label is "Network ....1234". + EXPECT_EQ(real_card_name_field_suggestion.label, + base::StrCat({u"Visa ", internal::GetObfuscatedStringForCardDigits( + u"1111", 4)})); +#elif BUILDFLAG(IS_IOS) + // For IOS, the label is "....1234". + EXPECT_EQ(real_card_name_field_suggestion.label, + internal::GetObfuscatedStringForCardDigits(u"1111", 4)); +#else + // For Desktop, the label is the descriptive expiration date formatted as + // "Network ....1234, expires on mm/yy". + EXPECT_EQ( + real_card_name_field_suggestion.label, + base::StrCat({u"Visa ", + internal::GetObfuscatedStringForCardDigits(u"1111", 4), + u", expires on 04/", + base::UTF8ToUTF16(test::NextYear().substr(2))})); +#endif +} + +// Credit card number field suggestion with metadata for non-virtual cards in +// Autofill popup. +TEST_F(AutofillSuggestionGeneratorTest, + CreateCreditCardSuggestion_PopupWithMetadata_NonVirtualCardNumberField) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillEnableVirtualCardMetadata); + + // Create a server card. + CreditCard server_card = test::GetMaskedServerCard(); + server_card.set_server_id("server_id1"); + server_card.set_guid("00000000-0000-0000-0000-000000000001"); + test::SetCreditCardInfo(&server_card, "Mojo Jojo", "4111111111111111", "04", + test::NextYear().c_str(), "1"); + server_card.SetNetworkForMaskedCard(kVisaCard); + + // Card number field suggestion for non-virtual cards. + Suggestion real_card_number_field_suggestion = + suggestion_generator()->CreateCreditCardSuggestion( + server_card, AutofillType(CREDIT_CARD_NUMBER), + /*prefix_matched_suggestion=*/false, /*virtual_card_option=*/false, + ""); + + // Only the card number is displayed on the first line. + EXPECT_EQ(real_card_number_field_suggestion.main_text.value, + base::StrCat({u"Visa ", internal::GetObfuscatedStringForCardDigits( + u"1111", 4)})); + EXPECT_EQ(real_card_number_field_suggestion.minor_text.value, u""); + +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) + // For mobile devices, the label is the expiration date formatted as mm/yy. + EXPECT_EQ( + real_card_number_field_suggestion.label, + base::StrCat({u"04/", base::UTF8ToUTF16(test::NextYear().substr(2))})); +#else + // For Desktop, the label is the descriptive expiration date formatted as + // "Expires on mm/yy". + EXPECT_EQ(real_card_number_field_suggestion.label, + base::StrCat({u"Expires on 04/", + base::UTF8ToUTF16(test::NextYear().substr(2))})); +#endif +} + TEST_F(AutofillSuggestionGeneratorTest, ShouldShowVirtualCardOption) { // Create a complete form. FormData credit_card_form; test::CreateTestCreditCardFormData(&credit_card_form, true, false); - TestFormStructure form_structure(credit_card_form); + FormStructure form_structure(credit_card_form); form_structure.DetermineHeuristicTypes(nullptr, nullptr); // Clear the heuristic types, and instead set the appropriate server types. std::vector<ServerFieldType> heuristic_types, server_types; @@ -353,7 +526,8 @@ TEST_F(AutofillSuggestionGeneratorTest, ShouldShowVirtualCardOption) { heuristic_types.push_back(UNKNOWN_TYPE); server_types.push_back(form_structure.field(i)->heuristic_type()); } - form_structure.SetFieldTypes(heuristic_types, server_types); + FormStructureTestApi(&form_structure) + .SetFieldTypes(heuristic_types, server_types); // Create a server card. CreditCard server_card = test::GetMaskedServerCard(); @@ -377,13 +551,6 @@ TEST_F(AutofillSuggestionGeneratorTest, ShouldShowVirtualCardOption) { EXPECT_TRUE(suggestion_generator()->ShouldShowVirtualCardOption( &local_card, form_structure)); - // Reset form to reset field storage types to mock as an incomplete form. - TestFormStructure incomplete_form_structure(credit_card_form); - - // If it is an incomplete form, it should return false; - EXPECT_FALSE(suggestion_generator()->ShouldShowVirtualCardOption( - &server_card, incomplete_form_structure)); - // Reset server card virtual card enrollment state. server_card.set_virtual_card_enrollment_state( CreditCard::VirtualCardEnrollmentState::UNSPECIFIED); @@ -404,6 +571,53 @@ TEST_F(AutofillSuggestionGeneratorTest, ShouldShowVirtualCardOption) { &local_card, form_structure)); } +TEST_F(AutofillSuggestionGeneratorTest, GetIBANSuggestions) { + std::vector<IBAN*> ibans; + + IBAN iban0(base::GenerateGUID()); + iban0.set_value(u"CH56 0483 5012 3456 7800 9"); + iban0.set_nickname(u"My doctor's IBAN"); + ibans.push_back(&iban0); + + IBAN iban1(base::GenerateGUID()); + iban1.set_value(u"DE91 1000 0000 0123 4567 89"); + iban1.set_nickname(u"My brother's IBAN"); + ibans.push_back(&iban1); + + IBAN iban2(base::GenerateGUID()); + iban2.set_value(u"GR96 0810 0010 0000 0123 4567 890"); + iban2.set_nickname(u"My teacher's IBAN"); + ibans.push_back(&iban2); + + IBAN iban3(base::GenerateGUID()); + iban3.set_value(u"PK70 BANK 0000 1234 5678 9000"); + ibans.push_back(&iban3); + + std::vector<Suggestion> iban_suggestions = + AutofillSuggestionGenerator::GetSuggestionsForIBANs(ibans); + EXPECT_TRUE(iban_suggestions.size() == 4); + + EXPECT_EQ(iban_suggestions[0].main_text.value, + u"CH" + iban0.RepeatEllipsisForTesting(4) + u"9"); + EXPECT_EQ(iban_suggestions[0].label, u"My doctor's IBAN"); + EXPECT_EQ(iban_suggestions[0].frontend_id, POPUP_ITEM_ID_IBAN_ENTRY); + + EXPECT_EQ(iban_suggestions[1].main_text.value, + u"DE" + iban1.RepeatEllipsisForTesting(4) + u"89"); + EXPECT_EQ(iban_suggestions[1].label, u"My brother's IBAN"); + EXPECT_EQ(iban_suggestions[1].frontend_id, POPUP_ITEM_ID_IBAN_ENTRY); + + EXPECT_EQ(iban_suggestions[2].main_text.value, + u"GR" + iban2.RepeatEllipsisForTesting(5) + u"890"); + EXPECT_EQ(iban_suggestions[2].label, u"My teacher's IBAN"); + EXPECT_EQ(iban_suggestions[2].frontend_id, POPUP_ITEM_ID_IBAN_ENTRY); + + EXPECT_EQ(iban_suggestions[3].main_text.value, + u"PK" + iban3.RepeatEllipsisForTesting(4) + u"9000"); + EXPECT_EQ(iban_suggestions[3].label, u""); + EXPECT_EQ(iban_suggestions[3].frontend_id, POPUP_ITEM_ID_IBAN_ENTRY); +} + TEST_F(AutofillSuggestionGeneratorTest, GetPromoCodeSuggestionsFromPromoCodeOffers_ValidPromoCodes) { std::vector<const AutofillOfferData*> promo_code_offers; @@ -433,26 +647,28 @@ TEST_F(AutofillSuggestionGeneratorTest, std::vector<Suggestion> promo_code_suggestions = AutofillSuggestionGenerator::GetPromoCodeSuggestionsFromPromoCodeOffers( promo_code_offers); - EXPECT_TRUE(promo_code_suggestions.size() == 3); + EXPECT_TRUE(promo_code_suggestions.size() == 4); EXPECT_EQ(promo_code_suggestions[0].main_text.value, u"test_promo_code_1"); EXPECT_EQ(promo_code_suggestions[0].label, u"test_value_prop_text_1"); - EXPECT_EQ(absl::get<std::string>(promo_code_suggestions[0].payload), "1"); + EXPECT_EQ(promo_code_suggestions[0].GetPayload<std::string>(), "1"); EXPECT_EQ(promo_code_suggestions[0].frontend_id, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY); EXPECT_EQ(promo_code_suggestions[1].main_text.value, u"test_promo_code_2"); EXPECT_EQ(promo_code_suggestions[1].label, u"test_value_prop_text_2"); - EXPECT_EQ(absl::get<std::string>(promo_code_suggestions[1].payload), "2"); + EXPECT_EQ(promo_code_suggestions[1].GetPayload<std::string>(), "2"); EXPECT_EQ(promo_code_suggestions[1].frontend_id, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY); - EXPECT_EQ(promo_code_suggestions[2].main_text.value, + EXPECT_EQ(promo_code_suggestions[2].frontend_id, POPUP_ITEM_ID_SEPARATOR); + + EXPECT_EQ(promo_code_suggestions[3].main_text.value, l10n_util::GetStringUTF16( IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT)); - EXPECT_EQ(absl::get<GURL>(promo_code_suggestions[2].payload), + EXPECT_EQ(promo_code_suggestions[3].GetPayload<GURL>(), offer1.GetOfferDetailsUrl().spec()); - EXPECT_EQ(promo_code_suggestions[2].frontend_id, + EXPECT_EQ(promo_code_suggestions[3].frontend_id, POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS); } diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.cc b/chromium/components/autofill/core/browser/autofill_test_utils.cc index ec8485a3b14..8d74afe8ba5 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.cc +++ b/chromium/components/autofill/core/browser/autofill_test_utils.cc @@ -17,6 +17,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/data_model/credit_card_test_api.h" +#include "components/autofill/core/browser/data_model/iban.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/randomized_encoder.h" #include "components/autofill/core/browser/webdata/autofill_table.h" @@ -99,6 +100,12 @@ FieldGlobalId MakeFieldGlobalId(RandomizeFrame randomize) { return {MakeLocalFrameToken(randomize), MakeFieldRendererId()}; } +FormData WithoutValues(FormData form) { + for (FormFieldData& field : form.fields) + field.value.clear(); + return form; +} + void SetFormGroupValues(FormGroup& form_group, const std::vector<FormGroupValue>& values) { for (const auto& value : values) { @@ -474,6 +481,13 @@ AutofillProfile GetServerProfile2() { return profile; } +IBAN GetIBAN() { + IBAN iban(base::GenerateGUID()); + iban.set_value(u"DE91 1000 0000 0123 4567 89"); + iban.set_nickname(u"Nickname for Iban"); + return iban; +} + CreditCard GetCreditCard() { CreditCard credit_card(base::GenerateGUID(), kEmptyOrigin); SetCreditCardInfo(&credit_card, "Test User", "4111111111111111" /* Visa */, @@ -559,15 +573,6 @@ CreditCard GetMaskedServerCardWithNickname() { return credit_card; } -CreditCard GetMaskedServerCardWithInvalidNickname() { - CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "c789"); - test::SetCreditCardInfo(&credit_card, "Test user", "1111" /* Visa */, - NextMonth().c_str(), NextYear().c_str(), "1"); - credit_card.SetNetworkForMaskedCard(kVisaCard); - credit_card.SetNickname(u"Invalid nickname which is too long"); - return credit_card; -} - CreditCard GetFullServerCard() { CreditCard credit_card(CreditCard::FULL_SERVER_CARD, "c123"); test::SetCreditCardInfo(&credit_card, "Full Carter", diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.h b/chromium/components/autofill/core/browser/autofill_test_utils.h index bbeea1f9057..ecb2ada072f 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.h +++ b/chromium/components/autofill/core/browser/autofill_test_utils.h @@ -14,6 +14,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/data_model/credit_card_cloud_token_data.h" +#include "components/autofill/core/browser/data_model/iban.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/proto/api_v1.pb.h" @@ -92,6 +93,9 @@ FormGlobalId MakeFormGlobalId( FieldGlobalId MakeFieldGlobalId( RandomizeFrame randomize_frame = RandomizeFrame(false)); +// Returns a copy of `form` with cleared values. +FormData WithoutValues(FormData form); + // Helper function to set values and verification statuses to a form group. void SetFormGroupValues(FormGroup& form_group, const std::vector<FormGroupValue>& values); @@ -202,6 +206,9 @@ AutofillProfile GetServerProfile(); // Returns a server profile full of dummy info, different to the above. AutofillProfile GetServerProfile2(); +// Returns an IBAN full of dummy info. +IBAN GetIBAN(); + // Returns a credit card full of dummy info. CreditCard GetCreditCard(); @@ -221,7 +228,6 @@ CreditCard GetMaskedServerCardWithNonLegacyId(); CreditCard GetMaskedServerCardWithLegacyId(); CreditCard GetMaskedServerCardAmex(); CreditCard GetMaskedServerCardWithNickname(); -CreditCard GetMaskedServerCardWithInvalidNickname(); // Returns a full server card full of dummy info. CreditCard GetFullServerCard(); diff --git a/chromium/components/autofill/core/browser/autofill_type.cc b/chromium/components/autofill/core/browser/autofill_type.cc index ec58ca421cb..fa7224ea415 100644 --- a/chromium/components/autofill/core/browser/autofill_type.cc +++ b/chromium/components/autofill/core/browser/autofill_type.cc @@ -24,14 +24,6 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case NAME_FULL_WITH_HONORIFIC_PREFIX: return FieldTypeGroup::kName; - case NAME_BILLING_FIRST: - case NAME_BILLING_MIDDLE: - case NAME_BILLING_LAST: - case NAME_BILLING_MIDDLE_INITIAL: - case NAME_BILLING_FULL: - case NAME_BILLING_SUFFIX: - return FieldTypeGroup::kNameBilling; - case EMAIL_ADDRESS: case USERNAME_AND_EMAIL_ADDRESS: return FieldTypeGroup::kEmail; @@ -48,13 +40,6 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case PHONE_HOME_EXTENSION: return FieldTypeGroup::kPhoneHome; - case PHONE_BILLING_NUMBER: - case PHONE_BILLING_CITY_CODE: - case PHONE_BILLING_COUNTRY_CODE: - case PHONE_BILLING_CITY_AND_NUMBER: - case PHONE_BILLING_WHOLE_NUMBER: - return FieldTypeGroup::kPhoneBilling; - case ADDRESS_HOME_LINE1: case ADDRESS_HOME_LINE2: case ADDRESS_HOME_LINE3: @@ -78,19 +63,6 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case ADDRESS_HOME_FLOOR: return FieldTypeGroup::kAddressHome; - case ADDRESS_BILLING_LINE1: - case ADDRESS_BILLING_LINE2: - case ADDRESS_BILLING_LINE3: - case ADDRESS_BILLING_APT_NUM: - case ADDRESS_BILLING_CITY: - case ADDRESS_BILLING_STATE: - case ADDRESS_BILLING_ZIP: - case ADDRESS_BILLING_COUNTRY: - case ADDRESS_BILLING_STREET_ADDRESS: - case ADDRESS_BILLING_SORTING_CODE: - case ADDRESS_BILLING_DEPENDENT_LOCALITY: - return FieldTypeGroup::kAddressBilling; - case CREDIT_CARD_NAME_FULL: case CREDIT_CARD_NAME_FIRST: case CREDIT_CARD_NAME_LAST: @@ -107,11 +79,6 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case COMPANY_NAME: return FieldTypeGroup::kCompany; - case MERCHANT_PROMO_CODE: - // TODO(crbug/1190334): Create new field type group kMerchantPromoCode. - // (This involves updating many switch statements.) - return FieldTypeGroup::kNoGroup; - case PASSWORD: case ACCOUNT_CREATION_PASSWORD: case NOT_ACCOUNT_CREATION_PASSWORD: @@ -127,14 +94,12 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case NO_SERVER_DATA: case EMPTY_TYPE: case AMBIGUOUS_TYPE: - case PHONE_FAX_NUMBER: - case PHONE_FAX_CITY_CODE: - case PHONE_FAX_COUNTRY_CODE: - case PHONE_FAX_CITY_AND_NUMBER: - case PHONE_FAX_WHOLE_NUMBER: case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: + case MERCHANT_PROMO_CODE: + case IBAN_VALUE: case UPI_VPA: + case CREDIT_CARD_STANDALONE_VERIFICATION_CODE: return FieldTypeGroup::kNoGroup; case MAX_VALID_FIELD_TYPE: @@ -146,7 +111,7 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case BIRTHDATE_DAY: case BIRTHDATE_MONTH: - case BIRTHDATE_YEAR_4_DIGITS: + case BIRTHDATE_4_DIGIT_YEAR: return FieldTypeGroup::kBirthdateField; case PRICE: @@ -222,6 +187,11 @@ FieldTypeGroup GroupTypeOfHtmlFieldType(HtmlFieldType field_type, case HTML_TYPE_EMAIL: return FieldTypeGroup::kEmail; + case HTML_TYPE_BIRTHDATE_DAY: + case HTML_TYPE_BIRTHDATE_MONTH: + case HTML_TYPE_BIRTHDATE_YEAR: + return FieldTypeGroup::kBirthdateField; + case HTML_TYPE_UPI_VPA: // TODO(crbug/702223): Add support for UPI-VPA. return FieldTypeGroup::kNoGroup; @@ -232,6 +202,9 @@ FieldTypeGroup GroupTypeOfHtmlFieldType(HtmlFieldType field_type, case HTML_TYPE_MERCHANT_PROMO_CODE: return FieldTypeGroup::kNoGroup; + case HTML_TYPE_IBAN: + return FieldTypeGroup::kNoGroup; + case HTML_TYPE_UNSPECIFIED: case HTML_TYPE_UNRECOGNIZED: return FieldTypeGroup::kNoGroup; @@ -264,80 +237,8 @@ bool AutofillType::IsUnknown() const { } ServerFieldType AutofillType::GetStorableType() const { - // Map billing types to the equivalent non-billing types. - switch (server_type_) { - case ADDRESS_BILLING_LINE1: - return ADDRESS_HOME_LINE1; - - case ADDRESS_BILLING_LINE2: - return ADDRESS_HOME_LINE2; - - case ADDRESS_BILLING_LINE3: - return ADDRESS_HOME_LINE3; - - case ADDRESS_BILLING_APT_NUM: - return ADDRESS_HOME_APT_NUM; - - case ADDRESS_BILLING_CITY: - return ADDRESS_HOME_CITY; - - case ADDRESS_BILLING_STATE: - return ADDRESS_HOME_STATE; - - case ADDRESS_BILLING_ZIP: - return ADDRESS_HOME_ZIP; - - case ADDRESS_BILLING_COUNTRY: - return ADDRESS_HOME_COUNTRY; - - case PHONE_BILLING_WHOLE_NUMBER: - return PHONE_HOME_WHOLE_NUMBER; - - case PHONE_BILLING_NUMBER: - return PHONE_HOME_NUMBER; - - case PHONE_BILLING_CITY_CODE: - return PHONE_HOME_CITY_CODE; - - case PHONE_BILLING_COUNTRY_CODE: - return PHONE_HOME_COUNTRY_CODE; - - case PHONE_BILLING_CITY_AND_NUMBER: - return PHONE_HOME_CITY_AND_NUMBER; - - case NAME_BILLING_FIRST: - return NAME_FIRST; - - case NAME_BILLING_MIDDLE: - return NAME_MIDDLE; - - case NAME_BILLING_LAST: - return NAME_LAST; - - case NAME_BILLING_MIDDLE_INITIAL: - return NAME_MIDDLE_INITIAL; - - case NAME_BILLING_FULL: - return NAME_FULL; - - case NAME_BILLING_SUFFIX: - return NAME_SUFFIX; - - case ADDRESS_BILLING_STREET_ADDRESS: - return ADDRESS_HOME_STREET_ADDRESS; - - case ADDRESS_BILLING_SORTING_CODE: - return ADDRESS_HOME_SORTING_CODE; - - case ADDRESS_BILLING_DEPENDENT_LOCALITY: - return ADDRESS_HOME_DEPENDENT_LOCALITY; - - case UNKNOWN_TYPE: - break; // Try to parse HTML types instead. - - default: - return server_type_; - } + if (server_type_ != UNKNOWN_TYPE) + return server_type_; switch (html_type_) { case HTML_TYPE_UNSPECIFIED: @@ -433,9 +334,13 @@ ServerFieldType AutofillType::GetStorableType() const { return PHONE_HOME_CITY_CODE; case HTML_TYPE_TEL_LOCAL: + return PHONE_HOME_NUMBER; + case HTML_TYPE_TEL_LOCAL_PREFIX: + return PHONE_HOME_NUMBER_PREFIX; + case HTML_TYPE_TEL_LOCAL_SUFFIX: - return PHONE_HOME_NUMBER; + return PHONE_HOME_NUMBER_SUFFIX; case HTML_TYPE_TEL_EXTENSION: return PHONE_HOME_EXTENSION; @@ -443,6 +348,13 @@ ServerFieldType AutofillType::GetStorableType() const { case HTML_TYPE_EMAIL: return EMAIL_ADDRESS; + case HTML_TYPE_BIRTHDATE_DAY: + return BIRTHDATE_DAY; + case HTML_TYPE_BIRTHDATE_MONTH: + return BIRTHDATE_MONTH; + case HTML_TYPE_BIRTHDATE_YEAR: + return BIRTHDATE_4_DIGIT_YEAR; + case HTML_TYPE_ADDITIONAL_NAME_INITIAL: return NAME_MIDDLE_INITIAL; @@ -466,6 +378,7 @@ ServerFieldType AutofillType::GetStorableType() const { case HTML_TYPE_TRANSACTION_CURRENCY: case HTML_TYPE_ONE_TIME_CODE: case HTML_TYPE_MERCHANT_PROMO_CODE: + case HTML_TYPE_IBAN: return UNKNOWN_TYPE; case HTML_TYPE_UNRECOGNIZED: diff --git a/chromium/components/autofill/core/browser/autofill_type_unittest.cc b/chromium/components/autofill/core/browser/autofill_type_unittest.cc index 9c4a5bb3a9d..ef7b9e64474 100644 --- a/chromium/components/autofill/core/browser/autofill_type_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_type_unittest.cc @@ -29,16 +29,6 @@ TEST(AutofillTypeTest, ServerFieldTypes) { EXPECT_EQ(PHONE_HOME_NUMBER, phone.GetStorableType()); EXPECT_EQ(FieldTypeGroup::kPhoneHome, phone.group()); - // Billing type. - AutofillType billing_address(ADDRESS_BILLING_LINE1); - EXPECT_EQ(ADDRESS_HOME_LINE1, billing_address.GetStorableType()); - EXPECT_EQ(FieldTypeGroup::kAddressBilling, billing_address.group()); - - // Last value, to check any offset errors. - AutofillType last(NAME_BILLING_SUFFIX); - EXPECT_EQ(NAME_SUFFIX, last.GetStorableType()); - EXPECT_EQ(FieldTypeGroup::kNameBilling, last.group()); - // Boundary (error) condition. AutofillType boundary(MAX_VALID_FIELD_TYPE); EXPECT_EQ(UNKNOWN_TYPE, boundary.GetStorableType()); diff --git a/chromium/components/autofill/core/browser/browser_autofill_manager.cc b/chromium/components/autofill/core/browser/browser_autofill_manager.cc index 2dad433c7de..4d123df8304 100644 --- a/chromium/components/autofill/core/browser/browser_autofill_manager.cc +++ b/chromium/components/autofill/core/browser/browser_autofill_manager.cc @@ -52,7 +52,6 @@ #include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_external_delegate.h" #include "components/autofill/core/browser/autofill_field.h" -#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/browser/autofill_suggestion_generator.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/browser_autofill_manager_test_delegate.h" @@ -77,6 +76,7 @@ #include "components/autofill/core/browser/suggestions_context.h" #include "components/autofill/core/browser/ui/popup_item_ids.h" #include "components/autofill/core/browser/validation.h" +#include "components/autofill/core/common/autocomplete_parsing_util.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_data_validation.h" @@ -85,6 +85,7 @@ #include "components/autofill/core/common/autofill_internals/logging_scope.h" #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_prefs.h" +#include "components/autofill/core/common/autofill_regexes.h" #include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_data_predictions.h" @@ -203,6 +204,41 @@ void LogLanguageMetrics(const translate::LanguageState* language_state) { } } +void LogAutocompletePredictionCollisionTypeMetrics( + const FormStructure& form_structure) { + for (size_t i = 0; i < form_structure.field_count(); i++) { + const AutofillField* field = form_structure.field(i); + auto heuristic_type = field->heuristic_type(); + auto server_type = field->server_type(); + + auto prediction_state = AutofillMetrics::PredictionState::kNone; + if (IsFillableFieldType(heuristic_type)) { + prediction_state = IsFillableFieldType(server_type) + ? AutofillMetrics::PredictionState::kBoth + : AutofillMetrics::PredictionState::kHeuristics; + } else if (IsFillableFieldType(server_type)) { + prediction_state = AutofillMetrics::PredictionState::kServer; + } + + // An unparsable autocomplete attribute is treated like kNone. + auto autocomplete_state = AutofillMetrics::AutocompleteState::kNone; + if (ShouldIgnoreAutocompleteAttribute(field->autocomplete_attribute)) { + autocomplete_state = AutofillMetrics::AutocompleteState::kOff; + } else if (auto autocomplete = ParseAutocompleteAttribute(*field)) { + autocomplete_state = autocomplete->field_type != HTML_TYPE_UNRECOGNIZED + ? AutofillMetrics::AutocompleteState::kValid + : AutofillMetrics::AutocompleteState::kGarbage; + } + + AutofillMetrics::LogAutocompletePredictionCollisionState( + prediction_state, autocomplete_state); + if (autocomplete_state == AutofillMetrics::AutocompleteState::kGarbage) { + AutofillMetrics::LogAutocompletePredictionCollisionTypes(server_type, + heuristic_type); + } + } +} + // Finds the first field in |form_structure| with |field.value|=|value|. AutofillField* FindFirstFieldWithValue(const FormStructure& form_structure, const std::u16string& value) { @@ -371,16 +407,6 @@ size_t TypeValueFormFillingLimit(ServerFieldType field_type) { } } -// Logs the reason for suppressing autofill suggestions to -// chrome://autofill-internals. -void LogSuppressReason(LogManager* log_manager, const std::string& reason) { - if (!log_manager) - return; - log_manager->Log() << LoggingScope::kFilling - << LogMessage::kSuggestionSuppressed - << " Reason: " << reason; -} - } // namespace BrowserAutofillManager::FillingContext::FillingContext( @@ -419,7 +445,7 @@ BrowserAutofillManager::BrowserAutofillManager( enable_download_manager), external_delegate_( std::make_unique<AutofillExternalDelegate>(this, driver)), - touch_to_fill_delegate_(std::make_unique<TouchToFillDelegate>()), + touch_to_fill_delegate_(std::make_unique<TouchToFillDelegateImpl>(this)), app_locale_(app_locale), personal_data_(client->GetPersonalDataManager()), field_filler_(app_locale, client->GetAddressNormalizer()), @@ -454,6 +480,10 @@ BrowserAutofillManager::~BrowserAutofillManager() { single_field_form_fill_router_->CancelPendingQueries(this); } +base::WeakPtr<AutofillManager> BrowserAutofillManager::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + AutofillOfferManager* BrowserAutofillManager::GetOfferManager() { return offer_manager_; } @@ -644,14 +674,10 @@ bool BrowserAutofillManager::ShouldParseForms( has_logged_autofill_enabled_ = true; } - // TODO(crbug.com/1293341): Enable the experiment by default. - // The placement of the IsEnabled() call is chosen very intentionally. - // Only users with disabled autofill will go into the control or experiment - // group. - return autofill_enabled || - (base::FeatureList::IsEnabled( - features::kAutofillFixServerQueriesIfPasswordManagerIsEnabled) && - password_manager_enabled); + // Enable the parsing also for the password manager, so that we fetch server + // classifications if the password manager is enabled but autofill is + // disabled. + return autofill_enabled || password_manager_enabled; } void BrowserAutofillManager::OnFormSubmittedImpl(const FormData& form, @@ -659,13 +685,11 @@ void BrowserAutofillManager::OnFormSubmittedImpl(const FormData& form, SubmissionSource source) { base::UmaHistogramEnumeration("Autofill.FormSubmission.PerProfileType", client()->GetProfileType()); - if (log_manager()) { - log_manager()->Log() << LoggingScope::kSubmission - << LogMessage::kFormSubmissionDetected << Br{} - << "known_success: " << known_success << Br{} - << "source: " << SubmissionSourceToString(source) - << Br{} << form; - } + LOG_AF(log_manager()) << LoggingScope::kSubmission + << LogMessage::kFormSubmissionDetected << Br{} + << "known_success: " << known_success << Br{} + << "source: " << SubmissionSourceToString(source) + << Br{} << form; // Always upload page language metrics. LogLanguageMetrics(client()->GetLanguageState()); @@ -681,6 +705,9 @@ void BrowserAutofillManager::OnFormSubmittedImpl(const FormData& form, return; } + // Log metrics about the autocomplete attribute usage in the submitted form. + LogAutocompletePredictionCollisionTypeMetrics(*submitted_form); + // Log interaction time metrics for the ablation study. if (!initial_interaction_timestamp_.is_null()) { base::TimeDelta time_from_interaction_to_submission = @@ -707,8 +734,12 @@ void BrowserAutofillManager::OnFormSubmittedImpl(const FormData& form, form_for_autocomplete.fields[i].should_autocomplete = false; } + // If the field was edited by the user and there existed an autofillable + // value for the field, log whether the value on submission is same as the + // autofillable value. if (submitted_form->field(i) - ->value_not_autofilled_over_existing_value_hash()) { + ->value_not_autofilled_over_existing_value_hash() && + (submitted_form->field(i)->properties_mask & kUserTyped)) { // Compare and record if the currently filled value is same as the // non-empty value that was to be autofilled in the field. AutofillMetrics:: @@ -946,10 +977,10 @@ bool BrowserAutofillManager::IsFormNonSecure(const FormData& form) const { } void BrowserAutofillManager::OnAskForValuesToFillImpl( - int query_id, const FormData& form, const FormFieldData& field, const gfx::RectF& transformed_box, + int query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible) { if (base::FeatureList::IsEnabled(features::kAutofillDisableFilling)) { @@ -972,17 +1003,25 @@ void BrowserAutofillManager::OnAskForValuesToFillImpl( single_field_form_fill_router_->CancelPendingQueries(this); external_delegate_->OnSuggestionsReturned(query_id, suggestions, autoselect_first_suggestion); - LogSuppressReason(log_manager(), "Ablation experiment"); + LOG_AF(log_manager()) + << LoggingScope::kFilling << LogMessage::kSuggestionSuppressed + << " Reason: Ablation experiment"; return; case SuppressReason::kInsecureForm: - LogSuppressReason(log_manager(), "Insecure form"); + LOG_AF(log_manager()) + << LoggingScope::kFilling << LogMessage::kSuggestionSuppressed + << " Reason: Insecure form"; return; case SuppressReason::kAutocompleteOff: - LogSuppressReason(log_manager(), "autocomplete=off"); + LOG_AF(log_manager()) + << LoggingScope::kFilling << LogMessage::kSuggestionSuppressed + << " Reason: autocomplete=off"; return; case SuppressReason::kAutocompleteUnrecognized: - LogSuppressReason(log_manager(), "autocomplete=unrecognized"); + LOG_AF(log_manager()) + << LoggingScope::kFilling << LogMessage::kSuggestionSuppressed + << " Reason: autocomplete=unrecognized"; return; } @@ -1003,18 +1042,18 @@ void BrowserAutofillManager::OnAskForValuesToFillImpl( } } - auto ShouldOfferAutocomplete = [&] { - // Do not offer autocomplete if one of the following: + auto ShouldOfferSingleFieldFormFill = [&] { + // Do not offer single field form fill if one of the following is true: // * There are already suggestions. // * Credit card sign-in promo is offered. - // * Autocomplete for the field is disabled. - if (!suggestions.empty() || ShouldShowCreditCardSigninPromo(form, field) || - !field.should_autocomplete) { + if (!suggestions.empty() || ShouldShowCreditCardSigninPromo(form, field)) return false; - } - // Do not offer autocomplete suggestions for credit card number, cvc and - // expiration date related fields. + // Do not offer single field form fill suggestions for credit card number, + // cvc, and expiration date related fields. Standalone cvc fields (used to + // re-authenticate the use of a credit card the website has on file) will be + // handled separately because those have the field type + // CREDIT_CARD_STANDALONE_VERIFICATION_CODE. ServerFieldType server_type = context.focused_field ? context.focused_field->Type().GetStorableType() : UNKNOWN_TYPE; @@ -1024,9 +1063,9 @@ void BrowserAutofillManager::OnAskForValuesToFillImpl( return false; } - // Do not offer autocomplete suggestions if popups are suppressed due to an - // unrecognized autocomplete attribute. Note that in the context of - // Autofill, the popup for credit card related fields is not getting + // Do not offer single field form fill suggestions if popups are suppressed + // due to an unrecognized autocomplete attribute. Note that in the context + // of Autofill, the popup for credit card related fields is not getting // suppressed due to an unrecognized autocomplete attribute. if (context.suppress_reason == SuppressReason::kAutocompleteUnrecognized) { return false; @@ -1045,20 +1084,25 @@ void BrowserAutofillManager::OnAskForValuesToFillImpl( return true; }; - if (ShouldOfferAutocomplete()) { + if (ShouldOfferSingleFieldFormFill()) { // Suggestions come back asynchronously, so the SingleFieldFormFillRouter // will handle sending the results back to the renderer. - single_field_form_fill_router_->OnGetSingleFieldSuggestions( - query_id, client()->IsAutocompleteEnabled(), - autoselect_first_suggestion, field.name, field.value, - field.form_control_type, weak_ptr_factory_.GetWeakPtr(), context); - return; + bool handled_by_single_field_form_filler = + single_field_form_fill_router_->OnGetSingleFieldSuggestions( + query_id, client()->IsAutocompleteEnabled(), + autoselect_first_suggestion, field, weak_ptr_factory_.GetWeakPtr(), + context); + if (handled_by_single_field_form_filler) + return; } single_field_form_fill_router_->CancelPendingQueries(this); - if (touch_to_fill_eligible && - touch_to_fill_delegate_->TryToShowTouchToFill(query_id, form, field)) { - // Touch To Fill is shown. + if (touch_to_fill_delegate_->IsShowingTouchToFill() || + (touch_to_fill_eligible && + touch_to_fill_delegate_->TryToShowTouchToFill(query_id, form, field))) { + // Touch To Fill surface is shown, so abort showing regular Autofill UI. + // Now the flow is controlled by the |touch_to_fill_delegate_| instead + // of |external_delegate_|. return; } // Send Autofill suggestions (could be an empty list). @@ -1165,11 +1209,12 @@ void BrowserAutofillManager::FillOrPreviewForm( FillOrPreviewProfileForm(action, query_id, form, field, *profile); } -void BrowserAutofillManager::FillCreditCardForm(int query_id, - const FormData& form, - const FormFieldData& field, - const CreditCard& credit_card, - const std::u16string& cvc) { +void BrowserAutofillManager::FillCreditCardFormImpl( + const FormData& form, + const FormFieldData& field, + const CreditCard& credit_card, + const std::u16string& cvc, + int query_id) { if (!IsValidFormData(form) || !IsValidFormFieldData(field) || !driver()->RendererIsAvailable()) { return; @@ -1185,13 +1230,24 @@ void BrowserAutofillManager::FillCreditCardForm(int query_id, autofill_field); } -void BrowserAutofillManager::FillProfileForm(const AutofillProfile& profile, - const FormData& form, - const FormFieldData& field) { +void BrowserAutofillManager::FillProfileFormImpl( + const FormData& form, + const FormFieldData& field, + const AutofillProfile& profile) { FillOrPreviewProfileForm(mojom::RendererFormDataAction::kFill, /*query_id=*/kNoQueryId, form, field, profile); } +void BrowserAutofillManager::SetProfileFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) { + address_form_event_logger_->SetAutofillAssistantIntentForFilling(intent); +} + +void BrowserAutofillManager::SetCreditCardFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) { + credit_card_form_event_logger_->SetAutofillAssistantIntentForFilling(intent); +} + void BrowserAutofillManager::FillOrPreviewVirtualCardInformation( mojom::RendererFormDataAction action, const std::string& guid, @@ -1211,7 +1267,8 @@ void BrowserAutofillManager::FillOrPreviewVirtualCardInformation( } } -void BrowserAutofillManager::OnFocusNoLongerOnForm(bool had_interacted_form) { +void BrowserAutofillManager::OnFocusNoLongerOnFormImpl( + 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. @@ -1265,12 +1322,12 @@ void BrowserAutofillManager::OnSelectControlDidChangeImpl( // TODO(crbug.com/814961): Handle select control change. } -void BrowserAutofillManager::OnDidPreviewAutofillFormData() { +void BrowserAutofillManager::OnDidPreviewAutofillFormDataImpl() { if (test_delegate_) test_delegate_->DidPreviewFormData(); } -void BrowserAutofillManager::OnDidFillAutofillFormData( +void BrowserAutofillManager::OnDidFillAutofillFormDataImpl( const FormData& form, const TimeTicks timestamp) { if (test_delegate_) @@ -1346,12 +1403,13 @@ void BrowserAutofillManager::DidShowSuggestions(bool has_autofill_suggestions, } } -void BrowserAutofillManager::OnHidePopup() { +void BrowserAutofillManager::OnHidePopupImpl() { if (!IsAutofillEnabled()) return; single_field_form_fill_router_->CancelPendingQueries(this); client()->HideAutofillPopup(PopupHidingReason::kRendererEvent); + touch_to_fill_delegate_->HideTouchToFill(); } bool BrowserAutofillManager::GetDeletionConfirmationText( @@ -1470,12 +1528,14 @@ void BrowserAutofillManager::SetDataList( external_delegate_->SetCurrentDataListValues(values, labels); } -void BrowserAutofillManager::SelectFieldOptionsDidChange(const FormData& form) { - // Look for a cached version of the form. It will be a null pointer if none is - // found, which is fine. - FormStructure* cached_form = FindCachedFormByRendererId(form.global_id()); - - FormStructure* form_structure = ParseForm(form, cached_form); +void BrowserAutofillManager::OnSelectFieldOptionsDidChangeImpl( + const FormData& form) { + FormStructure* form_structure = FindCachedFormByRendererId(form.global_id()); + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + // If AutofillParseAsync is enabled, the form has just been parsed + // asynchronously if necessary. + form_structure = ParseForm(form, form_structure); + } if (!form_structure) return; @@ -1485,45 +1545,44 @@ void BrowserAutofillManager::SelectFieldOptionsDidChange(const FormData& form) { TriggerRefill(form); } -void BrowserAutofillManager::JavaScriptChangedAutofilledValue( +void BrowserAutofillManager::OnJavaScriptChangedAutofilledValueImpl( const FormData& form, const FormFieldData& field, const std::u16string& old_value) { // Log to chrome://autofill-internals that a field's value was set by // JavaScript. - if (log_manager()) { - auto StructureOfString = [](std::u16string str) { - for (auto& c : str) { - if (base::IsAsciiAlpha(c)) { - c = 'a'; - } else if (base::IsAsciiDigit(c)) { - c = '0'; - } else if (base::IsAsciiWhitespace(c)) { - c = ' '; - } else { - c = '$'; - } + auto StructureOfString = [](std::u16string str) { + for (auto& c : str) { + if (base::IsAsciiAlpha(c)) { + c = 'a'; + } else if (base::IsAsciiDigit(c)) { + c = '0'; + } else if (base::IsAsciiWhitespace(c)) { + c = ' '; + } else { + c = '$'; } - return str; - }; - std::string field_number = "unknown"; + } + return str; + }; + auto GetFieldNumber = [&]() { for (size_t i = 0; i < form.fields.size(); ++i) { - if (form.fields[i].global_id() == field.global_id()) { - field_number = base::StringPrintf("Field %zu", i); - } + if (form.fields[i].global_id() == field.global_id()) + return base::StringPrintf("Field %zu", i); } - LogBuffer change; - change << Tag{"div"} << Attrib{"class", "form"}; - change << field << Br{}; - change << "Old value structure: '" - << StructureOfString(old_value.substr(0, 80)) << "'" << Br{}; - change << "New value structure: '" - << StructureOfString(field.value.substr(0, 80)) << "'"; - log_manager()->Log() << LoggingScope::kWebsiteModifiedFieldValue - << LogMessage::kJavaScriptChangedAutofilledValue - << Br{} << Tag{"table"} << Tr{} << field_number - << std::move(change); - } + return std::string("unknown"); + }; + LogBuffer change(IsLoggingActive(log_manager())); + LOG_AF(change) << Tag{"div"} << Attrib{"class", "form"}; + LOG_AF(change) << field << Br{}; + LOG_AF(change) << "Old value structure: '" + << StructureOfString(old_value.substr(0, 80)) << "'" << Br{}; + LOG_AF(change) << "New value structure: '" + << StructureOfString(field.value.substr(0, 80)) << "'"; + LOG_AF(log_manager()) << LoggingScope::kWebsiteModifiedFieldValue + << LogMessage::kJavaScriptChangedAutofilledValue << Br{} + << Tag{"table"} << Tr{} << GetFieldNumber() + << std::move(change); MaybeTriggerRefillForExpirationDate(form, field, old_value); } @@ -1548,14 +1607,15 @@ void BrowserAutofillManager::MaybeTriggerRefillForExpirationDate( if (old_value == field.value) return; - const char16_t* kFormatRegEx = uR"(^(\d\d)(\s?[/-]?\s?)?(\d\d|\d\d\d\d)$)"; + static constexpr char16_t kFormatRegEx[] = + uR"(^(\d\d)(\s?[/-]?\s?)?(\d\d|\d\d\d\d)$)"; std::vector<std::u16string> old_groups; - if (!MatchesPattern(old_value, kFormatRegEx, &old_groups)) + if (!MatchesRegex<kFormatRegEx>(old_value, &old_groups)) return; DCHECK_EQ(old_groups.size(), 4u); std::vector<std::u16string> new_groups; - if (!MatchesPattern(field.value, kFormatRegEx, &new_groups)) + if (!MatchesRegex<kFormatRegEx>(field.value, &new_groups)) return; DCHECK_EQ(new_groups.size(), 4u); @@ -1619,16 +1679,22 @@ void BrowserAutofillManager::OnCreditCardFetched(CreditCardFetchResult result, card_art_image ? *card_art_image : gfx::Image()); } - FillCreditCardForm(credit_card_query_id_, credit_card_form_, - credit_card_field_, *credit_card, cvc); + // After a server card is fetched, save its instrument id. + client()->GetFormDataImporter()->SetFetchedCardInstrumentId( + credit_card->instrument_id()); + + FillCreditCardFormImpl(credit_card_form_, credit_card_field_, *credit_card, + cvc, credit_card_query_id_); if (credit_card->record_type() == CreditCard::FULL_SERVER_CARD || credit_card->record_type() == CreditCard::VIRTUAL_CARD) { credit_card_access_manager_->CacheUnmaskedCardInfo(*credit_card, cvc); } } -void BrowserAutofillManager::OnDidEndTextFieldEditing() { +void BrowserAutofillManager::OnDidEndTextFieldEditingImpl() { external_delegate_->DidEndTextFieldEditing(); + // Should not hide the Touch To Fill surface, since it is an overlay UI + // which ends editing. } bool BrowserAutofillManager::IsAutofillEnabled() const { @@ -1671,7 +1737,7 @@ void BrowserAutofillManager::UploadFormDataAsyncCallback( const TimeTicks& submission_time, bool observed_submission) { if (submitted_form->ShouldRunHeuristics() || - submitted_form->ShouldRunPromoCodeHeuristics() || + submitted_form->ShouldRunHeuristicsForSingleFieldForms() || submitted_form->ShouldBeQueried()) { submitted_form->LogQualityMetrics( submitted_form->form_parsed_timestamp(), interaction_time, @@ -1710,6 +1776,15 @@ void BrowserAutofillManager::Reset() { // save a card is shown after page navigation. ProcessPendingFormForUpload(); DCHECK(!pending_form_data_); + // {address, credit_card}_form_event_logger_ need to be reset before + // AutofillManager::Reset() because ~FormEventLoggerBase() uses + // form_interactions_ukm_logger_ that is created and assigned in + // AutofillManager::Reset(). The new form_interactions_ukm_logger_ instance + // is needed for constructing the new *form_event_logger_ instances which is + // why calling AutofillManager::Reset() after constructing *form_event_logger_ + // instances is not an option. + address_form_event_logger_.reset(); + credit_card_form_event_logger_.reset(); AutofillManager::Reset(); address_form_event_logger_ = std::make_unique<AddressFormEventLogger>( driver()->IsInAnyMainFrame(), form_interactions_ukm_logger(), @@ -1735,6 +1810,7 @@ void BrowserAutofillManager::Reset() { credit_card_action_ = mojom::RendererFormDataAction::kPreview; initial_interaction_timestamp_ = TimeTicks(); external_delegate_->Reset(); + touch_to_fill_delegate_->Reset(); filling_context_.clear(); form_interactions_counter_ = std::make_unique<FormInteractionsCounter>(); } @@ -1805,11 +1881,11 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( DCHECK(form_structure); DCHECK(autofill_field); - LogBuffer buffer; - buffer << "is credit card section: " << is_credit_card << Br{}; - buffer << "is refill: " << is_refill << Br{}; - buffer << *form_structure << Br{}; - buffer << Tag{"table"}; + LogBuffer buffer(IsLoggingActive(log_manager())); + LOG_AF(buffer) << "is credit card section: " << is_credit_card << Br{}; + LOG_AF(buffer) << "is refill: " << is_refill << Br{}; + LOG_AF(buffer) << *form_structure << Br{}; + LOG_AF(buffer) << Tag{"table"}; form_structure->RationalizePhoneNumbersInSection(autofill_field->section); @@ -1853,13 +1929,15 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( result.fields[i].section = form_structure->field(i)->section; if (form_structure->field(i)->section != autofill_field->section) { - buffer << Tr{} << field_number << "Skipped: not part of filled section"; + LOG_AF(buffer) << Tr{} << field_number + << "Skipped: not part of filled section"; continue; } if (form_structure->field(i)->only_fill_when_focused() && !form_structure->field(i)->SameFieldAs(field)) { - buffer << Tr{} << field_number << "Skipped: only fill when focused"; + LOG_AF(buffer) << Tr{} << field_number + << "Skipped: only fill when focused"; continue; } @@ -1879,7 +1957,7 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( ->LogHiddenRepresentationalFieldSkipDecision(*form_structure, *cached_field, skip); if (skip) { - buffer << Tr{} << field_number << "Skipped: invisible field"; + LOG_AF(buffer) << Tr{} << field_number << "Skipped: invisible field"; continue; } } @@ -1896,6 +1974,8 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( // case. if (base::FeatureList::IsEnabled( features::kAutofillPreventOverridingPrefilledValues)) { + form_structure->field(i) + ->set_value_not_autofilled_over_existing_value_hash(absl::nullopt); if (form.fields[i].form_control_type != "select-one" && !form.fields[i].value.empty() && !has_override && !FormFieldData::DeepEqual(form.fields[i], field)) { @@ -1907,18 +1987,16 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( optional_cvc ? *optional_cvc : kEmptyCvc, action, &unused_failure_to_fill); if (action == mojom::RendererFormDataAction::kFill && - !fill_value.empty() && fill_value != form.fields[i].value) { - // Save the value that was supposed to be autofilled for this field. + !fill_value.empty() && fill_value != form.fields[i].value && + !form_structure->field(i)->value.empty()) { + // Save the value that was supposed to be autofilled for this field if + // the field contained an initial value. form_structure->field(i) ->set_value_not_autofilled_over_existing_value_hash( base::FastHash(base::UTF16ToUTF8(fill_value))); } continue; } - - // Clear out the value in case the autofill happens for the field. - form_structure->field(i) - ->set_value_not_autofilled_over_existing_value_hash(absl::nullopt); } // Do not fill fields that have been edited by the user, except if the field @@ -1929,8 +2007,8 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( (!form.fields[i].value.empty() || !form_structure->field(i)->value.empty()) && !cached_field->SameFieldAs(field)) { - buffer << Tr{} << field_number - << "Skipped: don't fill user-filled fields"; + LOG_AF(buffer) << Tr{} << field_number + << "Skipped: don't fill user-filled fields"; continue; } @@ -1938,15 +2016,16 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( // when it's a refill. if (result.fields[i].is_autofilled && !cached_field->SameFieldAs(field) && !is_refill) { - buffer << Tr{} << field_number - << "Skipped: don't fill previously filled fields unless during a " - "refill"; + LOG_AF(buffer) + << Tr{} << field_number + << "Skipped: don't fill previously filled fields unless during a " + "refill"; continue; } if (field_group_type == FieldTypeGroup::kNoGroup) { - buffer << Tr{} << field_number - << "Skipped: field type has no fillable group"; + LOG_AF(buffer) << Tr{} << field_number + << "Skipped: field type has no fillable group"; continue; } @@ -1955,9 +2034,10 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( if (is_refill && !base::Contains(filling_context->type_groups_originally_filled, field_group_type)) { - buffer << Tr{} << field_number - << "Skipped: in a refill, only fields from the group that was " - "filled in the initial fill may be filled"; + LOG_AF(buffer) + << Tr{} << field_number + << "Skipped: in a refill, only fields from the group that was " + "filled in the initial fill may be filled"; continue; } @@ -1967,16 +2047,16 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( if (data_util::IsCreditCardExpirationType(field_type) && (is_credit_card && absl::get<const CreditCard*>(profile_or_credit_card) ->IsExpired(AutofillClock::Now()))) { - buffer << Tr{} << field_number - << "Skipped: don't fill expiration date of expired cards"; + LOG_AF(buffer) << Tr{} << field_number + << "Skipped: don't fill expiration date of expired cards"; continue; } // A field with a specific type is only allowed to be filled a limited // number of times given by |TypeValueFormFillingLimit(field_type)|. if (++type_count[field_type] > TypeValueFormFillingLimit(field_type)) { - buffer << Tr{} << field_number - << "Skipped: field-type filling-limit reached"; + LOG_AF(buffer) << Tr{} << field_number + << "Skipped: field-type filling-limit reached"; continue; } @@ -2018,16 +2098,17 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( bool has_value_after = !result.fields[i].value.empty(); bool is_autofilled_after = result.fields[i].is_autofilled; - buffer << Tr{} << field_number - << base::StringPrintf( - "Fillable - has value: %d->%d; autofilled: %d->%d. %s", - has_value_before, has_value_after, is_autofilled_before, - is_autofilled_after, failure_to_fill.c_str()); + LOG_AF(buffer) + << Tr{} << field_number + << base::StringPrintf( + "Fillable - has value: %d->%d; autofilled: %d->%d. %s", + has_value_before, has_value_after, is_autofilled_before, + is_autofilled_after, failure_to_fill.c_str()); if (!cached_field->IsFocusable() && result.fields[i].is_autofilled) AutofillMetrics::LogHiddenOrPresentationalSelectFieldsFilled(); } - buffer << CTag{"table"}; + LOG_AF(buffer) << CTag{"table"}; autofilled_form_signatures_.push_front(form_structure->FormSignatureAsStr()); // Only remember the last few forms that we've seen, both to avoid false @@ -2035,11 +2116,9 @@ void BrowserAutofillManager::FillOrPreviewDataModelForm( if (autofilled_form_signatures_.size() > kMaxRecentFormSignaturesToRemember) autofilled_form_signatures_.pop_back(); - if (log_manager()) { - log_manager()->Log() << LoggingScope::kFilling - << LogMessage::kSendFillingData << Br{} - << std::move(buffer); - } + LOG_AF(log_manager()) << LoggingScope::kFilling + << LogMessage::kSendFillingData << Br{} + << std::move(buffer); auto field_types = base::MakeFlatMap<FieldGlobalId, ServerFieldType>( *form_structure, {}, [](const auto& field) { @@ -2159,6 +2238,8 @@ std::vector<Suggestion> BrowserAutofillManager::GetCreditCardSuggestions( return suggestions; } +// TODO(crbug.com/1309848) Eliminate and replace with a listener? +// Should we do the same with all the other BrowserAutofillManager events? void BrowserAutofillManager::OnBeforeProcessParsedForms() { has_parsed_forms_ = true; @@ -2648,6 +2729,8 @@ void BrowserAutofillManager::GetAvailableSuggestions( // warning message and don't offer autofill. The warning is shown even if // there are no autofill suggestions available. if (IsFormMixedContent(client(), form) && + client()->GetPrefs()->FindPreference( + ::prefs::kMixedFormsWarningsEnabled) && client()->GetPrefs()->GetBoolean(::prefs::kMixedFormsWarningsEnabled)) { suggestions->clear(); // If the user begins typing, we interpret that as dismissing the warning. diff --git a/chromium/components/autofill/core/browser/browser_autofill_manager.h b/chromium/components/autofill/core/browser/browser_autofill_manager.h index d6d33df9090..c33ab8422d4 100644 --- a/chromium/components/autofill/core/browser/browser_autofill_manager.h +++ b/chromium/components/autofill/core/browser/browser_autofill_manager.h @@ -37,7 +37,7 @@ #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/single_field_form_fill_router.h" #include "components/autofill/core/browser/sync_utils.h" -#include "components/autofill/core/browser/touch_to_fill_delegate.h" +#include "components/autofill/core/browser/touch_to_fill_delegate_impl.h" #include "components/autofill/core/browser/ui/popup_types.h" #include "components/autofill/core/common/dense_set.h" #include "components/autofill/core/common/form_data.h" @@ -157,28 +157,31 @@ class BrowserAutofillManager : public AutofillManager, // Called from our external delegate so they cannot be private. // FillCreditCardForm() is also called by Autofill Assistant through // ContentAutofillDriver::FillFormForAssistant(). + // TODO(crbug.com/1330108): Clean up the API. virtual void FillOrPreviewForm(mojom::RendererFormDataAction action, int query_id, const FormData& form, const FormFieldData& field, int unique_id); - void FillCreditCardForm(int query_id, - const FormData& form, - const FormFieldData& field, - const CreditCard& credit_card, - const std::u16string& cvc) override; + void FillCreditCardFormImpl(const FormData& form, + const FormFieldData& field, + const CreditCard& credit_card, + const std::u16string& cvc, + int query_id) override; void DidShowSuggestions(bool has_autofill_suggestions, const FormData& form, const FormFieldData& field); // Called only from Autofill Assistant through // ContentAutofillDriver::FillFormForAssistant(). - void FillProfileForm(const AutofillProfile& profile, - const FormData& form, - const FormFieldData& field) override; + // TODO(crbug.com/1330108): Clean up the API. + void FillProfileFormImpl(const FormData& form, + const FormFieldData& field, + const AutofillProfile& profile) override; // Fetches the related virtual card information given the related actual card // |guid| and fills the information into the form. + // TODO(crbug.com/1330108): Clean up the API. virtual void FillOrPreviewVirtualCardInformation( mojom::RendererFormDataAction action, const std::string& guid, @@ -238,20 +241,21 @@ class BrowserAutofillManager : public AutofillManager, void DidSuppressPopup(const FormData& form, const FormFieldData& field); // AutofillManager: + base::WeakPtr<AutofillManager> GetWeakPtr() override; AutofillOfferManager* GetOfferManager() override; CreditCardAccessManager* GetCreditCardAccessManager() override; bool ShouldClearPreviewedForm() override; - void OnFocusNoLongerOnForm(bool had_interacted_form) override; + void OnFocusNoLongerOnFormImpl(bool had_interacted_form) override; void OnFocusOnFormFieldImpl(const FormData& form, const FormFieldData& field, const gfx::RectF& bounding_box) override; - void OnDidFillAutofillFormData(const FormData& form, - const base::TimeTicks timestamp) override; - void OnDidPreviewAutofillFormData() override; - void OnDidEndTextFieldEditing() override; - void OnHidePopup() override; - void SelectFieldOptionsDidChange(const FormData& form) override; - void JavaScriptChangedAutofilledValue( + void OnDidFillAutofillFormDataImpl(const FormData& form, + const base::TimeTicks timestamp) override; + void OnDidPreviewAutofillFormDataImpl() override; + void OnDidEndTextFieldEditingImpl() override; + void OnHidePopupImpl() override; + void OnSelectFieldOptionsDidChangeImpl(const FormData& form) override; + void OnJavaScriptChangedAutofilledValueImpl( const FormData& form, const FormFieldData& field, const std::u16string& old_value) override; @@ -281,6 +285,12 @@ class BrowserAutofillManager : public AutofillManager, // to be uploadable. Exposed for testing. bool ShouldUploadForm(const FormStructure& form); + void SetProfileFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) override; + + void SetCreditCardFillViaAutofillAssistantIntent( + const autofill_assistant::AutofillAssistantIntent intent) override; + // Returns the last form the autofill manager considered in this frame. virtual const FormData& last_query_form() const; @@ -313,8 +323,8 @@ class BrowserAutofillManager : public AutofillManager, external_delegate_ = std::move(external_delegate); } - void SetTouchToFillDelegateForTest( - std::unique_ptr<TouchToFillDelegate> touch_to_fill_delegate) { + void SetTouchToFillDelegateImplForTest( + std::unique_ptr<TouchToFillDelegateImpl> touch_to_fill_delegate) { touch_to_fill_delegate_ = std::move(touch_to_fill_delegate); } @@ -380,10 +390,10 @@ class BrowserAutofillManager : public AutofillManager, const FormFieldData& field, const gfx::RectF& bounding_box) override {} void OnAskForValuesToFillImpl( - int query_id, const FormData& form, const FormFieldData& field, const gfx::RectF& transformed_box, + int query_id, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible) override; void OnSelectControlDidChangeImpl(const FormData& form, @@ -483,6 +493,7 @@ class BrowserAutofillManager : public AutofillManager, // Fills or previews the credit card form. // Assumes the form and field are valid. + // TODO(crbug.com/1330108): Clean up the API. void FillOrPreviewCreditCardForm(mojom::RendererFormDataAction action, int query_id, const FormData& form, @@ -491,6 +502,7 @@ class BrowserAutofillManager : public AutofillManager, // Fills or previews the profile form. // Assumes the form and field are valid. + // TODO(crbug.com/1330108): Clean up the API. void FillOrPreviewProfileForm(mojom::RendererFormDataAction action, int query_id, const FormData& form, @@ -498,6 +510,7 @@ class BrowserAutofillManager : public AutofillManager, const AutofillProfile& profile); // Fills or previews |data_model| in the |form|. + // TODO(crbug.com/1330108): Clean up the API. void FillOrPreviewDataModelForm( mojom::RendererFormDataAction action, int query_id, @@ -662,7 +675,7 @@ class BrowserAutofillManager : public AutofillManager, // Delegates to perform external processing (display, selection) on // our behalf. std::unique_ptr<AutofillExternalDelegate> external_delegate_; - std::unique_ptr<TouchToFillDelegate> touch_to_fill_delegate_; + std::unique_ptr<TouchToFillDelegateImpl> touch_to_fill_delegate_; std::string app_locale_; @@ -765,11 +778,12 @@ class BrowserAutofillManager : public AutofillManager, friend class AutofillAssistantTest; friend class AutofillMetricsCrossFrameFormTest; friend class BrowserAutofillManagerTest; - friend class AutofillMetricsTest; friend class metrics::AutofillMetricsBaseTest; friend class FormStructureBrowserTest; friend class GetMatchingTypesTest; friend class CreditCardAccessoryControllerTest; + FRIEND_TEST_ALL_PREFIXES(BrowserAutofillManagerTest, + OnCreditCardFetched_StoreInstrumentId); }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/browser_autofill_manager_unittest.cc index b287800c248..1dcdd51bebb 100644 --- a/chromium/components/autofill/core/browser/browser_autofill_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/browser_autofill_manager_unittest.cc @@ -23,6 +23,7 @@ #include "base/metrics/metrics_hashes.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" @@ -39,6 +40,7 @@ #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/browser/form_structure_test_api.h" #include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h" #include "components/autofill/core/browser/metrics/form_events/form_events.h" #include "components/autofill/core/browser/mock_autocomplete_history_manager.h" @@ -52,10 +54,10 @@ #include "components/autofill/core/browser/test_autofill_download_manager.h" #include "components/autofill/core/browser/test_autofill_driver.h" #include "components/autofill/core/browser/test_autofill_external_delegate.h" +#include "components/autofill/core/browser/test_autofill_manager_waiter.h" #include "components/autofill/core/browser/test_autofill_tick_clock.h" #include "components/autofill/core/browser/test_browser_autofill_manager.h" #include "components/autofill/core/browser/test_form_data_importer.h" -#include "components/autofill/core/browser/test_form_structure.h" #include "components/autofill/core/browser/test_personal_data_manager.h" #include "components/autofill/core/browser/ui/popup_item_ids.h" #include "components/autofill/core/browser/ui/suggestion.h" @@ -140,6 +142,7 @@ class MockAutofillClient : public TestAutofillClient { GetProfileType, (), (const override)); + MOCK_METHOD(void, HideAutofillPopup, (PopupHidingReason reason), (override)); MOCK_METHOD(bool, IsPasswordManagerEnabled, (), (override)); }; @@ -163,17 +166,21 @@ class MockAutofillDownloadManager : public TestAutofillDownloadManager { (override)); }; -class MockTouchToFillDelegate : public TouchToFillDelegate { +class MockTouchToFillDelegateImpl : public TouchToFillDelegateImpl { public: - MockTouchToFillDelegate() = default; - MockTouchToFillDelegate(const MockTouchToFillDelegate&) = delete; - MockTouchToFillDelegate& operator=(const MockTouchToFillDelegate&) = delete; - ~MockTouchToFillDelegate() override = default; + explicit MockTouchToFillDelegateImpl(BrowserAutofillManager* manager) + : TouchToFillDelegateImpl(manager) {} + MockTouchToFillDelegateImpl(const MockTouchToFillDelegateImpl&) = delete; + MockTouchToFillDelegateImpl& operator=(const MockTouchToFillDelegateImpl&) = + delete; + ~MockTouchToFillDelegateImpl() override = default; MOCK_METHOD(bool, TryToShowTouchToFill, (int query_id, const FormData& form, const FormFieldData& field), (override)); + MOCK_METHOD(bool, IsShowingTouchToFill, (), (override)); + MOCK_METHOD(void, HideTouchToFill, (), (override)); }; void ExpectFilledField(const char* expected_label, @@ -397,13 +404,14 @@ class BrowserAutofillManagerTest : public testing::Test { new TestCreditCardSaveManager(autofill_driver_.get(), &autofill_client_, payments_client_, &personal_data()); credit_card_save_manager->SetCreditCardUploadEnabled(true); - TestFormDataImporter* test_form_data_importer = new TestFormDataImporter( - &autofill_client_, payments_client_, - std::unique_ptr<CreditCardSaveManager>(credit_card_save_manager), - &personal_data(), "en-US"); + auto test_form_data_importer = + std::make_unique<autofill::TestFormDataImporter>( + &autofill_client_, payments_client_, + std::unique_ptr<CreditCardSaveManager>(credit_card_save_manager), + &personal_data(), "en-US"); + test_form_data_importer_ = test_form_data_importer.get(); autofill_client_.set_test_form_data_importer( - std::unique_ptr<autofill::TestFormDataImporter>( - test_form_data_importer)); + std::move(test_form_data_importer)); browser_autofill_manager_ = std::make_unique<TestBrowserAutofillManager>( autofill_driver_.get(), &autofill_client_); @@ -411,6 +419,12 @@ class BrowserAutofillManagerTest : public testing::Test { std::make_unique<NiceMock<MockSingleFieldFormFillRouter>>( autocomplete_history_manager_.get(), merchant_promo_code_manager_.get()); + // By default, if we offer single field form fill, suggestions should be + // returned because it is assumed |field.should_autocomplete| is set to + // true. This should be overridden in tests where + // |field.should_autocomplete| is set to false. + ON_CALL(*single_field_form_fill_router, OnGetSingleFieldSuggestions) + .WillByDefault(testing::Return(true)); single_field_form_fill_router_ = single_field_form_fill_router.get(); browser_autofill_manager_->set_single_field_form_fill_router_for_test( std::move(single_field_form_fill_router)); @@ -428,9 +442,12 @@ class BrowserAutofillManagerTest : public testing::Test { browser_autofill_manager_->SetExternalDelegateForTest( std::move(external_delegate)); - auto touch_to_fill_delegate = std::make_unique<MockTouchToFillDelegate>(); + auto touch_to_fill_delegate = std::make_unique<MockTouchToFillDelegateImpl>( + browser_autofill_manager_.get()); + ON_CALL(*touch_to_fill_delegate, IsShowingTouchToFill()) + .WillByDefault(Return(false)); touch_to_fill_delegate_ = touch_to_fill_delegate.get(); - browser_autofill_manager_->SetTouchToFillDelegateForTest( + browser_autofill_manager_->SetTouchToFillDelegateImplForTest( std::move(touch_to_fill_delegate)); auto test_strike_database = std::make_unique<TestStrikeDatabase>(); @@ -503,7 +520,7 @@ class BrowserAutofillManagerTest : public testing::Test { const FormData& form, const FormFieldData& field) { browser_autofill_manager_->OnAskForValuesToFill( - query_id, form, field, gfx::RectF(), + form, field, gfx::RectF(), query_id, /*autoselect_first_suggestion=*/false, TouchToFillEligible(false)); } @@ -517,7 +534,7 @@ class BrowserAutofillManagerTest : public testing::Test { const FormFieldData& field, TouchToFillEligible touch_to_fill_eligible) { browser_autofill_manager_->OnAskForValuesToFill( - query_id, form, field, gfx::RectF(), + form, field, gfx::RectF(), query_id, /*autoselect_first_suggestion=*/false, touch_to_fill_eligible); } @@ -547,6 +564,9 @@ class BrowserAutofillManagerTest : public testing::Test { const FormData& form, const FormFieldData& field, int unique_id) { + browser_autofill_manager_->OnAskForValuesToFill( + form, field, {}, query_id, /*autoselect_first_suggestion=*/true, + TouchToFillEligible(false)); browser_autofill_manager_->FillOrPreviewForm( mojom::RendererFormDataAction::kFill, query_id, form, field, unique_id); } @@ -662,8 +682,9 @@ class BrowserAutofillManagerTest : public testing::Test { const std::string& real_pan, bool is_virtual_card = false) { payments::FullCardRequest* full_card_request = - browser_autofill_manager_->credit_card_access_manager_ - ->cvc_authenticator_->full_card_request_.get(); + browser_autofill_manager_->client() + ->GetCVCAuthenticator() + ->full_card_request_.get(); DCHECK(full_card_request); // Mock user response. @@ -683,8 +704,8 @@ class BrowserAutofillManagerTest : public testing::Test { // Convenience method to cast the FullCardRequest into a CardUnmaskDelegate. CardUnmaskDelegate* full_card_unmask_delegate() { payments::FullCardRequest* full_card_request = - browser_autofill_manager_->credit_card_access_manager_ - ->GetOrCreateCVCAuthenticator() + browser_autofill_manager_->client() + ->GetCVCAuthenticator() ->full_card_request_.get(); DCHECK(full_card_request); return static_cast<CardUnmaskDelegate*>(full_card_request); @@ -761,7 +782,7 @@ class BrowserAutofillManagerTest : public testing::Test { std::unique_ptr<MockAutofillDriver> autofill_driver_; std::unique_ptr<TestBrowserAutofillManager> browser_autofill_manager_; raw_ptr<TestAutofillExternalDelegate> external_delegate_; - raw_ptr<MockTouchToFillDelegate> touch_to_fill_delegate_; + raw_ptr<MockTouchToFillDelegateImpl> touch_to_fill_delegate_; scoped_refptr<AutofillWebDataService> database_; raw_ptr<MockAutofillDownloadManager> download_manager_; std::unique_ptr<MockAutocompleteHistoryManager> autocomplete_history_manager_; @@ -770,6 +791,7 @@ class BrowserAutofillManagerTest : public testing::Test { base::test::ScopedFeatureList scoped_feature_list_; raw_ptr<TestStrikeDatabase> strike_database_; raw_ptr<payments::TestPaymentsClient> payments_client_; + raw_ptr<TestFormDataImporter> test_form_data_importer_; private: int ToHistogramSample(AutofillMetrics::CardUploadDecisionMetric metric) { @@ -859,9 +881,14 @@ class BrowserAutofillManagerStructuredProfileTest void BrowserAutofillManagerStructuredProfileTest::InitializeFeatures() { structured_names_and_addresses_ = GetParam(); + // The BrowserAutofillManagerStructuredProfileTest test suite will run every + // test once with these flags on, and once with these flags off. std::vector<base::Feature> features = { features::kAutofillEnableSupportForMoreStructureInAddresses, - features::kAutofillEnableSupportForMoreStructureInNames}; + features::kAutofillEnableSupportForMoreStructureInNames, + // TODO(crbug.com/1190334): Remove this feature flag once the GPay + // activated promo code autofill project is fully launched and stable. + features::kAutofillFillMerchantPromoCodeFields}; if (structured_names_and_addresses_) { scoped_features_.InitWithFeatures(features, {}); } else { @@ -1105,9 +1132,9 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, std::vector<FormData> forms(1, form); FormsSeen(forms); - // Ensure that the single field form fill router is not called for + // Ensure that the SingleFieldFormFillRouter is not called for // suggestions either. - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions) + EXPECT_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions) .Times(0); // Suggestions should be returned for the first two fields. @@ -1141,8 +1168,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, std::vector<FormData> forms(1, form); FormsSeen(forms); - // Ensure that the single field form fill router is called for both fields. - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions) + // Ensure that the SingleFieldFormFillRouter is called for both fields. + EXPECT_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions) .Times(2); GetAutofillSuggestions(form, form.fields[0]); @@ -1838,7 +1865,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, browser_autofill_manager_->ShouldShowCreditCardSigninPromo(form, field)); // Single field form fill suggestions are not queried. - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions) + EXPECT_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions) .Times(0); GetAutofillSuggestions(form, field); @@ -2363,6 +2390,26 @@ TEST_F(BrowserAutofillManagerTest, browser_autofill_manager_->GetPackedCreditCardID(1))); } +TEST_F(BrowserAutofillManagerTest, OnCreditCardFetched_StoreInstrumentId) { + FormData form; + CreateTestCreditCardFormData(&form, true, false); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + CreditCard credit_card = test::GetMaskedServerCard(); + browser_autofill_manager_->FillOrPreviewCreditCardForm( + mojom::RendererFormDataAction::kFill, kDefaultPageID, form, + form.fields[0], &credit_card); + + browser_autofill_manager_->OnCreditCardFetched( + CreditCardFetchResult::kSuccess, &credit_card, + /*cvc=*/u"123"); + + ASSERT_TRUE( + test_form_data_importer_->fetched_card_instrument_id().has_value()); + EXPECT_EQ(test_form_data_importer_->fetched_card_instrument_id().value(), + credit_card.instrument_id()); +} + // Test that we return profile and credit card suggestions for combined forms. TEST_P(SuggestionMatchingTest, GetAddressAndCreditCardSuggestions) { // Set up our form data. @@ -2750,8 +2797,7 @@ TEST_P(BrowserAutofillManagerLogAblationTest, TestLogging) { DisableAutofillViaAblation(scoped_feature_list_, /*for_addresses=*/true, /*for_credit_cards=*/true); - TestAutofillTickClock clock; - clock.SetNowTicks(base::TimeTicks::Now()); + TestAutofillTickClock clock(AutofillTickClock::NowTicks()); base::HistogramTester histogram_tester; // Set up our form data. In the kMixed case the form will contain the fields @@ -2950,26 +2996,9 @@ TEST_F(BrowserAutofillManagerTest, DetermineStateFieldTypeForUpload) { EXPECT_TRUE(form_structure.field(1)->state_is_a_matching_type()); } -// Test fixture which enables -// features::kAutofillFixServerQueriesIfPasswordManagerIsEnabled. -// TODO(crbug.com/1293341) Once enabled by default, delete this test -// fixture and use BrowserAutofillManagerTest in the test below. -class BrowserAutofillManagerTestWithFixForQueries - : public BrowserAutofillManagerTest { - public: - BrowserAutofillManagerTestWithFixForQueries() { - features_.InitAndEnableFeature( - features::kAutofillFixServerQueriesIfPasswordManagerIsEnabled); - } - ~BrowserAutofillManagerTestWithFixForQueries() override = default; - - private: - base::test::ScopedFeatureList features_; -}; - // Ensures that if autofill is disabled but the password manager is enabled, // Autofill still performs a lookup to the server. -TEST_F(BrowserAutofillManagerTestWithFixForQueries, +TEST_F(BrowserAutofillManagerTest, OnFormsSeen_AutofillDisabledPasswordManagerEnabled) { // Set up our form data. FormData form; @@ -4742,12 +4771,15 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPhoneNumber) { // In one form, rely on the max length attribute to imply US phone number // parts. In the other form, rely on the autocomplete type attribute. FormData form_with_us_number_max_length; + form_with_us_number_max_length.unique_renderer_id = + test::MakeFormRendererId(); form_with_us_number_max_length.name = u"MyMaxlengthPhoneForm"; form_with_us_number_max_length.url = GURL("https://myform.com/phone_form.html"); form_with_us_number_max_length.action = GURL("https://myform.com/phone_submit.html"); FormData form_with_autocompletetype = form_with_us_number_max_length; + form_with_autocompletetype.unique_renderer_id = test::MakeFormRendererId(); form_with_autocompletetype.name = u"MyAutocompletetypePhoneForm"; struct { @@ -4819,10 +4851,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPhoneNumber) { EXPECT_EQ(u"4567", response_data2.fields[3].value); EXPECT_EQ(std::u16string(), response_data2.fields[4].value); - // We should not be able to fill international numbers correctly in a form - // containing fields with US max_length. However, the field should fill with - // the number of digits equal to the max length specified, starting from the - // right. + // For other countries, fill prefix and suffix fields with best effort. work_profile->SetRawInfo(ADDRESS_HOME_COUNTRY, u"GB"); work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"447700954321"); page_id = 3; @@ -4837,7 +4866,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPhoneNumber) { ASSERT_EQ(5U, response_data3.fields.size()); EXPECT_EQ(u"4", response_data3.fields[0].value); EXPECT_EQ(u"700", response_data3.fields[1].value); - EXPECT_EQ(u"321", response_data3.fields[2].value); + EXPECT_EQ(u"95", response_data3.fields[2].value); EXPECT_EQ(u"4321", response_data3.fields[3].value); EXPECT_EQ(std::u16string(), response_data3.fields[4].value); @@ -4853,8 +4882,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FillPhoneNumber) { ASSERT_EQ(5U, response_data4.fields.size()); EXPECT_EQ(u"44", response_data4.fields[0].value); EXPECT_EQ(u"7700", response_data4.fields[1].value); - EXPECT_EQ(u"954321", response_data4.fields[2].value); - EXPECT_EQ(u"954321", response_data4.fields[3].value); + EXPECT_EQ(u"95", response_data4.fields[2].value); + EXPECT_EQ(u"4321", response_data4.fields[3].value); EXPECT_EQ(std::u16string(), response_data4.fields[4].value); } @@ -4870,6 +4899,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, // Verify only the first complete number is filled when there are multiple // componentized number fields. FormData form_with_multiple_componentized_phone_fields; + form_with_multiple_componentized_phone_fields.unique_renderer_id = + test::MakeFormRendererId(); form_with_multiple_componentized_phone_fields.url = GURL("https://www.foo.com/"); @@ -4936,6 +4967,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_multiple_whole_number_fields; + form_with_multiple_whole_number_fields.unique_renderer_id = + test::MakeFormRendererId(); form_with_multiple_whole_number_fields.url = GURL("https://www.foo.com/"); FormFieldData field; @@ -4988,6 +5021,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, // Verify only the first complete number is filled when there are multiple // componentized number fields. FormData form_with_multiple_componentized_phone_fields; + form_with_multiple_componentized_phone_fields.unique_renderer_id = + test::MakeFormRendererId(); form_with_multiple_componentized_phone_fields.url = GURL("https://www.foo.com/"); @@ -5059,6 +5094,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_misclassified_extension; + form_with_misclassified_extension.unique_renderer_id = + test::MakeFormRendererId(); form_with_misclassified_extension.url = GURL("https://www.foo.com/"); FormFieldData field; @@ -5122,6 +5159,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_no_complete_number; + form_with_no_complete_number.unique_renderer_id = test::MakeFormRendererId(); form_with_no_complete_number.url = GURL("https://www.foo.com/"); FormFieldData field; @@ -5179,6 +5217,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_multiple_whole_number_fields; + form_with_multiple_whole_number_fields.unique_renderer_id = + test::MakeFormRendererId(); form_with_multiple_whole_number_fields.url = GURL("https://www.foo.com/"); FormFieldData field; @@ -5232,6 +5272,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_multiple_whole_number_fields; + form_with_multiple_whole_number_fields.unique_renderer_id = + test::MakeFormRendererId(); form_with_multiple_whole_number_fields.url = GURL("https://www.foo.com/"); FormFieldData field; @@ -5280,6 +5322,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, TEST_P(BrowserAutofillManagerStructuredProfileTest, FormWithHiddenOrPresentationalSelects) { FormData form; + form.unique_renderer_id = test::MakeFormRendererId(); form.name = u"MyForm"; form.url = GURL("https://myform.com/form.html"); form.action = GURL("https://myform.com/submit.html"); @@ -5356,6 +5399,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, std::string guid(work_profile->guid()); FormData form_with_multiple_sections; + form_with_multiple_sections.unique_renderer_id = test::MakeFormRendererId(); form_with_multiple_sections.url = GURL("https://www.foo.com/"); FormFieldData field; @@ -5448,12 +5492,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormChangesRemoveField) { test::CreateTestFormField("Some", "field", "", "text", &field); form.fields.insert(form.fields.begin() + 3, field); - std::vector<FormData> forms(1, form); - FormsSeen(forms); + FormsSeen({form}); // Now, after the call to |FormsSeen|, we remove the field before filling. form.fields.erase(form.fields.begin() + 3); + FormsSeen({form}); + const char guid[] = "00000000-0000-0000-0000-000000000001"; int response_page_id = 0; FormData response_data; @@ -5484,6 +5529,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormChangesAddField) { // Now, after the call to |FormsSeen|, we restore the field before filling. form.fields.insert(pos, field); + FormsSeen({form}); + const char guid[] = "00000000-0000-0000-0000-000000000001"; int response_page_id = 0; FormData response_data; @@ -5500,6 +5547,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormChangesVisibilityOfFields) { // Set up our form data. FormData form; + form.unique_renderer_id = test::MakeFormRendererId(); form.url = GURL("https://www.foo.com/"); FormFieldData field; @@ -5551,6 +5599,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, // the right fields are getting filled.) response_data.fields[3].is_focusable = true; response_data.fields[4].is_focusable = true; + FormData later_response_data; const char guid2[] = "00000000-0000-0000-0000-000000000002"; FillAutofillFormDataAndSaveResults(kDefaultPageID, response_data, @@ -5628,7 +5677,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormData form; test::CreateTestAddressFormData(&form); - EXPECT_CALL(*(single_field_form_fill_router_), OnWillSubmitForm(_, _, true)); + EXPECT_CALL(*single_field_form_fill_router_, OnWillSubmitForm(_, _, true)); FormSubmitted(form); } @@ -5662,7 +5711,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, ValuePatternsMetric) { } // Test that when Autofill is disabled, single field form fill suggestions are -// still queried. +// still queried as a fallback. TEST_P(BrowserAutofillManagerStructuredProfileTest, SingleFieldFormFillSuggestions_SomeWhenAutofillDisabled) { browser_autofill_manager_->SetAutofillProfileEnabled(false); @@ -5681,39 +5730,14 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormsSeen(forms); const FormFieldData& field = form.fields[0]; - // Expect the single field form fill router to be called for suggestions. - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions); + // Expect the SingleFieldFormFillRouter to be called for suggestions. + EXPECT_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions); GetAutofillSuggestions(form, field); -} - -// Test that when Autofill is disabled and the field should not autocomplete, -// single field form fill suggestions are not queried. -TEST_P( - BrowserAutofillManagerStructuredProfileTest, - SingleFieldFormFillSuggestions_AutofillDisabledAndFieldShouldNotAutocomplete) { - browser_autofill_manager_->SetAutofillProfileEnabled(false); - browser_autofill_manager_->SetAutofillCreditCardEnabled(false); - auto external_delegate = std::make_unique<TestAutofillExternalDelegate>( - browser_autofill_manager_.get(), autofill_driver_.get(), - /*call_parent_methods=*/false); - external_delegate_ = external_delegate.get(); - browser_autofill_manager_->SetExternalDelegateForTest( - std::move(external_delegate)); - // Set up our form data. - FormData form; - test::CreateTestAddressFormData(&form); - std::vector<FormData> forms(1, form); - FormsSeen(forms); - FormFieldData field = form.fields[0]; - field.should_autocomplete = false; - - // Single field form fill router is not called for suggestions. - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions) - .Times(0); - - GetAutofillSuggestions(form, field); + // Single field form fill suggestions were returned, so we should not go + // through the normal autofill flow. + EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen()); } // Test that we do not query for single field form fill suggestions when there @@ -5727,8 +5751,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormsSeen(forms); const FormFieldData& field = form.fields[0]; - // Single field form fill router is not called for suggestions. - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions) + // SingleFieldFormFillRouter is not called for suggestions. + EXPECT_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions) .Times(0); GetAutofillSuggestions(form, field); @@ -5752,7 +5776,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, // Single field form fill manager is called for suggestions because Autofill // is empty. - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions); + EXPECT_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions); GetAutofillSuggestions(form, field); } @@ -5787,8 +5811,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormFieldData field = form.fields[0]; field.should_autocomplete = true; - // Single field form fill router is not called for suggestions. - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions); + // SingleFieldFormFillRouter is called for suggestions. + EXPECT_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions); GetAutofillSuggestions(form, field); } @@ -5823,19 +5847,20 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormFieldData field = form.fields[1]; field.should_autocomplete = true; - // Single field form fill router is not called for suggestions. - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions) + // SingleFieldFormFillRouter is not called for suggestions. + EXPECT_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions) .Times(0); GetAutofillSuggestions(form, field); } -// Test that we do not query for single field form fill suggestions when there -// are no Autofill suggestions available, and that the field should not -// autocomplete. +// Test that the situation where there are no Autofill suggestions available, +// and no single field form fill conditions were met is correctly handled. The +// single field form fill conditions were not met because autocomplete is set to +// off and the field is not recognized as a promo code field. TEST_F( BrowserAutofillManagerTest, - SingleFieldFormFillSuggestions_NoneWhenAutofillEmptyFieldShouldNotAutocomplete) { + SingleFieldFormFillSuggestions_NoneWhenAutofillEmptyAndSingleFieldFormFillConditionsNotMet) { // Set up our form data. FormData form; test::CreateTestAddressFormData(&form); @@ -5847,15 +5872,25 @@ TEST_F( field.should_autocomplete = false; test::CreateTestFormField("Email", "email", "donkey", "email", &field); - // Single field form fill router is not called for suggestions. - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions) - .Times(0); + // Autocomplete is set to off, so suggestions should not get returned from + // |single_field_form_fill_router_|. + EXPECT_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions) + .WillRepeatedly(testing::Return(false)); GetAutofillSuggestions(form, field); + + // Single field form fill was not triggered, so go through the normal autofill + // flow. + EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen()); } -TEST_P(BrowserAutofillManagerStructuredProfileTest, - AutocompleteOffRespectedForSingleFieldFormFill) { +// Test that the situation where no single field form fill conditions were met +// is handled correctly. The single field form fill conditions were not met +// because autocomplete is set to off and the field is not recognized as a promo +// code field. +TEST_P( + BrowserAutofillManagerStructuredProfileTest, + SingleFieldFormFillSuggestions_NoneWhenSingleFieldFormFillConditionsNotMet) { browser_autofill_manager_->SetAutofillProfileEnabled(false); browser_autofill_manager_->SetAutofillCreditCardEnabled(false); auto external_delegate = std::make_unique<TestAutofillExternalDelegate>( @@ -5865,9 +5900,6 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, browser_autofill_manager_->SetExternalDelegateForTest( std::move(external_delegate)); - EXPECT_CALL(*(single_field_form_fill_router_), OnGetSingleFieldSuggestions) - .Times(0); - // Set up our form data. FormData form; test::CreateTestAddressFormData(&form); @@ -5875,12 +5907,22 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormsSeen(forms); FormFieldData* field = &form.fields[0]; field->should_autocomplete = false; + + // Autocomplete is set to off, so suggestions should not get returned from + // |single_field_form_fill_router_|. + EXPECT_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions) + .WillRepeatedly(testing::Return(false)); + GetAutofillSuggestions(form, *field); + + // Single field form fill was not triggered, so go through the normal autofill + // flow. + EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen()); } TEST_P(BrowserAutofillManagerStructuredProfileTest, DestructorCancelsSingleFieldFormFillQueries) { - EXPECT_CALL(*(single_field_form_fill_router_), CancelPendingQueries).Times(1); + EXPECT_CALL(*single_field_form_fill_router_, CancelPendingQueries).Times(1); browser_autofill_manager_.reset(); } @@ -5919,9 +5961,9 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); // Simulate having seen this form on page load. // |form_structure_instance| will be owned by |browser_autofill_manager_|. - auto form_structure_instance = std::make_unique<TestFormStructure>(form); + auto form_structure_instance = std::make_unique<FormStructure>(form); // This pointer is valid as long as autofill manager lives. - TestFormStructure* form_structure = form_structure_instance.get(); + FormStructure* form_structure = form_structure_instance.get(); form_structure->DetermineHeuristicTypes(nullptr, nullptr); browser_autofill_manager_->AddSeenFormStructure( std::move(form_structure_instance)); @@ -5939,9 +5981,9 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form2.fields.push_back(field); test::CreateTestFormField("Postal Code", "zipcode", "", "text", &field); form2.fields.push_back(field); - auto form_structure_instance2 = std::make_unique<TestFormStructure>(form2); + auto form_structure_instance2 = std::make_unique<FormStructure>(form2); // This pointer is valid as long as autofill manager lives. - TestFormStructure* form_structure2 = form_structure_instance2.get(); + FormStructure* form_structure2 = form_structure_instance2.get(); form_structure2->DetermineHeuristicTypes(nullptr, nullptr); browser_autofill_manager_->AddSeenFormStructure( std::move(form_structure_instance2)); @@ -6013,12 +6055,11 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, // Simulate having seen this form on page load. // |form_structure| will be owned by |browser_autofill_manager_|. - TestFormStructure* form_structure = new TestFormStructure(form); + auto form_structure = std::make_unique<FormStructure>(form); form_structure->DetermineHeuristicTypes(nullptr, nullptr); std::vector<FormSignature> signatures = test::GetEncodedSignatures(*form_structure); - browser_autofill_manager_->AddSeenFormStructure( - std::unique_ptr<TestFormStructure>(form_structure)); + browser_autofill_manager_->AddSeenFormStructure(std::move(form_structure)); AutofillQueryResponse response; auto* form_suggestion = response.add_form_suggestions(); @@ -6073,10 +6114,13 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, // Simulate having seen this form on page load. // |form_structure| will be owned by |browser_autofill_manager_|. - TestFormStructure* form_structure = new TestFormStructure(form); - form_structure->DetermineHeuristicTypes(nullptr, nullptr); - browser_autofill_manager_->AddSeenFormStructure( - std::unique_ptr<TestFormStructure>(form_structure)); + FormStructure* form_structure = [&] { + auto form_structure = std::make_unique<FormStructure>(form); + FormStructure* ptr = form_structure.get(); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); + browser_autofill_manager_->AddSeenFormStructure(std::move(form_structure)); + return ptr; + }(); AutofillQueryResponse response; auto* form_suggestion = response.add_form_suggestions(); @@ -6147,7 +6191,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormSubmittedServerTypes) { // Simulate having seen this form on page load. // |form_structure| will be owned by |browser_autofill_manager_|. - TestFormStructure* form_structure = new TestFormStructure(form); + auto form_structure = std::make_unique<FormStructure>(form); form_structure->DetermineHeuristicTypes(nullptr, nullptr); // Clear the heuristic types, and instead set the appropriate server types. @@ -6156,9 +6200,9 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormSubmittedServerTypes) { heuristic_types.push_back(UNKNOWN_TYPE); server_types.push_back(form_structure->field(i)->heuristic_type()); } - form_structure->SetFieldTypes(heuristic_types, server_types); - browser_autofill_manager_->AddSeenFormStructure( - std::unique_ptr<TestFormStructure>(form_structure)); + FormStructureTestApi(form_structure.get()) + .SetFieldTypes(heuristic_types, server_types); + browser_autofill_manager_->AddSeenFormStructure(std::move(form_structure)); // Fill the form. const char guid[] = "00000000-0000-0000-0000-000000000001"; @@ -6363,8 +6407,8 @@ const ProfileMatchingTypesTestCase kProfileMatchingTypesTestCases[] = { {"1", {PHONE_HOME_COUNTRY_CODE}, {PHONE_HOME_COUNTRY_CODE}}, {"234", {PHONE_HOME_CITY_CODE}, {PHONE_HOME_CITY_CODE}}, {"5678901", {PHONE_HOME_NUMBER}, {PHONE_HOME_NUMBER}}, - {"567", {PHONE_HOME_NUMBER}, {PHONE_HOME_NUMBER}}, - {"8901", {PHONE_HOME_NUMBER}, {PHONE_HOME_NUMBER}}, + {"567", {PHONE_HOME_NUMBER_PREFIX}, {PHONE_HOME_NUMBER_PREFIX}}, + {"8901", {PHONE_HOME_NUMBER_SUFFIX}, {PHONE_HOME_NUMBER_SUFFIX}}, // Test a European profile. {"Paris", {ADDRESS_HOME_CITY}, {ADDRESS_HOME_CITY}}, @@ -7392,7 +7436,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, TEST_P(BrowserAutofillManagerStructuredProfileTest, DontSaveCvcInAutocompleteHistory) { FormData form_seen_by_ahm; - EXPECT_CALL(*(single_field_form_fill_router_), OnWillSubmitForm(_, _, true)) + EXPECT_CALL(*single_field_form_fill_router_, OnWillSubmitForm(_, _, true)) .WillOnce(SaveArg<0>(&form_seen_by_ahm)); FormData form; @@ -8081,8 +8125,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, } // Verify that suggestions are shown on desktop for credit card related fields -// even if the initiating field field has the "autocomplete" attribute set to -// off. +// even if the initiating field has the "autocomplete" attribute set to off. TEST_P(BrowserAutofillManagerStructuredProfileTest, DisplaySuggestions_AutocompleteOff_CreditCardField) { // Set up a credit card form. @@ -8107,7 +8150,15 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, // Suggestions should always be displayed. for (const FormFieldData& mixed_form_field : mixed_form.fields) { + // Single field form fill suggestions being returned are directly correlated + // to whether or not the field has autocomplete set to true or false. We + // know autocomplete must be the single field form filler in this case due + // to the field not having a type that would route to any of the other + // single field form fillers. + ON_CALL(*single_field_form_fill_router_, OnGetSingleFieldSuggestions) + .WillByDefault(testing::Return(mixed_form_field.should_autocomplete)); GetAutofillSuggestions(mixed_form, mixed_form_field); + EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen()); } } @@ -8130,7 +8181,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, test::CreateTestFormField("Field 3", "field3", "", "text", &field); form.fields.push_back(field); - auto form_structure = std::make_unique<TestFormStructure>(form); + auto form_structure = std::make_unique<FormStructure>(form); form_structure->DetermineHeuristicTypes(nullptr, nullptr); // Make sure the form can not be autofilled now. ASSERT_EQ(0u, form_structure->autofill_count()); @@ -8143,7 +8194,8 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, UNKNOWN_TYPE); const std::vector<ServerFieldType> server_types{NAME_FIRST, NAME_MIDDLE, NAME_LAST}; - form_structure->SetFieldTypes(heuristic_types, server_types); + FormStructureTestApi(form_structure.get()) + .SetFieldTypes(heuristic_types, server_types); browser_autofill_manager_->AddSeenFormStructure(std::move(form_structure)); // Make sure the form can be autofilled. @@ -8248,10 +8300,6 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, TEST_P(BrowserAutofillManagerStructuredProfileTest, GetCreditCardSuggestions_VirtualCard) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndDisableFeature( - features::kAutofillSuggestVirtualCardsOnIncompleteForm); - personal_data().ClearCreditCards(); CreditCard masked_server_card(CreditCard::MASKED_SERVER_CARD, /*server_id=*/"a123"); @@ -8320,7 +8368,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, GetAutofillSuggestions(form, field); CheckSuggestions( - kDefaultPageID, + kDefaultPageID, virtual_card_suggestion, Suggestion("Elvis Presley", label, kVisaCard, browser_autofill_manager_->GetPackedCreditCardID(7))); } @@ -8333,6 +8381,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormFieldData field; test::CreateTestFormField("Something", "something", "", "text", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8353,6 +8402,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, DidShowSuggestions_LogAutofillAddressShownMetric) { FormData form; test::CreateTestAddressFormData(&form); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8398,6 +8448,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Address Line 1", "addr1", "", "text", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8442,6 +8493,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Postal Code", "zipcode", "", "text", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8486,6 +8538,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Email", "email", "", "email", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8529,6 +8582,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Email", "email", "", "email", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8572,6 +8626,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Phone Number", "phonenumber", "", "tel", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8615,6 +8670,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Last Name", "lastname", "", "text", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8660,6 +8716,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Email", "email", "", "email", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8710,6 +8767,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Email", "email", "", "email", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8762,6 +8820,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Phone Number", "phonenumber", "", "tel", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8812,6 +8871,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Phone Number", "phonenumber", "", "tel", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8866,6 +8926,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Email", "email", "", "email", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8915,6 +8976,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, form.fields.push_back(field); test::CreateTestFormField("Email", "email", "", "email", &field); form.fields.push_back(field); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8948,6 +9010,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, DidShowSuggestions_LogAutofillCreditCardShownMetric) { FormData form; CreateTestCreditCardFormData(&form, true, false); + FormsSeen({form}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidShowSuggestions( @@ -8976,6 +9039,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, DidSuppressPopup_LogAutofillAddressPopupSuppressed) { FormData form; test::CreateTestAddressFormData(&form); + browser_autofill_manager_->OnFormsSeen({form}, {}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidSuppressPopup(form, form.fields[0]); @@ -8995,6 +9059,7 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormData form; CreateTestCreditCardFormData(&form, true, false); + browser_autofill_manager_->OnFormsSeen({form}, {}); base::HistogramTester histogram_tester; browser_autofill_manager_->DidSuppressPopup(form, form.fields[0]); histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard", @@ -9099,11 +9164,24 @@ TEST_F(BrowserAutofillManagerTest, PageLanguageGetsCorrectlySet) { ASSERT_EQ(LanguageCode("zh"), parsed_form->current_page_language()); } -TEST_F(BrowserAutofillManagerTest, PageLanguageGetsCorrectlyDetected) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - features::kAutofillPageLanguageDetection); +// Test language detection on frames depending on whether the frame is active or +// not. +class BrowserAutofillManagerTestPageLanguageDetection + : public BrowserAutofillManagerTest, + public testing::WithParamInterface<bool> { + public: + BrowserAutofillManagerTestPageLanguageDetection() { + scoped_features_.InitAndEnableFeature( + features::kAutofillPageLanguageDetection); + } + bool is_in_active_frame() const { return GetParam(); }; + + private: + base::test::ScopedFeatureList scoped_features_; +}; + +TEST_P(BrowserAutofillManagerTestPageLanguageDetection, GetsCorrectlyDetected) { FormData form; test::CreateTestAddressFormData(&form); @@ -9115,17 +9193,26 @@ TEST_F(BrowserAutofillManagerTest, PageLanguageGetsCorrectlyDetected) { ASSERT_EQ(LanguageCode(), parsed_form->current_page_language()); translate::LanguageDetectionDetails language_detection_details; - language_detection_details.adopted_language = "zh"; + language_detection_details.adopted_language = "hu"; + autofill_driver_->SetIsInActiveFrame(is_in_active_frame()); browser_autofill_manager_->OnLanguageDetermined(language_detection_details); - autofill_client_.GetLanguageState()->SetCurrentLanguage("zh"); + autofill_client_.GetLanguageState()->SetCurrentLanguage("hu"); parsed_form = browser_autofill_manager_->FindCachedFormByRendererId(form.global_id()); - ASSERT_EQ(LanguageCode("zh"), parsed_form->current_page_language()); + // Language detection is used only for active frames. + auto expected_language_code = + is_in_active_frame() ? LanguageCode("hu") : LanguageCode(); + + ASSERT_EQ(*expected_language_code, *parsed_form->current_page_language()); } +INSTANTIATE_TEST_SUITE_P(All, + BrowserAutofillManagerTestPageLanguageDetection, + testing::Bool()); + // BrowserAutofillManagerTest with different browser profile types. class BrowserAutofillManagerProfileMetricsTest : public BrowserAutofillManagerTest, @@ -9146,8 +9233,7 @@ TEST_P(BrowserAutofillManagerProfileMetricsTest, // Set up our form data. FormData form; test::CreateTestAddressFormData(&form); - std::vector<FormData> forms(1, form); - FormsSeen(forms); + FormsSeen({form}); FormFieldData field = form.fields[0]; GetAutofillSuggestions(form, field); @@ -9167,6 +9253,70 @@ INSTANTIATE_TEST_SUITE_P( profile_metrics::BrowserProfileType::kIncognito, profile_metrics::BrowserProfileType::kGuest})); +// Tests that autocomplete-related metrics are emitted correctly on form +// submission. +TEST_F(BrowserAutofillManagerTest, AutocompleteMetrics) { + // `kAutocompleteValues` corresponds to empty, valid, garbage and off. + constexpr base::StringPiece kAutocompleteValues[]{"", "name", "asdf", "off"}; + // The 4 possible combinations of heuristic and server type status: + // - Neither a fillable heuristic type nor a fillable server type. + // - Only a fillable server type. + // - Only a fillable heuristic type. + // - Both a fillable heuristic type and a fillable server type. + // NO_SERVER_DATA and UNKNOWN_TYPE are both unfillable types, but + // NO_SERVER_DATA is ignored in the PredictionCollisionType metric. + constexpr ServerFieldType kTypeClasses[][2]{ + {UNKNOWN_TYPE, NO_SERVER_DATA}, + {UNKNOWN_TYPE, EMAIL_ADDRESS}, + {ADDRESS_HOME_COUNTRY, UNKNOWN_TYPE}, + {ADDRESS_HOME_COUNTRY, EMAIL_ADDRESS}}; + + // Create a form with one field per kAutofillValue x kTypeClass combination. + FormData form; + form.name = u"MyForm"; + form.url = GURL("https://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); + std::vector<ServerFieldType> heuristic_types, server_types; + for (const auto& autocomplete : kAutocompleteValues) { + for (const auto& types : kTypeClasses) { + FormFieldData field; + test::CreateTestFormField("", "", "", "text", &field); + field.autocomplete_attribute = std::string(autocomplete); + form.fields.push_back(field); + heuristic_types.push_back(types[0]); + server_types.push_back(types[1]); + } + } + // Override the types and simulate seeing the form on page load. + auto form_structure = std::make_unique<FormStructure>(form); + form_structure->DetermineHeuristicTypes(nullptr, nullptr); + FormStructureTestApi(form_structure.get()) + .SetFieldTypes(heuristic_types, server_types); + browser_autofill_manager_->AddSeenFormStructure(std::move(form_structure)); + + // Submit the form and verify that all metrics are collected correctly. + base::HistogramTester histogram_tester; + FormSubmitted(form); + + // Expect one entry for each possible PredictionStateAutocompleteStatePair. + // Fields without type predictions and autocomplete attributes are ignored. + histogram_tester.ExpectTotalCount( + "Autofill.Autocomplete.PredictionCollisionState", form.fields.size() - 1); + for (int i = 0; i < 15; i++) { + histogram_tester.ExpectBucketCount( + "Autofill.Autocomplete.PredictionCollisionState", i, 1); + } + + // The PredictionCollisionType metric is collected for every field with + // autocomplete=garbage. This amounts 1 vote per `kTypeClass` here. + // Fields NO_SERVER_DATA are ignored for the .Server metric. + const std::string kTypeHistogram = + "Autofill.Autocomplete.PredictionCollisionType."; + histogram_tester.ExpectTotalCount(kTypeHistogram + "Heuristics", 4); + histogram_tester.ExpectTotalCount(kTypeHistogram + "Server", 3); + histogram_tester.ExpectTotalCount(kTypeHistogram + "ServerOrHeuristics", 4); +} + // Test that if a form is mixed content we show a warning instead of any // suggestions. TEST_F(BrowserAutofillManagerTest, GetSuggestions_MixedForm) { @@ -9399,6 +9549,22 @@ TEST_F(BrowserAutofillManagerTest, AutofillOverridePrefilledValue) { EXPECT_EQ(response_data.fields[3].value, u"Test Country"); } +// Tests that both Autofill popup and TTF are hidden on renderer event. +TEST_F(BrowserAutofillManagerTest, HideAutofillPopupAndTouchToFillOnHidePopup) { + EXPECT_CALL(autofill_client_, + HideAutofillPopup(PopupHidingReason::kRendererEvent)); + EXPECT_CALL(*touch_to_fill_delegate_, HideTouchToFill); + browser_autofill_manager_->OnHidePopup(); +} + +// Tests that only Autofill popup is hidden on editing end, but not TTF. +TEST_F(BrowserAutofillManagerTest, OnDidEndTextFieldEditing) { + EXPECT_CALL(autofill_client_, + HideAutofillPopup(PopupHidingReason::kEndEditing)); + EXPECT_CALL(*touch_to_fill_delegate_, HideTouchToFill).Times(0); + browser_autofill_manager_->OnDidEndTextFieldEditing(); +} + // Tests that Autofill suggestions are not shown if TTF is eligible and shown. TEST_F(BrowserAutofillManagerTest, AutofillSuggestionsOrTouchToFill) { FormData form; @@ -9428,6 +9594,24 @@ TEST_F(BrowserAutofillManagerTest, AutofillSuggestionsOrTouchToFill) { EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen()); } +// Tests that neither Autofill suggestions nor TTF is triggered if TTF is +// already shown. +TEST_F(BrowserAutofillManagerTest, ShowNothingIfTouchToFillAlreadyShown) { + FormData form; + CreateTestCreditCardFormData(&form, /*is_https=*/true, + /*use_month_type=*/false); + FormsSeen({form}); + const FormFieldData& field = form.fields[1]; + + EXPECT_CALL(*touch_to_fill_delegate_, IsShowingTouchToFill) + .WillOnce(Return(true)); + EXPECT_CALL(*touch_to_fill_delegate_, + TryToShowTouchToFill(kDefaultPageID, _, _)) + .Times(0); + TryToShowTouchToFill(kDefaultPageID, form, field, TouchToFillEligible(true)); + EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen()); +} + // Desktop only tests. #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) class BrowserAutofillManagerTestForVirtualCardOption @@ -10099,7 +10283,7 @@ TEST_P(BrowserAutofillManagerRefillTest, // Simulate that JavaScript modifies the expiration date field. FormData form_after_js_modification = first_fill_data; form_after_js_modification.fields[2].value = test_case.exp_date_from_js; - browser_autofill_manager_->JavaScriptChangedAutofilledValue( + browser_autofill_manager_->OnJavaScriptChangedAutofilledValue( form_after_js_modification, form_after_js_modification.fields[2], u"04/2999"); diff --git a/chromium/components/autofill/core/browser/crypto/rc4_decryptor.h b/chromium/components/autofill/core/browser/crypto/rc4_decryptor.h deleted file mode 100644 index 4598274fc2e..00000000000 --- a/chromium/components/autofill/core/browser/crypto/rc4_decryptor.h +++ /dev/null @@ -1,112 +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_BROWSER_CRYPTO_RC4_DECRYPTOR_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_CRYPTO_RC4_DECRYPTOR_H_ - -#include <stdint.h> -#include <string.h> - -#include <memory> -#include <string> - -namespace autofill { - -// This is modified RC4 decryption used for import of Toolbar autofill data -// only. The difference from the Crypto Api implementation is twofold: -// First, it uses a non-standard key size (160 bit), not supported by Microsoft -// (it supports only 40 and 128 bit for RC4). Second, it codes 128 words with -// value 0x0020 at the beginning of the code to enhance security. -// -// This class used in -// components/autofill/core/browser/autofill_ie_toolbar_import_win.cc. -// -// This class should not be used anywhere else!!! -class RC4Decryptor { - public: - explicit RC4Decryptor(wchar_t const* password) { - PrepareKey(reinterpret_cast<const uint8_t*>(password), - wcslen(password) * sizeof(wchar_t)); - std::wstring data; - // First 128 bytes should be spaces. - data.resize(128, L' '); - Run(data.c_str()); - } - - // Run the algorithm - std::wstring Run(const std::wstring& data) { - int data_size = data.length() * sizeof(wchar_t); - - std::unique_ptr<wchar_t[]> buffer(new wchar_t[data.length() + 1]); - memset(buffer.get(), 0, (data.length() + 1) * sizeof(wchar_t)); - memcpy(buffer.get(), data.c_str(), data_size); - - RunInternal(reinterpret_cast<uint8_t*>(buffer.get()), data_size); - - std::wstring result(buffer.get()); - - // Clear the memory - memset(buffer.get(), 0, data_size); - return result; - } - - private: - static const int kKeyDataSize = 256; - struct Rc4Key { - uint8_t state[kKeyDataSize]; - uint8_t x; - uint8_t y; - }; - - void SwapByte(uint8_t* byte1, uint8_t* byte2) { - uint8_t temp = *byte1; - *byte1 = *byte2; - *byte2 = temp; - } - - void PrepareKey(const uint8_t* key_data, int key_data_len) { - uint8_t index1 = 0; - uint8_t index2 = 0; - uint8_t* state; - short counter; - - state = &key_.state[0]; - for (counter = 0; counter < kKeyDataSize; ++counter) - state[counter] = static_cast<uint8_t>(counter); - - key_.x = key_.y = 0; - - for (counter = 0; counter < kKeyDataSize; counter++) { - index2 = (key_data[index1] + state[counter] + index2) % kKeyDataSize; - SwapByte(&state[counter], &state[index2]); - index1 = (index1 + 1) % key_data_len; - } - } - - void RunInternal(uint8_t* buffer, int buffer_len) { - uint8_t x, y; - uint8_t xor_index = 0; - uint8_t* state; - int counter; - - x = key_.x; - y = key_.y; - state = &key_.state[0]; - for (counter = 0; counter < buffer_len; ++counter) { - x = (x + 1) % kKeyDataSize; - y = (state[x] + y) % kKeyDataSize; - SwapByte(&state[x], &state[y]); - xor_index = (state[x] + state[y]) % kKeyDataSize; - buffer[counter] ^= state[xor_index]; - } - key_.x = x; - key_.y = y; - } - - Rc4Key key_; -}; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_CRYPTO_RC4_DECRYPTOR_H_ 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 631f9191bf0..f244e931a31 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 @@ -83,7 +83,7 @@ AutofillMetadata AutofillDataModel::GetMetadata() const { return metadata; } -bool AutofillDataModel::SetMetadata(const AutofillMetadata metadata) { +bool AutofillDataModel::SetMetadata(const AutofillMetadata& metadata) { use_count_ = metadata.use_count; use_date_ = metadata.use_date; return true; diff --git a/chromium/components/autofill/core/browser/data_model/autofill_data_model.h b/chromium/components/autofill/core/browser/data_model/autofill_data_model.h index 0219cd73ffa..392d5b391a9 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_data_model.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_data_model.h @@ -88,7 +88,7 @@ class AutofillDataModel : public FormGroup { // Sets the |use_count_| and |use_date_| of this autofill data model. Returns // whether the metadata was set. - virtual bool SetMetadata(const AutofillMetadata metadata); + virtual bool SetMetadata(const AutofillMetadata& metadata); // Returns whether the data model is deletable: if it has not been used for // longer than |kDisusedCreditCardDeletionTimeDelta|. 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 a6a61be5b64..62ffaa67bdb 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile.cc @@ -286,7 +286,7 @@ AutofillMetadata AutofillProfile::GetMetadata() const { return metadata; } -bool AutofillProfile::SetMetadata(const AutofillMetadata metadata) { +bool AutofillProfile::SetMetadata(const AutofillMetadata& metadata) { // Make sure the ids matches. if (metadata.id != (record_type_ == LOCAL_PROFILE ? guid() : server_id_)) return false; diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile.h b/chromium/components/autofill/core/browser/data_model/autofill_profile.h index 6099221f2f9..63949ac312f 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile.h @@ -56,7 +56,7 @@ class AutofillProfile : public AutofillDataModel { // AutofillDataModel: AutofillMetadata GetMetadata() const override; - bool SetMetadata(const AutofillMetadata metadata) override; + bool SetMetadata(const AutofillMetadata& metadata) override; // Returns whether the profile is deletable: if it is not verified and has not // been used for longer than |kDisusedAddressDeletionTimeDelta|. bool IsDeletable() const override; 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 f24164ecae5..210e0dda5a4 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 @@ -43,8 +43,6 @@ std::ostream& operator<<(std::ostream& os, const ::i18n::phonenumbers::PhoneNumber& n) { os << "country_code: " << n.country_code() << " " << "national_number: " << n.national_number(); - if (n.has_extension()) - os << " extension: \"" << n.extension() << "\""; if (n.has_italian_leading_zero()) os << " italian_leading_zero: " << n.italian_leading_zero(); if (n.has_number_of_leading_zeros()) @@ -718,11 +716,6 @@ bool AutofillProfileComparator::MergePhoneNumbers( HasInternationalCountryCode(n1) ? n1.country_code() : n2.country_code()); merged_number.set_national_number( std::max(n1.national_number(), n2.national_number())); - if (n1.has_extension() && !n1.extension().empty()) { - merged_number.set_extension(n1.extension()); - } else if (n2.has_extension() && !n2.extension().empty()) { - merged_number.set_extension(n2.extension()); - } if (n1.has_italian_leading_zero() || n2.has_italian_leading_zero()) { merged_number.set_italian_leading_zero(n1.italian_leading_zero() || n2.italian_leading_zero()); @@ -1011,10 +1004,6 @@ bool AutofillProfileComparator::MergeAddresses(const AutofillProfile& p1, bool AutofillProfileComparator::MergeBirthdates(const AutofillProfile& p1, const AutofillProfile& p2, Birthdate& birthdate) const { - if (!base::FeatureList::IsEnabled( - features::kAutofillEnableCompatibilitySupportForBirthdates)) { - return true; - } DCHECK(HaveMergeableBirthdates(p1, p2)); for (ServerFieldType component : Birthdate::GetRawComponents()) { @@ -1475,10 +1464,6 @@ bool AutofillProfileComparator::HaveMergeableAddresses( bool AutofillProfileComparator::HaveMergeableBirthdates( const AutofillProfile& p1, const AutofillProfile& p2) const { - if (!base::FeatureList::IsEnabled( - features::kAutofillEnableCompatibilitySupportForBirthdates)) { - return true; - } return base::ranges::all_of( Birthdate::GetRawComponents(), [&](ServerFieldType component) { const std::u16string& component1 = p1.GetInfo(component, app_locale_); diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc index 21015691393..e1f91f5c32b 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc @@ -32,9 +32,9 @@ using autofill::ADDRESS_HOME_SORTING_CODE; using autofill::ADDRESS_HOME_STATE; using autofill::ADDRESS_HOME_STREET_ADDRESS; using autofill::ADDRESS_HOME_ZIP; +using autofill::BIRTHDATE_4_DIGIT_YEAR; using autofill::BIRTHDATE_DAY; using autofill::BIRTHDATE_MONTH; -using autofill::BIRTHDATE_YEAR_4_DIGITS; using autofill::COMPANY_NAME; using autofill::EMAIL_ADDRESS; using autofill::NAME_FIRST; @@ -45,7 +45,6 @@ using autofill::NAME_MIDDLE; using autofill::PHONE_HOME_CITY_AND_NUMBER; using autofill::PHONE_HOME_CITY_CODE; using autofill::PHONE_HOME_COUNTRY_CODE; -using autofill::PHONE_HOME_EXTENSION; using autofill::PHONE_HOME_NUMBER; using autofill::PHONE_HOME_WHOLE_NUMBER; @@ -193,7 +192,7 @@ class AutofillProfileComparatorTest AutofillProfile profile(base::GenerateGUID(), "http://www.example.com/"); profile.SetRawInfo(BIRTHDATE_DAY, base::UTF8ToUTF16(day)); profile.SetRawInfo(BIRTHDATE_MONTH, base::UTF8ToUTF16(month)); - profile.SetRawInfo(BIRTHDATE_YEAR_4_DIGITS, base::UTF8ToUTF16(year)); + profile.SetRawInfo(BIRTHDATE_4_DIGIT_YEAR, base::UTF8ToUTF16(year)); return profile; } @@ -276,8 +275,6 @@ class AutofillProfileComparatorTest actual.GetInfo(AutofillType(PHONE_HOME_CITY_CODE), kLocale)); EXPECT_EQ(expected.GetInfo(AutofillType(PHONE_HOME_NUMBER), kLocale), actual.GetInfo(AutofillType(PHONE_HOME_NUMBER), kLocale)); - EXPECT_EQ(expected.GetInfo(AutofillType(PHONE_HOME_EXTENSION), kLocale), - actual.GetInfo(AutofillType(PHONE_HOME_EXTENSION), kLocale)); } void MergeAddressesAndExpect(const AutofillProfile& a, @@ -653,30 +650,14 @@ TEST_P(AutofillProfileComparatorTest, HaveMergeableCompanyNames) { TEST_P(AutofillProfileComparatorTest, HaveMergeablePhoneNumbers) { AutofillProfile empty = CreateProfileWithPhoneNumber(""); AutofillProfile p1 = CreateProfileWithPhoneNumber("+1 (800) 670-8700"); - AutofillProfile p2 = CreateProfileWithPhoneNumber("800.670.8700x321"); - AutofillProfile p3 = CreateProfileWithPhoneNumber("670-8700 ext321"); - AutofillProfile p4 = CreateProfileWithPhoneNumber("6708700"); + AutofillProfile p2 = CreateProfileWithPhoneNumber("6708700"); AutofillProfile different = CreateProfileWithPhoneNumber("1-800-321-4567"); EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p1, p1)); EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p1, p2)); - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p1, p3)); - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p1, p4)); EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p2, p1)); EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p2, p2)); - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p2, p3)); - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p2, p4)); - - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p3, p1)); - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p3, p2)); - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p3, p3)); - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p3, p4)); - - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p4, p1)); - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p4, p2)); - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p4, p3)); - EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p4, p4)); EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(p1, empty)); EXPECT_TRUE(comparator_.HaveMergeablePhoneNumbers(empty, p2)); @@ -742,10 +723,6 @@ TEST_P(AutofillProfileComparatorTest, HaveMergeableAddresses) { } TEST_P(AutofillProfileComparatorTest, HaveMergeableBirthdates) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature( - autofill::features::kAutofillEnableCompatibilitySupportForBirthdates); - // Birthdates are mergeable if the components are either equal or one of them // is empty. AutofillProfile p1 = CreateProfileWithBirthdate("14", "", "1997"); @@ -780,7 +757,7 @@ TEST_P(AutofillProfileComparatorTest, AreMergeable) { {ADDRESS_HOME_LINE1, u"123 zoo st. w., #5"}, {ADDRESS_HOME_LINE1, u""}, {ADDRESS_HOME_STATE, u"california"}, - {PHONE_HOME_WHOLE_NUMBER, u"5678910 ext. 77"}}); + {PHONE_HOME_WHOLE_NUMBER, u"5678910"}}); AutofillProfile not_mergeable_by_name = CopyAndModify(p, {{NAME_FIRST, u"Steven"}, {NAME_FULL, u""}, @@ -1074,143 +1051,50 @@ TEST_P(AutofillProfileComparatorTest, MergeCompanyNames) { } TEST_P(AutofillProfileComparatorTest, MergePhoneNumbers_NA) { - static const char kPhoneA[] = "5550199"; - static const char16_t kPhoneA16[] = u"5550199"; - static const char kPhoneB[] = "555.0199"; - static const char16_t kPhoneB16[] = u"555.0199"; - static const char kPhoneC[] = "555-0199 ext321"; - static const char16_t kPhoneC16[] = u"555-0199 ext321"; - static const char kPhoneD[] = "8005550199"; - static const char16_t kPhoneD16[] = u"8005550199"; - static const char kPhoneE[] = "800-555-0199 #321"; - static const char16_t kPhoneE16[] = u"800-555-0199 #321"; - static const char kPhoneF[] = "1-800-555-0199 #321"; - static const char16_t kPhoneF16[] = u"1-800-555-0199 #321"; - static const char kPhoneG[] = "+1 (800) 555.0199;ext=321"; - static const char16_t kPhoneG16[] = u"+1 (800) 555.0199;ext=321"; - static const char16_t kMergedShortNumber[] = u"555-0199"; - static const char16_t kMergedShortNumberExt[] = u"555-0199 ext. 321"; - static const char16_t kMergedNationalNumber[] = u"(800) 555-0199"; - static const char16_t kMergedNationalNumberExt[] = u"(800) 555-0199 ext. 321"; - static const char16_t kMergedFullNumberExt[] = u"+1 800-555-0199 ext. 321"; - - AutofillProfile profile_a = CreateProfileWithPhoneNumber(kPhoneA); - AutofillProfile profile_b = CreateProfileWithPhoneNumber(kPhoneB); - AutofillProfile profile_c = CreateProfileWithPhoneNumber(kPhoneC); - AutofillProfile profile_d = CreateProfileWithPhoneNumber(kPhoneD); - AutofillProfile profile_e = CreateProfileWithPhoneNumber(kPhoneE); - AutofillProfile profile_f = CreateProfileWithPhoneNumber(kPhoneF); - AutofillProfile profile_g = CreateProfileWithPhoneNumber(kPhoneG); + AutofillProfile profile_a = CreateProfileWithPhoneNumber("5550199"); + AutofillProfile profile_b = CreateProfileWithPhoneNumber("555.0199"); + AutofillProfile profile_c = CreateProfileWithPhoneNumber("8005550199"); // Profile A - MergePhoneNumbersAndExpect(profile_a, profile_a, kPhoneA16); - MergePhoneNumbersAndExpect(profile_a, profile_b, kMergedShortNumber); - MergePhoneNumbersAndExpect(profile_a, profile_c, kMergedShortNumberExt); - MergePhoneNumbersAndExpect(profile_a, profile_d, kMergedNationalNumber); - MergePhoneNumbersAndExpect(profile_a, profile_e, kMergedNationalNumberExt); - MergePhoneNumbersAndExpect(profile_a, profile_f, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_a, profile_g, kMergedFullNumberExt); + MergePhoneNumbersAndExpect(profile_a, profile_a, u"5550199"); + MergePhoneNumbersAndExpect(profile_a, profile_b, u"555-0199"); + MergePhoneNumbersAndExpect(profile_a, profile_c, u"(800) 555-0199"); // Profile B - MergePhoneNumbersAndExpect(profile_b, profile_a, kMergedShortNumber); - MergePhoneNumbersAndExpect(profile_b, profile_b, kPhoneB16); - MergePhoneNumbersAndExpect(profile_b, profile_c, kMergedShortNumberExt); - MergePhoneNumbersAndExpect(profile_b, profile_d, kMergedNationalNumber); - MergePhoneNumbersAndExpect(profile_b, profile_e, kMergedNationalNumberExt); - MergePhoneNumbersAndExpect(profile_b, profile_f, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_b, profile_g, kMergedFullNumberExt); - - // Profile C - MergePhoneNumbersAndExpect(profile_c, profile_a, kMergedShortNumberExt); - MergePhoneNumbersAndExpect(profile_c, profile_b, kMergedShortNumberExt); - MergePhoneNumbersAndExpect(profile_c, profile_c, kPhoneC16); - MergePhoneNumbersAndExpect(profile_c, profile_d, kMergedNationalNumberExt); - MergePhoneNumbersAndExpect(profile_c, profile_e, kMergedNationalNumberExt); - MergePhoneNumbersAndExpect(profile_c, profile_f, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_c, profile_g, kMergedFullNumberExt); + MergePhoneNumbersAndExpect(profile_b, profile_a, u"555-0199"); + MergePhoneNumbersAndExpect(profile_b, profile_b, u"555.0199"); + MergePhoneNumbersAndExpect(profile_b, profile_c, u"(800) 555-0199"); // Profile D - MergePhoneNumbersAndExpect(profile_d, profile_a, kMergedNationalNumber); - MergePhoneNumbersAndExpect(profile_d, profile_b, kMergedNationalNumber); - MergePhoneNumbersAndExpect(profile_d, profile_c, kMergedNationalNumberExt); - MergePhoneNumbersAndExpect(profile_d, profile_d, kPhoneD16); - MergePhoneNumbersAndExpect(profile_d, profile_e, kMergedNationalNumberExt); - MergePhoneNumbersAndExpect(profile_d, profile_f, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_d, profile_g, kMergedFullNumberExt); - - // Profile E - MergePhoneNumbersAndExpect(profile_e, profile_a, kMergedNationalNumberExt); - MergePhoneNumbersAndExpect(profile_e, profile_b, kMergedNationalNumberExt); - MergePhoneNumbersAndExpect(profile_e, profile_c, kMergedNationalNumberExt); - MergePhoneNumbersAndExpect(profile_e, profile_d, kMergedNationalNumberExt); - MergePhoneNumbersAndExpect(profile_e, profile_e, kPhoneE16); - MergePhoneNumbersAndExpect(profile_e, profile_f, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_e, profile_g, kMergedFullNumberExt); - - // Profile F - MergePhoneNumbersAndExpect(profile_f, profile_a, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_f, profile_b, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_f, profile_c, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_f, profile_d, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_f, profile_e, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_f, profile_f, kPhoneF16); - MergePhoneNumbersAndExpect(profile_f, profile_g, kMergedFullNumberExt); - - // Profile G - MergePhoneNumbersAndExpect(profile_g, profile_a, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_g, profile_b, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_g, profile_c, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_g, profile_d, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_g, profile_e, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_g, profile_f, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_g, profile_g, kPhoneG16); + MergePhoneNumbersAndExpect(profile_c, profile_a, u"(800) 555-0199"); + MergePhoneNumbersAndExpect(profile_c, profile_b, u"(800) 555-0199"); + MergePhoneNumbersAndExpect(profile_c, profile_c, u"8005550199"); } TEST_P(AutofillProfileComparatorTest, MergePhoneNumbers_Intl) { - const std::u16string kGermany = u"DE"; - const AutofillType kCountry(ADDRESS_HOME_COUNTRY); - - static const char kPhoneA[] = "+49492180185611"; - static const char16_t kPhoneA16[] = u"+49492180185611"; - static const char kPhoneB[] = "+49 4921 801 856-11"; - static const char16_t kPhoneB16[] = u"+49 4921 801 856-11"; - static const char kPhoneC[] = "+49 4921 8018 5611;ext=22"; - static const char16_t kPhoneC16[] = u"+49 4921 8018 5611;ext=22"; - static const char kPhoneD[] = "04921 80185611"; // National Format. - static const char16_t kPhoneD16[] = u"04921 80185611"; // National Format. - static const char16_t kMergedFullNumber[] = u"+49 4921 80185611"; - static const char16_t kMergedFullNumberExt[] = u"+49 4921 80185611 ext. 22"; - - AutofillProfile profile_a = CreateProfileWithPhoneNumber(kPhoneA); - AutofillProfile profile_b = CreateProfileWithPhoneNumber(kPhoneB); - AutofillProfile profile_c = CreateProfileWithPhoneNumber(kPhoneC); - AutofillProfile profile_d = CreateProfileWithPhoneNumber(kPhoneD); - - profile_a.SetInfo(kCountry, kGermany, kLocale); - profile_b.SetInfo(kCountry, kGermany, kLocale); - profile_c.SetInfo(kCountry, kGermany, kLocale); - profile_d.SetInfo(kCountry, kGermany, kLocale); + AutofillProfile profile_a = CreateProfileWithPhoneNumber("+49492180185611"); + AutofillProfile profile_b = + CreateProfileWithPhoneNumber("+49 4921 801 856-11"); + AutofillProfile profile_c = CreateProfileWithPhoneNumber("04921 80185611"); + + profile_a.SetInfo(ADDRESS_HOME_COUNTRY, u"DE", kLocale); + profile_b.SetInfo(ADDRESS_HOME_COUNTRY, u"DE", kLocale); + profile_c.SetInfo(ADDRESS_HOME_COUNTRY, u"DE", kLocale); // Profile A - MergePhoneNumbersAndExpect(profile_a, profile_a, kPhoneA16); - MergePhoneNumbersAndExpect(profile_a, profile_b, kMergedFullNumber); - MergePhoneNumbersAndExpect(profile_a, profile_c, kMergedFullNumberExt); + MergePhoneNumbersAndExpect(profile_a, profile_a, u"+49492180185611"); + MergePhoneNumbersAndExpect(profile_a, profile_b, u"+49 4921 80185611"); + MergePhoneNumbersAndExpect(profile_a, profile_c, u"+49 4921 80185611"); // Profile B - MergePhoneNumbersAndExpect(profile_b, profile_a, kMergedFullNumber); - MergePhoneNumbersAndExpect(profile_b, profile_b, kPhoneB16); - MergePhoneNumbersAndExpect(profile_b, profile_c, kMergedFullNumberExt); - - // Profile C - MergePhoneNumbersAndExpect(profile_c, profile_a, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_c, profile_b, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_c, profile_c, kPhoneC16); + MergePhoneNumbersAndExpect(profile_b, profile_a, u"+49 4921 80185611"); + MergePhoneNumbersAndExpect(profile_b, profile_b, u"+49 4921 801 856-11"); + MergePhoneNumbersAndExpect(profile_b, profile_c, u"+49 4921 80185611"); // Profile D - MergePhoneNumbersAndExpect(profile_d, profile_a, kMergedFullNumber); - MergePhoneNumbersAndExpect(profile_d, profile_b, kMergedFullNumber); - MergePhoneNumbersAndExpect(profile_d, profile_c, kMergedFullNumberExt); - MergePhoneNumbersAndExpect(profile_d, profile_d, kPhoneD16); + MergePhoneNumbersAndExpect(profile_c, profile_a, u"+49 4921 80185611"); + MergePhoneNumbersAndExpect(profile_c, profile_b, u"+49 4921 80185611"); + MergePhoneNumbersAndExpect(profile_c, profile_c, u"04921 80185611"); } TEST_P(AutofillProfileComparatorTest, MergeAddresses) { @@ -1364,17 +1248,13 @@ TEST_P(AutofillProfileComparatorTest, } TEST_P(AutofillProfileComparatorTest, MergeBirthdates) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature( - autofill::features::kAutofillEnableCompatibilitySupportForBirthdates); - AutofillProfile profile1 = CreateProfileWithBirthdate("14", "", "1997"); AutofillProfile profile2 = CreateProfileWithBirthdate("", "3", "1997"); Birthdate expected; expected.SetRawInfo(BIRTHDATE_DAY, u"14"); expected.SetRawInfo(BIRTHDATE_MONTH, u"3"); - expected.SetRawInfo(BIRTHDATE_YEAR_4_DIGITS, u"1997"); + expected.SetRawInfo(BIRTHDATE_4_DIGIT_YEAR, u"1997"); Birthdate actual; EXPECT_TRUE(comparator_.MergeBirthdates(profile1, profile2, actual)); @@ -1386,20 +1266,29 @@ TEST_P(AutofillProfileComparatorTest, MergeBirthdates) { // Checks for various scenarios for determining mergeability of profiles w.r.t. // the state. TEST_P(AutofillProfileComparatorTest, CheckStatesMergeability) { + // |kAutofillEnableSupportForMoreStructureInAddresses| is not compatible with + // AlternativeStateNameMap merging logic. + if (structured_addresses_enabled_) + return; + base::test::ScopedFeatureList feature; feature.InitAndEnableFeature( autofill::features::kAutofillUseAlternativeStateNameMap); - autofill::test::ClearAlternativeStateNameMapForTesting(); - autofill::test::PopulateAlternativeStateNameMapForTesting(); + autofill::test::PopulateAlternativeStateNameMapForTesting( + "DE", "RandomState", + {{.canonical_name = "RandomState", + .abbreviations = {"RS"}, + .alternative_names = {"AlternateRandomState"}}}); AutofillProfile empty = CreateProfileWithAddress("", "", "", "", "", "DE"); - AutofillProfile p1 = CreateProfileWithAddress("", "", "", "Bayern", "", "DE"); + AutofillProfile p1 = + CreateProfileWithAddress("", "", "", "RandomState", "", "DE"); AutofillProfile p2 = CreateProfileWithAddress("", "", "", "Random", "", "DE"); - AutofillProfile p3 = - CreateProfileWithAddress("", "", "", "Bayern - BY - Bavaria", "", "DE"); + AutofillProfile p3 = CreateProfileWithAddress( + "", "", "", "RandomState - RS - AlternateRandomState", "", "DE"); AutofillProfile p4 = - CreateProfileWithAddress("", "", "", "Bavaria", "", "DE"); + CreateProfileWithAddress("", "", "", "AlternateRandomState", "", "DE"); EXPECT_TRUE(comparator_.HaveMergeableAddresses(empty, empty)); EXPECT_TRUE(comparator_.HaveMergeableAddresses(p1, empty)); @@ -1696,13 +1585,8 @@ TEST_P(AutofillProfileComparatorTest, GetMergeCandidate) { // Tests that the profiles are merged when they have common states. TEST_P(AutofillProfileComparatorTest, MergeProfilesBasedOnState) { base::test::ScopedFeatureList feature; - // The feature - // |autofill::features::kAutofillEnableSupportForMoreStructureInAddresses| is - // disabled since it is incompatible with the feature - // |autofill::features::kAutofillUseStateMappingCache|. - feature.InitWithFeatures( - {autofill::features::kAutofillUseAlternativeStateNameMap}, - {autofill::features::kAutofillEnableSupportForMoreStructureInAddresses}); + feature.InitAndEnableFeature( + autofill::features::kAutofillUseAlternativeStateNameMap); autofill::test::ClearAlternativeStateNameMapForTesting(); autofill::test::PopulateAlternativeStateNameMapForTesting(); diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc index 7474affa4a6..fbfb1f4f776 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc @@ -615,7 +615,6 @@ TEST_P(AutofillProfileTest, CreateInferredLabelsNoDuplicatedFields) { // should not fall back to the full name as a distinguishing field. std::vector<ServerFieldType> suggested_fields; suggested_fields.push_back(ADDRESS_HOME_LINE1); - suggested_fields.push_back(ADDRESS_BILLING_LINE1); suggested_fields.push_back(EMAIL_ADDRESS); std::vector<std::u16string> labels; AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), 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 6a289ce4625..b094a2234c7 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 @@ -6,21 +6,18 @@ #include <utility> #include "base/containers/contains.h" -#include "base/i18n/case_conversion.h" -#include "base/strings/strcat.h" +#include "base/feature_list.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/browser/address_rewriter.h" #include "components/autofill/core/browser/autofill_type.h" -#include "components/autofill/core/browser/data_model/autofill_structured_address_constants.h" #include "components/autofill/core/browser/data_model/autofill_structured_address_regex_provider.h" #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/geo/alternative_state_name_map.h" +#include "components/autofill/core/common/autofill_features.h" -namespace autofill { - -namespace structured_address { +namespace autofill::structured_address { std::u16string AddressComponentWithRewriter::RewriteValue( const std::u16string& value, @@ -316,10 +313,39 @@ State::State(AddressComponent* parent) : AddressComponentWithRewriter( ADDRESS_HOME_STATE, parent, - MergeMode::kPickShorterIfOneContainsTheOther | kReplaceEmpty) {} + kPickShorterIfOneContainsTheOther | + (base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap) + ? MergeMode::kMergeBasedOnCanonicalizedValues + : 0) | + kReplaceEmpty) {} State::~State() = default; +absl::optional<std::u16string> State::GetCanonicalizedValue() const { + if (!base::FeatureList::IsEnabled( + features::kAutofillUseAlternativeStateNameMap)) { + return absl::nullopt; + } + + std::string country_code = + base::UTF16ToUTF8(GetRootNode().GetValueForType(ADDRESS_HOME_COUNTRY)); + + if (country_code.empty()) { + return absl::nullopt; + } + + absl::optional<AlternativeStateNameMap::CanonicalStateName> + canonicalized_state_name = AlternativeStateNameMap::GetCanonicalStateName( + country_code, GetValue()); + + if (!canonicalized_state_name.has_value()) { + return absl::nullopt; + } + + return canonicalized_state_name.value().value(); +} + // Zips are mergeable when one is a substring of the other one. // For merging, the shorter substring is taken. PostalCode::PostalCode(AddressComponent* parent) @@ -389,6 +415,4 @@ void Address::MigrateLegacyStructure(bool is_verified_profile) { } } -} // namespace structured_address - -} // namespace autofill +} // namespace autofill::structured_address 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 971d418b2de..3765788dc55 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 @@ -188,6 +188,10 @@ class State : public AddressComponentWithRewriter { public: explicit State(AddressComponent* parent); ~State() override; + + // For states we use the AlternativeStateNameMap to offer canonicalized state + // names. + absl::optional<std::u16string> GetCanonicalizedValue() const override; }; // Stores the postal code of an address. 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 54d5276b045..ed64141235e 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 @@ -9,7 +9,6 @@ #include <string> #include <utility> -#include "base/feature_list.h" #include "base/notreached.h" #include "base/strings/strcat.h" #include "base/strings/string_piece.h" @@ -17,14 +16,10 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_type.h" -#include "components/autofill/core/browser/data_model/autofill_structured_address_constants.h" #include "components/autofill/core/browser/data_model/autofill_structured_address_utils.h" #include "components/autofill/core/browser/field_types.h" -#include "components/autofill/core/common/autofill_features.h" -namespace autofill { - -namespace structured_address { +namespace autofill::structured_address { bool IsLessSignificantVerificationStatus(VerificationStatus left, VerificationStatus right) { @@ -217,6 +212,10 @@ const std::u16string& AddressComponent::GetValue() const { return base::EmptyString16(); } +absl::optional<std::u16string> AddressComponent::GetCanonicalizedValue() const { + return absl::nullopt; +} + bool AddressComponent::IsValueAssigned() const { return value_.has_value(); } @@ -808,8 +807,10 @@ const std::vector<AddressToken> AddressComponent::GetSortedTokens() const { bool AddressComponent::IsMergeableWithComponent( const AddressComponent& newer_component) const { - const std::u16string value = ValueForComparison(newer_component); - const std::u16string value_newer = newer_component.ValueForComparison(*this); + const std::u16string older_comparison_value = + ValueForComparison(newer_component); + const std::u16string newer_comparison_value = + newer_component.ValueForComparison(*this); // If both components are the same, there is nothing to do. if (SameAs(newer_component)) @@ -820,18 +821,52 @@ bool AddressComponent::IsMergeableWithComponent( return true; } - if ((merge_mode_ & kReplaceEmpty) && (value.empty() || value_newer.empty())) { + if ((merge_mode_ & kReplaceEmpty) && + (older_comparison_value.empty() || newer_comparison_value.empty())) { return true; } - if (merge_mode_ & kUseBetterOrNewerForSameValue) { - if (base::ToUpperASCII(value) == base::ToUpperASCII(value_newer)) { + SortedTokenComparisonResult token_comparison_result = + CompareSortedTokens(older_comparison_value, newer_comparison_value); + + bool comparison_values_are_substrings_of_each_other = + (older_comparison_value.find(newer_comparison_value) != + std::u16string::npos || + newer_comparison_value.find(older_comparison_value) != + std::u16string::npos); + + if (merge_mode_ & kMergeBasedOnCanonicalizedValues) { + absl::optional<std::u16string> older_canonical_value = + GetCanonicalizedValue(); + absl::optional<std::u16string> newer_canonical_value = + newer_component.GetCanonicalizedValue(); + + bool older_has_canonical_value = older_canonical_value.has_value(); + bool newer_has_canonical_value = newer_canonical_value.has_value(); + + // If both have a canonical value and the value is the same, they are + // obviously mergeable. + if (older_has_canonical_value && newer_has_canonical_value && + older_canonical_value == newer_canonical_value) { + return true; + } + + // If one value does not have canonicalized representation but the actual + // values are substrings of each other, or the tokens contain each other we + // will merge by just using the one with the canonicalized name. + if (older_has_canonical_value != newer_has_canonical_value && + (comparison_values_are_substrings_of_each_other || + token_comparison_result.ContainEachOther())) { return true; } } - SortedTokenComparisonResult token_comparison_result = - CompareSortedTokens(value, value_newer); + if (merge_mode_ & kUseBetterOrNewerForSameValue) { + if (base::ToUpperASCII(older_comparison_value) == + base::ToUpperASCII(newer_comparison_value)) { + return true; + } + } if ((merge_mode_ & (kRecursivelyMergeTokenEquivalentValues | kRecursivelyMergeSingleTokenSubset)) && @@ -859,8 +894,7 @@ bool AddressComponent::IsMergeableWithComponent( // If the one value is a substring of the other, use the substring of the // corresponding mode is active. if ((merge_mode_ & kUseMostRecentSubstring) && - (value.find(value_newer) != std::u16string::npos || - value_newer.find(value) != std::u16string::npos)) { + comparison_values_are_substrings_of_each_other) { return true; } @@ -893,6 +927,16 @@ bool AddressComponent::MergeWithComponent( const std::u16string value = ValueForComparison(newer_component); const std::u16string value_newer = newer_component.ValueForComparison(*this); + + bool newer_component_has_better_or_equal_status = + !IsLessSignificantVerificationStatus( + newer_component.GetVerificationStatus(), GetVerificationStatus()); + bool components_have_the_same_status = + GetVerificationStatus() == newer_component.GetVerificationStatus(); + bool newer_component_has_better_status = + newer_component_has_better_or_equal_status && + !components_have_the_same_status; + if (SameAs(newer_component)) return true; @@ -933,8 +977,7 @@ bool AddressComponent::MergeWithComponent( // Replace the subset with the superset if the corresponding mode is active. if ((merge_mode_ & kReplaceSubset) && token_comparison_result.OneIsSubset()) { if (token_comparison_result.status == SUBSET && - !IsLessSignificantVerificationStatus( - newer_component.GetVerificationStatus(), GetVerificationStatus())) { + newer_component_has_better_or_equal_status) { CopyFrom(newer_component); } return true; @@ -952,8 +995,7 @@ bool AddressComponent::MergeWithComponent( if ((merge_mode_ & (kReplaceSuperset | kReplaceSubset)) && token_comparison_result.status == MATCH) { if (newer_was_more_recently_used && - !IsLessSignificantVerificationStatus( - newer_component.GetVerificationStatus(), GetVerificationStatus())) { + newer_component_has_better_or_equal_status) { CopyFrom(newer_component); } return true; @@ -988,12 +1030,59 @@ bool AddressComponent::MergeWithComponent( (value.find(value_newer) != std::u16string::npos || value_newer.find(value) != std::u16string::npos)) { if (newer_was_more_recently_used && - !IsLessSignificantVerificationStatus( - newer_component.GetVerificationStatus(), GetVerificationStatus())) + newer_component_has_better_or_equal_status) { CopyFrom(newer_component); + } return true; } + bool comparison_values_are_substrings_of_each_other = + (value.find(value_newer) != std::u16string::npos || + value_newer.find(value) != std::u16string::npos); + + if (merge_mode_ & kMergeBasedOnCanonicalizedValues) { + absl::optional<std::u16string> canonical_value = GetCanonicalizedValue(); + absl::optional<std::u16string> other_canonical_value = + newer_component.GetCanonicalizedValue(); + + bool this_has_canonical_value = canonical_value.has_value(); + bool newer_has_canonical_value = other_canonical_value.has_value(); + + // When both have the same canonical value they are obviously mergeable. + if (canonical_value.has_value() && other_canonical_value.has_value() && + *canonical_value == *other_canonical_value) { + // If the newer component has a better verification status use the newer + // one. + if (newer_component_has_better_status) { + CopyFrom(newer_component); + } + // If they have the same status use the shorter one. + if (components_have_the_same_status && + newer_component.GetValue().size() <= GetValue().size()) { + CopyFrom(newer_component); + } + return true; + } + + // If only one component has a canonicalized name but the actual values + // contain each other either tokens-wise or as substrings, use the component + // that has a canonicalized name unless the other component has a better + // verification status. + if (this_has_canonical_value != newer_has_canonical_value && + (comparison_values_are_substrings_of_each_other || + token_comparison_result.ContainEachOther())) { + // Copy the new component if it has a canoniscalized name and a status + // that is not worse of it if has a better stastus even if it is not + // canoniscalized. + if ((!this_has_canonical_value && + newer_component_has_better_or_equal_status) || + (this_has_canonical_value && newer_component_has_better_status)) { + CopyFrom(newer_component); + } + return true; + } + } + if ((merge_mode_ & kPickShorterIfOneContainsTheOther) && token_comparison_result.ContainEachOther()) { if (newer_component.GetValue().size() <= GetValue().size() && @@ -1307,6 +1396,4 @@ std::u16string AddressComponent::ValueForComparison( return NormalizedValue(); } -} // namespace structured_address - -} // namespace autofill +} // namespace autofill::structured_address 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 5da2898e1c6..15e4ddf3b33 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 @@ -84,15 +84,17 @@ enum MergeMode { kRecursivelyMergeSingleTokenSubset = 1 << 6, // If one is a substring of the other use the most recent one. kUseMostRecentSubstring = 1 << 7, - // Merge the child nodes and reformat the node from its children after merge - // if the value has changed. + // If the tokens match or one is a subset of the other, pick the shorter one. kPickShorterIfOneContainsTheOther = 1 << 8, // If the normalized values are different, use the better one in terms // of verification score or the most recent one if both scores are the same. kUseBetterOrMostRecentIfDifferent = 1 << 9, - // Defines the default merging behavior. + // Merge the child nodes and reformat the node from its children after merge + // if the value has changed. kMergeChildrenAndReformatIfNeeded = 1 << 10, - // If the tokens match or one is a subset of the other, pick the shorter one. + // Make a merge decision based on canonicalized values. + kMergeBasedOnCanonicalizedValues = 1 << 11, + // Defines the default merging behavior. kDefault = kRecursivelyMergeTokenEquivalentValues }; @@ -182,6 +184,10 @@ class AddressComponent { // assigned, an empty string is returned. const std::u16string& GetValue() const; + // Returns a canonicalized version of the value or absl::nullopt if + // canonicalization is not possible or not implemented. + virtual absl::optional<std::u16string> GetCanonicalizedValue() const; + // Returns true if the value of this AddressComponent is assigned. bool IsValueAssigned() const; 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 ec0fbf78877..b37dc253067 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 @@ -263,7 +263,7 @@ class TestNonProperFirstNameAddressComponent : public AddressComponent { TestAtomicFirstNameAddressComponent second_name_first_node_{this}; }; -// Tests the merging of two atomic component with |type|, and vales +// Tests the merging of two atomic component with |type|, and values // |older_values| and |newer_values| respectively, and |merge_modes|. // If |is_mergeable| it is expected that the two components are mergeable. // If |newer_was_more_recently_used| the newer component was also more recently 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 9a5b12693a7..a2bf928478a 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,8 +12,11 @@ #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/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_features.h" #include "testing/gtest/include/gtest/gtest.h" @@ -639,6 +642,133 @@ TEST(AutofillStructuredAddress, TestGetCommonCountryForMerge) { EXPECT_EQ(country2.GetCommonCountryForMerge(country1), u""); } +struct MergeStatesWithCanonicalNamesTestCase { + std::string older_state; + VerificationStatus older_status; + std::string newer_state; + VerificationStatus newer_status; + std::string expectation; + bool is_mergeable; +}; + +class MergeStatesWithCanonicalNamesTest + : public testing::Test, + public testing::WithParamInterface< + MergeStatesWithCanonicalNamesTestCase> { + private: + void SetUp() override { + feature_list_.InitAndEnableFeature( + autofill::features::kAutofillUseAlternativeStateNameMap); + + AlternativeStateNameMap::GetInstance() + ->ClearAlternativeStateNameMapForTesting(); + + autofill::test::PopulateAlternativeStateNameMapForTesting( + "XX", "CS", + {{.canonical_name = "CanonicalState", + .abbreviations = {"AS"}, + .alternative_names = {"CoolState"}}}); + autofill::test::PopulateAlternativeStateNameMapForTesting( + "XX", "OS", + {{.canonical_name = "OtherState", + .abbreviations = {"OS"}, + .alternative_names = {""}}}); + } + + base::test::ScopedFeatureList feature_list_; +}; + +// Test that the correct country for merging structured addresses is computed. +TEST_P(MergeStatesWithCanonicalNamesTest, MergeTest) { + MergeStatesWithCanonicalNamesTestCase test_case = GetParam(); + + AddressComponentTestValues older_values = { + {.type = ADDRESS_HOME_COUNTRY, + .value = "XX", + .status = VerificationStatus::kUserVerified}, + {.type = ADDRESS_HOME_STATE, + .value = test_case.older_state, + .status = test_case.older_status}, + }; + + AddressComponentTestValues newer_values = { + {.type = ADDRESS_HOME_COUNTRY, + .value = "XX", + .status = VerificationStatus::kUserVerified}, + {.type = ADDRESS_HOME_STATE, + .value = test_case.newer_state, + .status = test_case.newer_status}, + }; + + // In the expectations it is already assumed that the higher + // verification status should always win. + AddressComponentTestValues expectation_values = { + {.type = ADDRESS_HOME_COUNTRY, + .value = "XX", + .status = VerificationStatus::kUserVerified}, + {.type = ADDRESS_HOME_STATE, + .value = test_case.expectation, + .status = IsLessSignificantVerificationStatus(test_case.older_status, + test_case.newer_status) + ? test_case.newer_status + : test_case.older_status}, + }; + + Address older_address; + SetTestValues(&older_address, older_values); + + Address newer_address; + SetTestValues(&newer_address, newer_values); + + EXPECT_EQ(test_case.is_mergeable, + older_address.IsMergeableWithComponent(newer_address)); + + Address expectation_address; + SetTestValues(&expectation_address, expectation_values); + + older_address.MergeWithComponent(newer_address); + EXPECT_TRUE(older_address.SameAs(expectation_address)); +}; + +INSTANTIATE_TEST_SUITE_P( + AutofillStructuredAddress, + MergeStatesWithCanonicalNamesTest, + ::testing::Values( + + // Both have the same canonical name but the older one has the better + // status and should win in the merge. + MergeStatesWithCanonicalNamesTestCase{ + "CanonicalState", VerificationStatus::kUserVerified, "CoolState", + VerificationStatus::kParsed, "CanonicalState", true}, + + // Both have the same canonical name but the newer one has the better + // status and should win in the merge. + MergeStatesWithCanonicalNamesTestCase{ + "CanonicalState", VerificationStatus::kObserved, "CoolState", + VerificationStatus::kUserVerified, "CoolState", true}, + + // The newer one has no canonical name but the value is a substring of + // the older one. The older has a higher status and should win. + MergeStatesWithCanonicalNamesTestCase{ + "CanonicalState", VerificationStatus::kUserVerified, "state", + VerificationStatus::kParsed, "CanonicalState", true}, + + // The other way round: Now the old one remains because it is a + // substring and has the better status. + MergeStatesWithCanonicalNamesTestCase{ + "state", VerificationStatus::kUserVerified, "CanonicalState", + VerificationStatus::kParsed, "state", true}, + + // Those two are not mergeable but both have a canonical name. + MergeStatesWithCanonicalNamesTestCase{ + "CanonicalState", VerificationStatus::kUserVerified, "OtherState", + VerificationStatus::kParsed, "CanonicalState", false}, + + // Here the newer one does not have a canonical test. + MergeStatesWithCanonicalNamesTestCase{ + "CanonicalState", VerificationStatus::kUserVerified, "Random", + VerificationStatus::kParsed, "CanonicalState", false})); + } // namespace } // namespace structured_address } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/birthdate.cc b/chromium/components/autofill/core/browser/data_model/birthdate.cc index ce34cf2415d..09bf0d95d9a 100644 --- a/chromium/components/autofill/core/browser/data_model/birthdate.cc +++ b/chromium/components/autofill/core/browser/data_model/birthdate.cc @@ -22,7 +22,7 @@ std::u16string Birthdate::GetRawInfo(ServerFieldType type) const { switch (type) { case BIRTHDATE_DAY: case BIRTHDATE_MONTH: - case BIRTHDATE_YEAR_4_DIGITS: { + case BIRTHDATE_4_DIGIT_YEAR: { int value = GetRawInfoAsInt(type); return value != 0 ? base::NumberToString16(value) : std::u16string(); } @@ -38,7 +38,7 @@ int Birthdate::GetRawInfoAsInt(ServerFieldType type) const { return day_; case BIRTHDATE_MONTH: return month_; - case BIRTHDATE_YEAR_4_DIGITS: + case BIRTHDATE_4_DIGIT_YEAR: return year_; default: NOTREACHED(); @@ -54,7 +54,7 @@ void Birthdate::SetRawInfoWithVerificationStatus(ServerFieldType type, switch (type) { case BIRTHDATE_DAY: case BIRTHDATE_MONTH: - case BIRTHDATE_YEAR_4_DIGITS: { + case BIRTHDATE_4_DIGIT_YEAR: { // If |value| is not a number, |StringToInt()| sets it to 0, which will // clear the field. int int_value; @@ -83,7 +83,7 @@ void Birthdate::SetRawInfoAsIntWithVerificationStatus( case BIRTHDATE_MONTH: month_ = ValueIfInRangeOrZero(1, 12); break; - case BIRTHDATE_YEAR_4_DIGITS: + case BIRTHDATE_4_DIGIT_YEAR: year_ = ValueIfInRangeOrZero(1900, 9999); break; default: @@ -94,7 +94,7 @@ void Birthdate::SetRawInfoAsIntWithVerificationStatus( void Birthdate::GetSupportedTypes(ServerFieldTypeSet* supported_types) const { supported_types->insert(BIRTHDATE_DAY); supported_types->insert(BIRTHDATE_MONTH); - supported_types->insert(BIRTHDATE_YEAR_4_DIGITS); + supported_types->insert(BIRTHDATE_4_DIGIT_YEAR); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/birthdate.h b/chromium/components/autofill/core/browser/data_model/birthdate.h index ec07d067f55..a18555aea57 100644 --- a/chromium/components/autofill/core/browser/data_model/birthdate.h +++ b/chromium/components/autofill/core/browser/data_model/birthdate.h @@ -25,7 +25,7 @@ class Birthdate : public FormGroup { // Convenience accessor to the day, month and 4 digit year components. static ServerFieldTypeSet GetRawComponents() { - return {BIRTHDATE_DAY, BIRTHDATE_MONTH, BIRTHDATE_YEAR_4_DIGITS}; + return {BIRTHDATE_DAY, BIRTHDATE_MONTH, BIRTHDATE_4_DIGIT_YEAR}; } // FormGroup: diff --git a/chromium/components/autofill/core/browser/data_model/birthdate_unittest.cc b/chromium/components/autofill/core/browser/data_model/birthdate_unittest.cc index 819e63f4591..17554223d83 100644 --- a/chromium/components/autofill/core/browser/data_model/birthdate_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/birthdate_unittest.cc @@ -16,7 +16,7 @@ Birthdate CreateBirthdate(const std::u16string& day, Birthdate birthdate; birthdate.SetRawInfo(BIRTHDATE_DAY, day); birthdate.SetRawInfo(BIRTHDATE_MONTH, month); - birthdate.SetRawInfo(BIRTHDATE_YEAR_4_DIGITS, year); + birthdate.SetRawInfo(BIRTHDATE_4_DIGIT_YEAR, year); return birthdate; } @@ -26,7 +26,7 @@ void VerifyValues(const Birthdate& birthdate, const std::u16string& year) { EXPECT_EQ(birthdate.GetRawInfo(BIRTHDATE_DAY), day); EXPECT_EQ(birthdate.GetRawInfo(BIRTHDATE_MONTH), month); - EXPECT_EQ(birthdate.GetRawInfo(BIRTHDATE_YEAR_4_DIGITS), year); + EXPECT_EQ(birthdate.GetRawInfo(BIRTHDATE_4_DIGIT_YEAR), year); } // Expect that setting |field| to |value| clears the |field|. This is used to @@ -61,8 +61,8 @@ TEST(BirthdateTest, Validation) { SetFieldAndExpectEmpty(BIRTHDATE_DAY, u"NaN"); SetFieldAndExpectEmpty(BIRTHDATE_MONTH, u"13"); SetFieldAndExpectEmpty(BIRTHDATE_MONTH, u"a"); - SetFieldAndExpectEmpty(BIRTHDATE_YEAR_4_DIGITS, u"12345"); - SetFieldAndExpectEmpty(BIRTHDATE_YEAR_4_DIGITS, u"1234"); + SetFieldAndExpectEmpty(BIRTHDATE_4_DIGIT_YEAR, u"12345"); + SetFieldAndExpectEmpty(BIRTHDATE_4_DIGIT_YEAR, u"1234"); } // Tests that empty values clear the corresponding fields. 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 1e734b70666..988c42ce088 100644 --- a/chromium/components/autofill/core/browser/data_model/contact_info.cc +++ b/chromium/components/autofill/core/browser/data_model/contact_info.cc @@ -13,12 +13,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 { @@ -381,16 +381,15 @@ void CompanyInfo::SetRawInfoWithVerificationStatus(ServerFieldType type, } bool CompanyInfo::IsValidOrVerified(const std::u16string& value) const { - // TODO(crbug/1117296): retrieve regular expressions dynamically. - static const char* kBirthyearRe = "^(19|20)\\d{2}$"; - static const char* kSocialTitleRe = - "^(Ms\\.?|Mrs\\.?|Mr\\.?|Miss|Mistress|Mister|" - "Frau|Herr|" - "Mlle|Mme|M\\.|" - "Dr\\.?|Prof\\.?)$"; + static constexpr char16_t kBirthyearRe[] = u"^(19|20)\\d{2}$"; + static constexpr char16_t kSocialTitleRe[] = + u"^(Ms\\.?|Mrs\\.?|Mr\\.?|Miss|Mistress|Mister|" + u"Frau|Herr|" + u"Mlle|Mme|M\\.|" + u"Dr\\.?|Prof\\.?)$"; return (profile_ && profile_->IsVerified()) || - (!MatchesPattern(value, base::UTF8ToUTF16(kBirthyearRe)) && - !MatchesPattern(value, base::UTF8ToUTF16(kSocialTitleRe))); + (!MatchesRegex<kBirthyearRe>(value) && + !MatchesRegex<kSocialTitleRe>(value)); } } // 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 b9e126b5f38..a357783fc89 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card.cc +++ b/chromium/components/autofill/core/browser/data_model/credit_card.cc @@ -24,7 +24,6 @@ #include "build/build_config.h" #include "components/autofill/core/browser/autofill_data_util.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/data_model/autofill_metadata.h" #include "components/autofill/core/browser/data_model/data_model_utils.h" @@ -34,6 +33,7 @@ #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" @@ -403,7 +403,7 @@ double CreditCard::GetRankingScore(base::Time current_time) const { return AutofillDataModel::GetRankingScore(current_time) + virtual_card_boost; } -bool CreditCard::SetMetadata(const AutofillMetadata metadata) { +bool CreditCard::SetMetadata(const AutofillMetadata& metadata) { // Make sure the ids matches. if (metadata.id != (record_type_ == LOCAL_CARD ? guid() : server_id_)) return false; @@ -563,8 +563,9 @@ void CreditCard::GetMatchingTypes(const std::u16string& text, } void CreditCard::SetInfoForMonthInputType(const std::u16string& value) { + static constexpr char16_t kDateRegex[] = u"^[0-9]{4}-[0-9]{1,2}$"; // Check if |text| is "yyyy-mm" format first, and check normal month format. - if (!MatchesPattern(value, u"^[0-9]{4}-[0-9]{1,2}$")) + if (!MatchesRegex<kDateRegex>(value)) return; std::vector<base::StringPiece16> year_month = base::SplitStringPiece( @@ -819,10 +820,12 @@ bool CreditCard::SetExpirationYearFromString(const std::u16string& text) { } void CreditCard::SetExpirationDateFromString(const std::u16string& text) { + static constexpr char16_t kDateRegex[] = + uR"(^\s*[0-9]{1,2}\s*[-/|]?\s*[0-9]{2,4}\s*$)"; // Check that |text| fits the supported patterns: mmyy, mmyyyy, m-yy, // mm-yy, m-yyyy and mm-yyyy. Note that myy and myyyy matched by this pattern // but are not supported (ambiguous). Separators: -, / and |. - if (!MatchesPattern(text, uR"(^\s*[0-9]{1,2}\s*[-/|]?\s*[0-9]{2,4}\s*$)")) + if (!MatchesRegex<kDateRegex>(text)) return; std::u16string month; @@ -863,9 +866,7 @@ void CreditCard::SetExpirationDateFromString(const std::u16string& text) { SetExpirationYear(num); } -const std::pair<std::u16string, std::u16string> CreditCard::LabelPieces() - const { - std::u16string label; +std::pair<std::u16string, std::u16string> CreditCard::LabelPieces() const { if (number().empty()) { // No CC number, if valid nickname is present, return nickname only. // Otherwise, return cardholder name only. @@ -875,22 +876,16 @@ const std::pair<std::u16string, std::u16string> CreditCard::LabelPieces() return std::make_pair(name_on_card_, std::u16string()); } - std::u16string obfuscated_cc_number = - CardIdentifierStringForAutofillDisplay(); - // No expiration date set. - if (!expiration_month_ || !expiration_year_) - return std::make_pair(obfuscated_cc_number, std::u16string()); - - std::u16string formatted_date = ExpirationDateForDisplay(); - - std::u16string separator = - l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR); - return std::make_pair(obfuscated_cc_number, separator + formatted_date); + return std::make_pair(CardIdentifierStringForAutofillDisplay(), + name_on_card_); } -const std::u16string CreditCard::Label() const { +std::u16string CreditCard::Label() const { std::pair<std::u16string, std::u16string> pieces = LabelPieces(); - return pieces.first + pieces.second; + if (pieces.first.empty() || pieces.second.empty()) + return pieces.first + pieces.second; + + return pieces.first + u", " + pieces.second; } std::u16string CreditCard::LastFourDigits() const { @@ -949,6 +944,12 @@ std::u16string CreditCard::CardIdentifierStringForAutofillDisplay( int obfuscation_length) const { if (HasNonEmptyValidNickname() || !customized_nickname.empty()) { return NicknameAndLastFourDigits(customized_nickname, obfuscation_length); + } else if (base::FeatureList::IsEnabled( + features::kAutofillEnableCardProductName) && + !product_description_.empty()) { + // If product description is available, format card label as 'Product + // description ****2345'. + return ProductDescriptionAndLastFourdigits(obfuscation_length); } return NetworkAndLastFourDigits(obfuscation_length); } @@ -1113,6 +1114,18 @@ std::u16string CreditCard::NicknameAndLastFourDigits( internal::GetObfuscatedStringForCardDigits(digits, obfuscation_length); } +std::u16string CreditCard::ProductDescriptionAndLastFourdigits( + int obfuscation_length) const { + DCHECK(!product_description_.empty()); + const std::u16string digits = LastFourDigits(); + // If digits are empty, return product description. + if (digits.empty()) + return product_description_; + + return product_description_ + u" " + + internal::GetObfuscatedStringForCardDigits(digits, obfuscation_length); +} + void CreditCard::SetNumber(const std::u16string& number) { number_ = number; diff --git a/chromium/components/autofill/core/browser/data_model/credit_card.h b/chromium/components/autofill/core/browser/data_model/credit_card.h index 44a74f8ae28..cb8daaf72fb 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card.h +++ b/chromium/components/autofill/core/browser/data_model/credit_card.h @@ -134,7 +134,7 @@ class CreditCard : public AutofillDataModel { // AutofillDataModel: AutofillMetadata GetMetadata() const override; double GetRankingScore(base::Time current_time) const override; - bool SetMetadata(const AutofillMetadata metadata) override; + bool SetMetadata(const AutofillMetadata& metadata) override; // Returns whether the card is deletable: if it is expired and has not been // used for longer than |kDisusedCreditCardDeletionTimeDelta|. bool IsDeletable() const override; @@ -275,11 +275,11 @@ class CreditCard : public AutofillDataModel { // Various display functions. - // Card preview summary, for example: "Nickname/Network - ****1234", - // ", 01/2020". - const std::pair<std::u16string, std::u16string> LabelPieces() const; + // Card preview summary, for example: "Nickname/Network - ****1234 John + // Smith". + std::pair<std::u16string, std::u16string> LabelPieces() const; // Like LabelPieces, but appends the two pieces together. - const std::u16string Label() const; + std::u16string Label() const; // The last four digits of the card number (or possibly less if there aren't // enough characters). std::u16string LastFourDigits() const; @@ -403,6 +403,12 @@ class CreditCard : public AutofillDataModel { std::u16string customized_nickname = std::u16string(), int obfuscation_length = 4) const; + // A label for the card formatted as 'Product description ****LastFour' like + // 'ABC Bank XYZ Card ****1234'. Check that product description exists before + // calling this method. By default, the `obfuscation_length` is set to 4. + std::u16string ProductDescriptionAndLastFourdigits( + int obfuscation_length = 4) const; + // Sets the name_on_card_ value based on the saved name parts. void SetNameOnCardFromSeparateParts(); diff --git a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc index 7b8c201f425..3e704144d4d 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc @@ -103,81 +103,133 @@ TEST(CreditCardTest, GetObfuscatedStringForCardDigits) { // Tests credit card summary string generation. This test simulates a variety // of different possible summary strings. Variations occur based on the // existence of credit card number, month, and year fields. -TEST(CreditCardTest, PreviewSummaryAndNetworkAndLastFourDigitsStrings) { +TEST(CreditCardTest, LabelSummary) { std::u16string valid_nickname = u"My Visa Card"; // Case 0: empty credit card. CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com/"); - std::u16string summary0 = credit_card0.Label(); - EXPECT_EQ(std::u16string(), summary0); - std::u16string obfuscated0 = credit_card0.NetworkAndLastFourDigits(); - EXPECT_EQ(ASCIIToUTF16(std::string("Card")), obfuscated0); + EXPECT_EQ(std::u16string(), credit_card0.Label()); // Case 00: Empty credit card with empty strings. CreditCard credit_card00(base::GenerateGUID(), "https://www.example.com/"); test::SetCreditCardInfo(&credit_card00, "John Dillinger", "", "", "", ""); - std::u16string summary00 = credit_card00.Label(); - EXPECT_EQ(std::u16string(u"John Dillinger"), summary00); - std::u16string obfuscated00 = credit_card00.NetworkAndLastFourDigits(); - EXPECT_EQ(ASCIIToUTF16(std::string("Card")), obfuscated00); + EXPECT_EQ(std::u16string(u"John Dillinger"), credit_card00.Label()); // Case 1: No credit card number. CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com/"); test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2010", "1"); - std::u16string summary1 = credit_card1.Label(); - EXPECT_EQ(std::u16string(u"John Dillinger"), summary1); - std::u16string obfuscated1 = credit_card1.NetworkAndLastFourDigits(); - EXPECT_EQ(ASCIIToUTF16(std::string("Card")), obfuscated1); + EXPECT_EQ(std::u16string(u"John Dillinger"), credit_card1.Label()); // Case 1.1: No credit card number, but has nickname. CreditCard credit_card11(base::GenerateGUID(), "https://www.example.com/"); test::SetCreditCardInfo(&credit_card11, "John Dillinger", "", "01", "2010", "1"); credit_card11.SetNickname(valid_nickname); - std::u16string summary11 = credit_card11.Label(); - EXPECT_EQ(valid_nickname, summary11); - std::u16string obfuscated11 = credit_card11.NetworkAndLastFourDigits(); - EXPECT_EQ(ASCIIToUTF16(std::string("Card")), obfuscated11); + EXPECT_EQ(valid_nickname, credit_card11.Label()); // Case 2: No month. CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com/"); test::SetCreditCardInfo(&credit_card2, "John Dillinger", "5105 1051 0510 5100", "", "2010", "1"); - std::u16string summary2 = credit_card2.Label(); EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + - test::ObfuscatedCardDigitsAsUTF8("5100")), - summary2); - std::u16string obfuscated2 = credit_card2.NetworkAndLastFourDigits(); - EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + - test::ObfuscatedCardDigitsAsUTF8("5100")), - obfuscated2); + test::ObfuscatedCardDigitsAsUTF8("5100") + + ", John Dillinger"), + credit_card2.Label()); // Case 3: No year. CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com/"); test::SetCreditCardInfo(&credit_card3, "John Dillinger", "5105 1051 0510 5100", "01", "", "1"); - std::u16string summary3 = credit_card3.Label(); + EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + + test::ObfuscatedCardDigitsAsUTF8("5100") + + ", John Dillinger"), + credit_card3.Label()); + + // Case 4: Have everything except nickname. + CreditCard credit_card4(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card4, "John Dillinger", + "5105 1051 0510 5100", "01", "2010", "1"); + EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + + test::ObfuscatedCardDigitsAsUTF8("5100") + + ", John Dillinger"), + credit_card4.Label()); + + // Case 5: Very long credit card + CreditCard credit_card5(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo( + &credit_card5, "John Dillinger", + "0123456789 0123456789 0123456789 5105 1051 0510 5100", "01", "2010", + "1"); + EXPECT_EQ(UTF8ToUTF16(std::string("Card ") + + test::ObfuscatedCardDigitsAsUTF8("5100") + + ", John Dillinger"), + credit_card5.Label()); + + // Case 6: Have everything including nickname. + CreditCard credit_card6(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card6, "John Dillinger", + "5105 1051 0510 5100", "01", "2010", "1"); + credit_card6.SetNickname(valid_nickname); + EXPECT_EQ( + valid_nickname + UTF8ToUTF16(std::string(" ") + + test::ObfuscatedCardDigitsAsUTF8("5100") + + ", John Dillinger"), + credit_card6.Label()); +} + +TEST(CreditCardTest, NetworkAndLastFourDigits) { + std::u16string valid_nickname = u"My Visa Card"; + + // Case 0: empty credit card. + CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com/"); + EXPECT_EQ(ASCIIToUTF16(std::string("Card")), + credit_card0.NetworkAndLastFourDigits()); + + // Case 00: Empty credit card with empty strings. + CreditCard credit_card00(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card00, "John Dillinger", "", "", "", ""); + EXPECT_EQ(ASCIIToUTF16(std::string("Card")), + credit_card00.NetworkAndLastFourDigits()); + + // Case 1: No credit card number. + CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2010", + "1"); + EXPECT_EQ(ASCIIToUTF16(std::string("Card")), + credit_card1.NetworkAndLastFourDigits()); + + // Case 1.1: No credit card number, but has nickname. + CreditCard credit_card11(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card11, "John Dillinger", "", "01", "2010", + "1"); + credit_card11.SetNickname(valid_nickname); + EXPECT_EQ(ASCIIToUTF16(std::string("Card")), + credit_card11.NetworkAndLastFourDigits()); + + // Case 2: No month. + CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card2, "John Dillinger", + "5105 1051 0510 5100", "", "2010", "1"); EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + test::ObfuscatedCardDigitsAsUTF8("5100")), - summary3); - std::u16string obfuscated3 = credit_card3.NetworkAndLastFourDigits(); + credit_card2.NetworkAndLastFourDigits()); + + // Case 3: No year. + CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card3, "John Dillinger", + "5105 1051 0510 5100", "01", "", "1"); EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + test::ObfuscatedCardDigitsAsUTF8("5100")), - obfuscated3); + credit_card3.NetworkAndLastFourDigits()); // Case 4: Have everything except nickname. CreditCard credit_card4(base::GenerateGUID(), "https://www.example.com/"); test::SetCreditCardInfo(&credit_card4, "John Dillinger", "5105 1051 0510 5100", "01", "2010", "1"); - std::u16string summary4 = credit_card4.Label(); - EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + - test::ObfuscatedCardDigitsAsUTF8("5100") + ", 01/2010"), - summary4); - std::u16string obfuscated4 = credit_card4.NetworkAndLastFourDigits(); EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + test::ObfuscatedCardDigitsAsUTF8("5100")), - obfuscated4); + credit_card4.NetworkAndLastFourDigits()); // Case 5: Very long credit card CreditCard credit_card5(base::GenerateGUID(), "https://www.example.com/"); @@ -185,26 +237,18 @@ TEST(CreditCardTest, PreviewSummaryAndNetworkAndLastFourDigitsStrings) { &credit_card5, "John Dillinger", "0123456789 0123456789 0123456789 5105 1051 0510 5100", "01", "2010", "1"); - std::u16string summary5 = credit_card5.Label(); - EXPECT_EQ(UTF8ToUTF16(std::string("Card ") + - test::ObfuscatedCardDigitsAsUTF8("5100") + ", 01/2010"), - summary5); - std::u16string obfuscated5 = credit_card5.NetworkAndLastFourDigits(); EXPECT_EQ(UTF8ToUTF16(std::string("Card ") + test::ObfuscatedCardDigitsAsUTF8("5100")), - obfuscated5); + credit_card5.NetworkAndLastFourDigits()); // Case 6: Have everything including nickname. CreditCard credit_card6(base::GenerateGUID(), "https://www.example.com/"); test::SetCreditCardInfo(&credit_card6, "John Dillinger", "5105 1051 0510 5100", "01", "2010", "1"); credit_card6.SetNickname(valid_nickname); - std::u16string summary6 = credit_card6.Label(); - EXPECT_EQ( - valid_nickname + - UTF8ToUTF16(std::string(" ") + - test::ObfuscatedCardDigitsAsUTF8("5100") + ", 01/2010"), - summary6); + EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + + test::ObfuscatedCardDigitsAsUTF8("5100")), + credit_card6.NetworkAndLastFourDigits()); } TEST(CreditCardTest, NicknameAndLastFourDigitsStrings) { @@ -228,33 +272,153 @@ TEST(CreditCardTest, NicknameAndLastFourDigitsStrings) { credit_card2.NicknameAndLastFourDigitsForTesting()); } -TEST(CreditCardTest, CardIdentifierStringsForAutofillDisplay) { +// Test that card identifier string falls back to issuer network when both +// nickname and product description are unavailable. +TEST(CreditCardTest, + CardIdentifierStringsForAutofillDisplay_NoNicknameNoProductDescription) { base::test::ScopedFeatureList scoped_feature_list; - std::u16string valid_nickname = u"My Visa Card"; - std::u16string invalid_nickname = u"Nickname length exceeds 25 characters"; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillEnableCardProductName); - // Case 1: Nickname name is invalid -> show network name. - CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com/"); - test::SetCreditCardInfo(&credit_card1, "John Dillinger", + CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card, "John Dillinger", "5105 1051 0510 5100" /* Mastercard */, "01", "2020", "1"); - credit_card1.SetNickname(invalid_nickname); - EXPECT_FALSE(credit_card1.HasNonEmptyValidNickname()); + EXPECT_FALSE(credit_card.HasNonEmptyValidNickname()); EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + test::ObfuscatedCardDigitsAsUTF8("5100")), - credit_card1.CardIdentifierStringForAutofillDisplay()); + credit_card.CardIdentifierStringForAutofillDisplay()); +} - // Case 2: Experiment is on and nickname is valid -> show nickname. - CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com/"); - test::SetCreditCardInfo(&credit_card2, "John Dillinger", +// Test that card identifier string falls back to issuer network when nickname +// is invalid and product description is unavailable. +TEST( + CreditCardTest, + CardIdentifierStringsForAutofillDisplay_InvalidNicknameNoProductDescription) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillEnableCardProductName); + + CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card, "John Dillinger", "5105 1051 0510 5100" /* Mastercard */, "01", "2020", "1"); - credit_card2.SetNickname(valid_nickname); - EXPECT_TRUE(credit_card2.HasNonEmptyValidNickname()); + credit_card.SetNickname(u"Nickname length exceeds 25 characters"); + EXPECT_FALSE(credit_card.HasNonEmptyValidNickname()); + EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") + + test::ObfuscatedCardDigitsAsUTF8("5100")), + credit_card.CardIdentifierStringForAutofillDisplay()); +} + +// Test that card identifier string falls back to product description when +// nickname is unavailable. +TEST(CreditCardTest, + CardIdentifierStringsForAutofillDisplay_NoNicknameWithProductDescription) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillEnableCardProductName); + + std::u16string product_description = u"ABC bank XYZ card"; + + CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card, "John Dillinger", + "5105 1051 0510 5100" /* Mastercard */, "01", "2020", + "1"); + credit_card.set_product_description(product_description); + EXPECT_FALSE(credit_card.HasNonEmptyValidNickname()); + EXPECT_EQ(product_description + + UTF8ToUTF16(std::string(" ") + + test::ObfuscatedCardDigitsAsUTF8("5100")), + credit_card.CardIdentifierStringForAutofillDisplay()); +} + +// Test that card identifier string falls back to product description when +// nickname is invalid. +TEST( + CreditCardTest, + CardIdentifierStringsForAutofillDisplay_InvalidNicknameWithProductDescription) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillEnableCardProductName); + + std::u16string product_description = u"ABC bank XYZ card"; + + CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card, "John Dillinger", + "5105 1051 0510 5100" /* Mastercard */, "01", "2020", + "1"); + credit_card.SetNickname(u"Nickname length exceeds 25 characters"); + credit_card.set_product_description(product_description); + EXPECT_FALSE(credit_card.HasNonEmptyValidNickname()); + EXPECT_EQ(product_description + + UTF8ToUTF16(std::string(" ") + + test::ObfuscatedCardDigitsAsUTF8("5100")), + credit_card.CardIdentifierStringForAutofillDisplay()); +} + +// Test that card identifier string shows nickname when it is valid. +TEST(CreditCardTest, + CardIdentifierStringsForAutofillDisplay_WithValidNickname) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillEnableCardProductName); + + std::u16string valid_nickname = u"My Visa Card"; + + CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card, "John Dillinger", + "5105 1051 0510 5100" /* Mastercard */, "01", "2020", + "1"); + credit_card.SetNickname(valid_nickname); + credit_card.set_product_description(u"ABC bank XYZ card"); + EXPECT_TRUE(credit_card.HasNonEmptyValidNickname()); EXPECT_EQ( valid_nickname + UTF8ToUTF16(std::string(" ") + test::ObfuscatedCardDigitsAsUTF8("5100")), - credit_card2.CardIdentifierStringForAutofillDisplay()); + credit_card.CardIdentifierStringForAutofillDisplay()); +} + +// Test that customized nickname takes precedence over credit card's nickname. +TEST(CreditCardTest, + CardIdentifierStringsForAutofillDisplay_WithCustomizedNickname) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillEnableCardProductName); + + std::u16string customized_nickname = u"My grocery shopping Visa card"; + + CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card, "John Dillinger", + "5105 1051 0510 5100" /* Mastercard */, "01", "2020", + "1"); + credit_card.SetNickname(u"My Visa Card"); + credit_card.set_product_description(u"ABC bank XYZ card"); + EXPECT_TRUE(credit_card.HasNonEmptyValidNickname()); + EXPECT_EQ( + customized_nickname + + UTF8ToUTF16(std::string(" ") + + test::ObfuscatedCardDigitsAsUTF8("5100")), + credit_card.CardIdentifierStringForAutofillDisplay(customized_nickname)); +} + +// Test that the card number is formatted as per the obfuscation length. +TEST(CreditCardTest, + CardIdentifierStringsForAutofillDisplay_WithObfuscationLength) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillEnableCardProductName); + + int obfuscation_length = 2; + + CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/"); + test::SetCreditCardInfo(&credit_card, "John Dillinger", + "5105 1051 0510 5100" /* Mastercard */, "01", "2020", + "1"); + EXPECT_EQ( + UTF8ToUTF16(std::string("Mastercard ") + + test::ObfuscatedCardDigitsAsUTF8("5100", obfuscation_length)), + credit_card.CardIdentifierStringForAutofillDisplay(u"", + obfuscation_length)); } TEST(CreditCardTest, AssignmentOperator) { 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 12d2e4069e8..2eb0ae1aedd 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 @@ -9,9 +9,9 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.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" @@ -145,7 +145,7 @@ std::u16string FindPossiblePhoneCountryCode(const std::u16string& text) { if (text.find(u"00") != std::u16string::npos || text.find('+') != std::u16string::npos) { std::vector<std::u16string> captures; - if (MatchesPattern(text, kAugmentedPhoneCountryCodeRe, &captures)) + if (MatchesRegex<kAugmentedPhoneCountryCodeRe>(text, &captures)) return captures[1]; } diff --git a/chromium/components/autofill/core/browser/data_model/iban.cc b/chromium/components/autofill/core/browser/data_model/iban.cc new file mode 100644 index 00000000000..9efe62d7ac3 --- /dev/null +++ b/chromium/components/autofill/core/browser/data_model/iban.cc @@ -0,0 +1,160 @@ +// Copyright 2022 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/data_model/iban.h" + +#include <string> + +#include "base/guid.h" +#include "base/notreached.h" +#include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/browser/data_model/autofill_metadata.h" + +namespace autofill { + +// Unicode characters used in IBAN value obfuscation: +// - \u2022 - Bullet. +// - \u2006 - SIX-PER-EM SPACE (small space between bullets). +constexpr char16_t kMidlineEllipsisFourDotsAndOneSpace[] = + u"\u2022\u2022\u2022\u2022\u2006"; +constexpr char16_t kMidlineEllipsisTwoDotsAndOneSpace[] = u"\u2022\u2022\u2006"; + +IBAN::IBAN(const std::string& guid) + : AutofillDataModel(guid, /*origin=*/std::string()), + record_type_(LOCAL_IBAN) {} + +IBAN::IBAN() : IBAN(base::GenerateGUID()) {} + +IBAN::IBAN(const IBAN& iban) : IBAN() { + operator=(iban); +} + +IBAN::~IBAN() = default; + +IBAN& IBAN::operator=(const IBAN& iban) = default; + +AutofillMetadata IBAN::GetMetadata() const { + AutofillMetadata metadata = AutofillDataModel::GetMetadata(); + metadata.id = (record_type_ == LOCAL_IBAN ? guid() : server_id_); + return metadata; +} + +bool IBAN::SetMetadata(const AutofillMetadata& metadata) { + // Make sure the ids match. + return ((metadata.id != + (record_type_ == LOCAL_IBAN ? guid() : server_id_))) && + AutofillDataModel::SetMetadata(metadata); +} + +bool IBAN::IsDeletable() const { + return false; +} + +std::u16string IBAN::GetRawInfo(ServerFieldType type) const { + if (type == IBAN_VALUE) { + return value_; + } + + NOTREACHED(); + return std::u16string(); +} + +void IBAN::SetRawInfoWithVerificationStatus( + ServerFieldType type, + const std::u16string& value, + structured_address::VerificationStatus status) { + if (type == IBAN_VALUE) { + set_value(value); + } else { + NOTREACHED() << "Attempting to set unknown info-type" << type; + } +} + +void IBAN::GetSupportedTypes(ServerFieldTypeSet* supported_types) const { + supported_types->insert(IBAN_VALUE); +} + +bool IBAN::IsEmpty(const std::string& app_locale) const { + ServerFieldTypeSet types; + GetNonEmptyTypes(app_locale, &types); + return types.empty(); +} + +int IBAN::Compare(const IBAN& iban) const { + int comparison = server_id_.compare(iban.server_id_); + if (comparison != 0) { + return comparison; + } + + comparison = nickname_.compare(iban.nickname_); + if (comparison != 0) { + return comparison; + } + + return value_.compare(iban.value_); +} + +bool IBAN::operator==(const IBAN& iban) const { + return guid() == iban.guid() && record_type() == iban.record_type() && + Compare(iban) == 0; +} + +bool IBAN::operator!=(const IBAN& iban) const { + return !operator==(iban); +} + +void IBAN::set_nickname(const std::u16string& nickname) { + // First replace all tabs and newlines with whitespaces and store it as + // |nickname_|. + base::ReplaceChars(nickname, u"\t\r\n", u" ", &nickname_); + // An additional step to collapse whitespaces, this step does: + // 1. Trim leading and trailing whitespaces. + // 2. All other whitespace sequences are converted to a single space. + nickname_ = + base::CollapseWhitespace(nickname_, + /*trim_sequences_with_line_breaks=*/true); +} + +std::u16string IBAN::GetIdentifierStringForAutofillDisplay() const { + const std::u16string stripped_value = GetStrippedValue(); + size_t value_length = stripped_value.size(); + // Directly return an empty string if the length of IBAN value is invalid. + if (value_length < 5 || value_length > 34) + return std::u16string(); + + std::u16string value_to_display = stripped_value.substr(0, 2); + + // Get the number of groups of four characters to be obfuscated. + size_t number_of_groups = value_length % 4 == 0 ? (value_length - 4) / 4 - 1 + : (value_length - 4) / 4; + // Get the position of rest of characters to be revealed. + size_t first_revealed_digit_pos = value_length % 4 == 0 + ? value_length - 4 + : value_length - (value_length % 4); + + value_to_display.append(RepeatEllipsis(number_of_groups)); + + value_to_display.append(stripped_value.substr(first_revealed_digit_pos)); + return value_to_display; +} + +std::u16string IBAN::GetStrippedValue() const { + std::u16string stripped_value; + base::RemoveChars(value_, u"- ", &stripped_value); + return stripped_value; +} + +std::u16string IBAN::RepeatEllipsis(size_t number_of_groups) const { + std::u16string ellipsis_value; + ellipsis_value.reserve(sizeof(kMidlineEllipsisTwoDotsAndOneSpace) + + number_of_groups * + sizeof(kMidlineEllipsisFourDotsAndOneSpace)); + ellipsis_value.append(kMidlineEllipsisTwoDotsAndOneSpace); + for (size_t i = 0; i < number_of_groups; ++i) + ellipsis_value.append(kMidlineEllipsisFourDotsAndOneSpace); + + return ellipsis_value; +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/iban.h b/chromium/components/autofill/core/browser/data_model/iban.h new file mode 100644 index 00000000000..d7266c719c5 --- /dev/null +++ b/chromium/components/autofill/core/browser/data_model/iban.h @@ -0,0 +1,137 @@ +// Copyright 2022 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_DATA_MODEL_IBAN_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_IBAN_H_ + +#include <string> + +#include "base/time/time.h" +#include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/browser/data_model/autofill_data_model.h" + +namespace autofill { + +// A form group that stores IBAN information. +class IBAN : public AutofillDataModel { + public: + enum RecordType { + // An IBAN stored and editable locally. + // Note: We only have local IBAN for now. + LOCAL_IBAN, + // An IBAN synced down from the server. These are read-only locally. + // Note: Server IBAN is not supported for now. + SERVER_IBAN, + }; + + explicit IBAN(const std::string& guid); + + IBAN(); + IBAN(const IBAN&); + ~IBAN() override; + + IBAN& operator=(const IBAN& iban); + + // AutofillDataModel: + AutofillMetadata GetMetadata() const override; + bool SetMetadata(const AutofillMetadata& metadata) override; + + // Whether the IBAN is deletable. Always returns false for now as IBAN + // never expires. + bool IsDeletable() const override; + + std::u16string GetRawInfo(ServerFieldType type) const override; + void SetRawInfoWithVerificationStatus( + ServerFieldType type, + const std::u16string& value, + structured_address::VerificationStatus status) override; + void GetSupportedTypes(ServerFieldTypeSet* supported_types) const override; + + // How this IBAN is stored. + RecordType record_type() const { return record_type_; } + void set_record_type(RecordType type) { record_type_ = type; } + + // Returns true if there are no values (field types) set. + bool IsEmpty(const std::string& app_locale) const; + + // Comparison for Sync. Returns 0 if |iban| is the same as this, or < 0, + // or > 0 if it is different. The implied ordering can be used for culling + // duplicates. The ordering is based on the collation order of the textual + // contents of the fields. + // GUIDs, origins, and server id are not compared, only the values of + // the IBANs themselves. + int Compare(const IBAN& iban) const; + + // Equality operators compare GUIDs, origins, |record_type_|, |value_|, + // |nickname_| and the |server_id_|. + bool operator==(const IBAN& iban) const; + bool operator!=(const IBAN& iban) const; + + // Returns the ID assigned by the server. |server_id_| is empty if it's a + // local IBAN. + const std::string& server_id() const { return server_id_; } + + // Returns the value (the actual bank account number) of IBAN. + const std::u16string& value() const { return value_; } + void set_value(const std::u16string& value) { value_ = value; } + + const std::u16string& nickname() const { return nickname_; } + // Set the |nickname_| with the processed input (replace all tabs and newlines + // with whitespaces, condense multiple whitespaces into a single one, and + // trim leading/trailing whitespaces). + void set_nickname(const std::u16string& nickname); + + // Converts value (E.g., CH12 1234 1234 1234 1234) of IBAN to a partial masked + // text formatted by the following steps: + // 1. Reveal the first two characters, containing the country code. + // 2. Obfuscate the following two check digits. + // 3. Arrange the remaining digits in groups of four and obfuscate them, + // adding a space between each group. + // Note: If the number of remaining digits is a multiple of four, reveal + // the last four digits. + // 4. Reveal any leftover digits not in a group of four. + // + // Here are some examples: + // BE71 0961 2345 6769 will be shown as: BE** **** **** 6769. + // CH56 0483 5012 3456 7800 9 will be shown as: CH** **** **** **** **** 9. + // DE91 1000 0000 0123 4567 89 will be show as: DE** **** **** **** **** 89. + std::u16string GetIdentifierStringForAutofillDisplay() const; + +#if defined(UNIT_TEST) + // Call RepeatEllipsis for testing purposes. + std::u16string RepeatEllipsisForTesting(size_t number_of_groups) const { + return RepeatEllipsis(number_of_groups); + } +#endif + + private: + // Returns a version of |value_| which does not have any separator characters + // (e.g., '-' and ' '). + std::u16string GetStrippedValue() const; + + // This method does the following steps: + // 1. Adds two bullets and a space which represent the two characters after + // the country code. + // 2. Adds |number_of_groups| groups of "**** " to obfuscate the value of + // IBAN. + std::u16string RepeatEllipsis(size_t number_of_groups) const; + + // This is the ID assigned by the server to uniquely identify this IBAN. + // Note: server_id is empty for now as only local IBAN is supported. + std::string server_id_; + + // Type of how IBAN is stored, either local or server. + // Note: IBAN will only be stored locally for now. + RecordType record_type_; + + // The IBAN's value, i.e., the actual bank account number. + std::u16string value_; + + // The nickname of the IBAN. May be empty. + std::u16string nickname_; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_MODEL_IBAN_H_ diff --git a/chromium/components/autofill/core/browser/data_model/iban_unittest.cc b/chromium/components/autofill/core/browser/data_model/iban_unittest.cc new file mode 100644 index 00000000000..b6580a5b989 --- /dev/null +++ b/chromium/components/autofill/core/browser/data_model/iban_unittest.cc @@ -0,0 +1,149 @@ +// Copyright 2022 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/data_model/iban.h" + +#include <string> + +#include "base/guid.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/data_model/autofill_metadata.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +TEST(IBANTest, AssignmentOperator) { + // Creates two IBANs with different parameters. + std::string guid = base::GenerateGUID(); + IBAN iban_0; + iban_0.set_guid(guid); + iban_0.set_nickname(u"Nickname 0"); + iban_0.set_value(u"DE91 1000 0000 0123 4567 89"); + IBAN iban_1; + guid = base::GenerateGUID(); + iban_1.set_guid(guid); + iban_1.set_nickname(u"Nickname 1"); + iban_1.set_value(u"IE64 IRCE 9205 0112 3456 78"); + iban_1 = iban_0; + + EXPECT_EQ(iban_0, iban_1); +} + +TEST(IBANTest, GetMetadata) { + IBAN local_iban = test::GetIBAN(); + local_iban.set_use_count(2); + local_iban.set_use_date(base::Time::FromDoubleT(25)); + AutofillMetadata local_metadata = local_iban.GetMetadata(); + + EXPECT_EQ(local_iban.guid(), local_metadata.id); + EXPECT_EQ(local_iban.use_count(), local_metadata.use_count); + EXPECT_EQ(local_iban.use_date(), local_metadata.use_date); +} + +// Verify that we set nickname with the processed string. We replace all tabs +// and newlines with whitespace, replace multiple spaces into a single one +// and trim leading/trailing whitespace. +TEST(IBANTest, SetNickname) { + IBAN iban(base::GenerateGUID()); + + // Normal input nickname. + iban.set_nickname(u"My doctor's IBAN"); + EXPECT_EQ(u"My doctor's IBAN", iban.nickname()); + + // Input nickname has leading and trailing whitespaces. + iban.set_nickname(u" My doctor's IBAN "); + EXPECT_EQ(u"My doctor's IBAN", iban.nickname()); + + // Input nickname has newlines. + iban.set_nickname(u"\r\n My doctor's\nIBAN \r\n"); + EXPECT_EQ(u"My doctor's IBAN", iban.nickname()); + + // Input nickname has tabs. + iban.set_nickname(u" \tMy doctor's\t IBAN\t "); + EXPECT_EQ(u"My doctor's IBAN", iban.nickname()); + + // Input nickname has newlines & whitespaces & tabs. + iban.set_nickname(u"\n\t My doctor's \tIBAN \n \r\n"); + EXPECT_EQ(u"My doctor's IBAN", iban.nickname()); + + // Input nickname has newlines & tabs & multi spaces. + iban.set_nickname(u"\n\t My doctor's \tIBAN \n \r\n"); + EXPECT_EQ(u"My doctor's IBAN", iban.nickname()); +} + +TEST(IBANTest, SetValue) { + IBAN iban(base::GenerateGUID()); + + // Input value. + iban.set_value(u"DE91 1000 0000 0123 4567 89"); + EXPECT_EQ(u"DE91 1000 0000 0123 4567 89", iban.value()); +} + +TEST(IBANTest, SetRawData) { + IBAN iban(base::GenerateGUID()); + + // Verify RawInfo can be correctly set and read. + iban.SetRawInfoWithVerificationStatus( + IBAN_VALUE, u"DE91 1000 0000 0123 4567 89", + structured_address::VerificationStatus::kUserVerified); + EXPECT_EQ(u"DE91 1000 0000 0123 4567 89", iban.GetRawInfo(IBAN_VALUE)); +} + +// Verify that for all invalid IBAN values, empty identifier value will +// be returned. +TEST(IBANTest, GetObfuscatedStringForValue_InvalidIbanValue) { + IBAN iban(base::GenerateGUID()); + iban.set_value(u"CH56-0483-5012-3456-7800-9999-9999-9999-9999"); + EXPECT_EQ(u"", iban.GetIdentifierStringForAutofillDisplay()); + + iban.set_value(u""); + EXPECT_EQ(u"", iban.GetIdentifierStringForAutofillDisplay()); + + iban.set_value(u"CH5"); + EXPECT_EQ(u"", iban.GetIdentifierStringForAutofillDisplay()); +} + +TEST(IBANTest, GetObfuscatedStringForValue_ValidIbanValue) { + // Verify each case of an IBAN ending in 1, 2, 3, and 4 unobfuscated + // digits. + IBAN iban(base::GenerateGUID()); + + iban.set_value(u"CH56 0483 5012 3456 7800 9"); + std::u16string leading_digits = u"CH"; + std::u16string trailing_digits = u"9"; + // Obfuscated value is: CH** **** **** **** **** 9 + std::u16string expected = + leading_digits + iban.RepeatEllipsisForTesting(4) + trailing_digits; + + EXPECT_EQ(expected, iban.GetIdentifierStringForAutofillDisplay()); + + iban.set_value(u"DE91 1000 0000 0123 4567 89"); + leading_digits = u"DE"; + trailing_digits = u"89"; + // Obfuscated value is: DE** **** **** **** **** 89 + expected = + leading_digits + iban.RepeatEllipsisForTesting(4) + trailing_digits; + + EXPECT_EQ(expected, iban.GetIdentifierStringForAutofillDisplay()); + + iban.set_value(u"GR96 0810 0010 0000 0123 4567 890"); + leading_digits = u"GR"; + trailing_digits = u"890"; + // Obfuscated value is: GR** **** **** **** **** **** 890 + expected = + leading_digits + iban.RepeatEllipsisForTesting(5) + trailing_digits; + + EXPECT_EQ(expected, iban.GetIdentifierStringForAutofillDisplay()); + + iban.set_value(u"PK70 BANK 0000 1234 5678 9000"); + leading_digits = u"PK"; + trailing_digits = u"9000"; + // Obfuscated value is: PK** **** **** **** **** 9000 + expected = + leading_digits + iban.RepeatEllipsisForTesting(4) + trailing_digits; + + EXPECT_EQ(expected, iban.GetIdentifierStringForAutofillDisplay()); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/phone_number.cc b/chromium/components/autofill/core/browser/data_model/phone_number.cc index ab2cb1424c2..f31a7707c12 100644 --- a/chromium/components/autofill/core/browser/data_model/phone_number.cc +++ b/chromium/components/autofill/core/browser/data_model/phone_number.cc @@ -68,6 +68,8 @@ bool PhoneNumber::operator==(const PhoneNumber& other) const { void PhoneNumber::GetSupportedTypes(ServerFieldTypeSet* supported_types) const { supported_types->insert(PHONE_HOME_WHOLE_NUMBER); supported_types->insert(PHONE_HOME_NUMBER); + supported_types->insert(PHONE_HOME_NUMBER_PREFIX); + supported_types->insert(PHONE_HOME_NUMBER_SUFFIX); supported_types->insert(PHONE_HOME_CITY_CODE); supported_types->insert(PHONE_HOME_CITY_AND_NUMBER); supported_types->insert(PHONE_HOME_COUNTRY_CODE); @@ -119,17 +121,6 @@ void PhoneNumber::GetMatchingTypes(const std::u16string& text, base::RemoveChars(stripped_text, u" .()-", &stripped_text); FormGroup::GetMatchingTypes(stripped_text, app_locale, matching_types); - // For US numbers, also compare to the three-digit prefix and the four-digit - // suffix, since web sites often split numbers into these two fields. - std::u16string number = GetInfo(AutofillType(PHONE_HOME_NUMBER), app_locale); - if (GetRegion(*profile_, app_locale) == "US" && - number.size() == (kPrefixLength + kSuffixLength)) { - std::u16string prefix = number.substr(kPrefixOffset, kPrefixLength); - std::u16string suffix = number.substr(kSuffixOffset, kSuffixLength); - if (text == prefix || text == suffix) - matching_types->insert(PHONE_HOME_NUMBER); - } - // TODO(crbug.com/581391): Investigate the use of PhoneNumberUtil when // matching phone numbers for upload. // If there is not already a match for PHONE_HOME_WHOLE_NUMBER, normalize the @@ -234,9 +225,10 @@ std::u16string PhoneNumber::GetInfoImpl(const AutofillType& type, // autocomplete="tel-local-suffix" corresponds to. In all countries using // this format that we are aware of (see unit tests), the suffix consists // of the last 4 digits, while the length of the prefix varies. - constexpr int kSuffixLength = 4; - DCHECK(number.size() >= kSuffixLength); - return number.substr(number.size() - kSuffixLength); + constexpr size_t kHomePhoneNumberSuffixLength = 4; + return number.size() >= kHomePhoneNumberSuffixLength + ? number.substr(number.size() - kHomePhoneNumberSuffixLength) + : number; } case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX: @@ -274,6 +266,8 @@ std::u16string PhoneNumber::GetInfoImpl(const AutofillType& type, } case PHONE_HOME_EXTENSION: + // Autofill doesn't support filling extensions, but some basic local + // heuristics classify them. return std::u16string(); default: @@ -306,6 +300,16 @@ bool PhoneNumber::SetInfoWithVerificationStatusImpl( // doesn't contain formatting marks. if (base::ContainsOnlyChars(number_, u"+0123456789")) { number_ = cached_parsed_phone_.GetFormattedNumber(); + } else { + // Strip `number_` of extensions, e.g. "(123)-123 ext. 123" -> "(123)-123". + // In the if case, this is done by `GetFormattedNumber()` already. To + // preserve the formatting, everything after the last digit of the whole + // number is removed manually here. The whole number only consists of digits + // and has any extensions removed already. + size_t i = 0; + for (auto digit : cached_parsed_phone_.GetWholeNumber()) + i = number_.find(digit, i) + 1; // Skip `digit`. + number_ = number_.substr(0, i); } return true; } @@ -336,12 +340,14 @@ bool PhoneNumber::PhoneCombineHelper::SetInfo(const AutofillType& type, return true; } - if (storable_type == PHONE_HOME_CITY_CODE) { + if (storable_type == PHONE_HOME_CITY_CODE || + storable_type == PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX) { city_ = value; return true; } - if (storable_type == PHONE_HOME_CITY_AND_NUMBER) { + if (storable_type == PHONE_HOME_CITY_AND_NUMBER || + storable_type == PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX) { phone_ = value; return true; } @@ -351,7 +357,13 @@ bool PhoneNumber::PhoneCombineHelper::SetInfo(const AutofillType& type, return true; } - if (storable_type == PHONE_HOME_NUMBER) { + if (storable_type == PHONE_HOME_NUMBER || + storable_type == PHONE_HOME_NUMBER_PREFIX) { + phone_ = value; + return true; + } + + if (storable_type == PHONE_HOME_NUMBER_SUFFIX) { phone_.append(value); return true; } diff --git a/chromium/components/autofill/core/browser/data_model/phone_number.h b/chromium/components/autofill/core/browser/data_model/phone_number.h index aa7665755aa..4d3bb90f35c 100644 --- a/chromium/components/autofill/core/browser/data_model/phone_number.h +++ b/chromium/components/autofill/core/browser/data_model/phone_number.h @@ -39,12 +39,6 @@ class PhoneNumber : public FormGroup { const std::u16string& value, structured_address::VerificationStatus status) override; - // Size and offset of the prefix and suffix portions of phone numbers. - static const size_t kPrefixOffset = 0; - static const size_t kPrefixLength = 3; - static const size_t kSuffixOffset = 3; - static const size_t kSuffixLength = 4; - // The class used to combine home phone parts into a whole number. class PhoneCombineHelper { public: diff --git a/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc b/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc index 52bd3f6aa0b..e81a743476f 100644 --- a/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/phone_number_unittest.cc @@ -67,8 +67,8 @@ TEST(PhoneNumberTest, Matcher) { {u"16502345678", {PHONE_HOME_WHOLE_NUMBER}}, {u"650", {PHONE_HOME_CITY_CODE}}, {u"2345678", {PHONE_HOME_NUMBER}}, - {u"234", {PHONE_HOME_NUMBER}}, - {u"5678", {PHONE_HOME_NUMBER}}, + {u"234", {PHONE_HOME_NUMBER_PREFIX}}, + {u"5678", {PHONE_HOME_NUMBER_SUFFIX}}, {u"2345", {}}, {u"6502345678", {PHONE_HOME_CITY_AND_NUMBER}}, {u"(650)2345678", {PHONE_HOME_CITY_AND_NUMBER}}}); @@ -241,7 +241,7 @@ TEST(PhoneNumberTest, PhoneCombineHelper) { profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"US"); PhoneNumber::PhoneCombineHelper number1; - EXPECT_FALSE(number1.SetInfo(AutofillType(ADDRESS_BILLING_CITY), u"1")); + EXPECT_FALSE(number1.SetInfo(AutofillType(ADDRESS_HOME_COUNTRY), u"1")); EXPECT_TRUE(number1.SetInfo(AutofillType(PHONE_HOME_COUNTRY_CODE), u"1")); EXPECT_TRUE(number1.SetInfo(AutofillType(PHONE_HOME_CITY_CODE), u"650")); EXPECT_TRUE(number1.SetInfo(AutofillType(PHONE_HOME_NUMBER), u"2345678")); @@ -272,8 +272,8 @@ TEST(PhoneNumberTest, PhoneCombineHelper) { PhoneNumber::PhoneCombineHelper number6; EXPECT_TRUE(number6.SetInfo(AutofillType(PHONE_HOME_CITY_CODE), u"650")); - EXPECT_TRUE(number6.SetInfo(AutofillType(PHONE_HOME_NUMBER), u"234")); - EXPECT_TRUE(number6.SetInfo(AutofillType(PHONE_HOME_NUMBER), u"5682")); + EXPECT_TRUE(number6.SetInfo(AutofillType(PHONE_HOME_NUMBER_PREFIX), u"234")); + EXPECT_TRUE(number6.SetInfo(AutofillType(PHONE_HOME_NUMBER_SUFFIX), u"5682")); EXPECT_TRUE(number6.ParseNumber(profile, "en-US", &parsed_phone)); EXPECT_EQ(u"(650) 234-5682", parsed_phone); @@ -281,8 +281,8 @@ TEST(PhoneNumberTest, PhoneCombineHelper) { // based on the app locale. PhoneNumber::PhoneCombineHelper number7; EXPECT_TRUE(number7.SetInfo(AutofillType(PHONE_HOME_CITY_CODE), u"650")); - EXPECT_TRUE(number7.SetInfo(AutofillType(PHONE_HOME_NUMBER), u"234")); - EXPECT_TRUE(number7.SetInfo(AutofillType(PHONE_HOME_NUMBER), u"5682")); + EXPECT_TRUE(number7.SetInfo(AutofillType(PHONE_HOME_NUMBER_PREFIX), u"234")); + EXPECT_TRUE(number7.SetInfo(AutofillType(PHONE_HOME_NUMBER_SUFFIX), u"5682")); EXPECT_TRUE(number7.ParseNumber(AutofillProfile(), "en-US", &parsed_phone)); EXPECT_EQ(u"(650) 234-5682", parsed_phone); } @@ -370,6 +370,13 @@ TEST(PhoneNumberTest, TrunkPrefix) { u"3381234567"); TestNumber(u"338 1234567", u"338", u"338", u"3381234567", u"3381234567"); } + + // RU: An 8 is used as a trunk prefix. + { + profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"RU"); + TestNumber(u"+7 495 123 45 67", u"8495", u"495", u"84951234567", + u"4951234567"); + } } // Tests that PHONE_HOME_NUMBER_PREFIX and PHONE_HOME_NUMBER_PREFIX are @@ -385,7 +392,7 @@ TEST(PhoneNumberTest, NumberPreAndSuffixes) { // The `locale` is irrelevant, as the `profile` has country information. const std::string locale = "en-US"; PhoneNumber phone_number(&profile); - phone_number.SetInfo(PHONE_HOME_WHOLE_NUMBER, number, locale); + EXPECT_TRUE(phone_number.SetInfo(PHONE_HOME_WHOLE_NUMBER, number, locale)); EXPECT_EQ(prefix, phone_number.GetInfo(PHONE_HOME_NUMBER_PREFIX, locale)); EXPECT_EQ(suffix, phone_number.GetInfo(PHONE_HOME_NUMBER_SUFFIX, locale)); }; @@ -402,6 +409,24 @@ TEST(PhoneNumberTest, NumberPreAndSuffixes) { TestNumber(u"090-1234-5678", u"1234", u"5678"); // Mobile TestNumber(u"+81 824-86-3123", u"86", u"3123"); // Different length prefix } + // DE + { + // Emergency numbers can be shorter than 4 digits. Make sure we don't crash. + profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"DE"); + TestNumber(u"110", u"", u"110"); + } +} + +// Tests that extensions are not stored and even stripped from the raw info. +TEST(PhoneNumberTest, Extension) { + AutofillProfile profile; + PhoneNumber phone(&profile); + const std::string locale = "en-US"; + EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"(650) 234-2345 ext. 234", + locale)); + EXPECT_EQ(u"(650) 234-2345", phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); + EXPECT_EQ(u"6502342345", phone.GetInfo(PHONE_HOME_WHOLE_NUMBER, locale)); + EXPECT_TRUE(phone.GetInfo(PHONE_HOME_EXTENSION, locale).empty()); } // Tests whether the |PHONE_HOME_COUNTRY_CODE| is added to the set of matching diff --git a/chromium/components/autofill/core/browser/field_filler.cc b/chromium/components/autofill/core/browser/field_filler.cc index f7309c0055e..f4ae5b2d479 100644 --- a/chromium/components/autofill/core/browser/field_filler.cc +++ b/chromium/components/autofill/core/browser/field_filler.cc @@ -18,7 +18,6 @@ #include "components/autofill/core/browser/address_normalizer.h" #include "components/autofill/core/browser/autofill_data_util.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/data_model/autofill_data_model.h" #include "components/autofill/core/browser/data_model/credit_card.h" @@ -32,6 +31,7 @@ #include "components/autofill/core/browser/proto/states.pb.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_l10n_util.h" +#include "components/autofill/core/common/autofill_regexes.h" #include "components/autofill/core/common/autofill_util.h" #include "components/strings/grit/components_strings.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h" @@ -268,7 +268,11 @@ bool FillStateSelectControl(const std::u16string& value, for (const auto& alternative_name : state_entry->alternative_names()) full_names.push_back(base::UTF8ToUTF16(alternative_name)); } else { - full_names.push_back(value); + if (value.size() > 2) { + full_names.push_back(value); + } else { + abbreviations.push_back(value); + } } } @@ -283,6 +287,17 @@ bool FillStateSelectControl(const std::u16string& value, if (!state_abbreviation.empty()) abbreviations.push_back(std::move(state_abbreviation)); + // Remove `abbreviations` from the `full_names` as a precautionary measure in + // case the `AlternativeStateNameMap` contains bad data. + base::ranges::sort(abbreviations); + full_names.erase( + base::ranges::remove_if(full_names, + [&](const std::u16string& full_name) { + return base::ranges::binary_search( + abbreviations, full_name); + }), + full_names.end()); + // Try an exact match of the abbreviation first. for (const auto& abbreviation : abbreviations) { if (!abbreviation.empty() && @@ -756,13 +771,14 @@ std::u16string GetExpirationDateForInput(const CreditCard& credit_card, if (base::FeatureList::IsEnabled( features::kAutofillFillCreditCardAsPerFormatString)) { std::vector<std::u16string> groups; - const char16_t* kFormatRegEx = u"mm(\\s?[/-]?\\s?)?yy(yy)?"; - // ^^^^ optional white space - // ^^^^^ optional separator - // ^^^ optional white space - // ^^^^^ 4 digit year? - if (MatchesPattern(field.placeholder, kFormatRegEx, &groups) || - MatchesPattern(field.label, kFormatRegEx, &groups)) { + static const char16_t kFormatRegEx[] = u"mm(\\s?[/-]?\\s?)?yy(yy)?"; + // ^^^^ opt white space + // ^^^^^ opt separator + // ^^^ opt white space + // ^^^^^ 4 digit + // year? + if (MatchesRegex<kFormatRegEx>(field.placeholder, &groups) || + MatchesRegex<kFormatRegEx>(field.label, &groups)) { bool is_two_digit_year = groups[2].empty(); std::u16string expiration_candidate = base::StrCat({month, groups[1], @@ -1077,24 +1093,6 @@ std::u16string FieldFiller::GetPhoneNumberValueForInput( const std::u16string& number, const std::u16string& phone_home_city_and_number, const FormFieldData& field_data) { - // TODO(crbug.com/581485): Investigate the use of libphonenumber here. - // Check to see if the |field| size matches the "prefix" or "suffix" size or - // if the field was labeled as such. If so, return the appropriate substring. - if (number.length() == - PhoneNumber::kPrefixLength + PhoneNumber::kSuffixLength) { - if (field.phone_part() == AutofillField::PHONE_PREFIX || - field_data.max_length == PhoneNumber::kPrefixLength) { - return number.substr(PhoneNumber::kPrefixOffset, - PhoneNumber::kPrefixLength); - } - - if (field.phone_part() == AutofillField::PHONE_SUFFIX || - field_data.max_length == PhoneNumber::kSuffixLength) { - return number.substr(PhoneNumber::kSuffixOffset, - PhoneNumber::kSuffixLength); - } - } - // If no max length was specified, return the complete number. if (field_data.max_length == 0) return number; diff --git a/chromium/components/autofill/core/browser/field_filler_unittest.cc b/chromium/components/autofill/core/browser/field_filler_unittest.cc index 195b28ad333..030677576df 100644 --- a/chromium/components/autofill/core/browser/field_filler_unittest.cc +++ b/chromium/components/autofill/core/browser/field_filler_unittest.cc @@ -167,21 +167,21 @@ TEST_F(AutofillFieldFillerTest, Type) { EXPECT_EQ(FieldTypeGroup::kName, field.Type().group()); // Set the server type and check it. - prediction.set_type(ADDRESS_BILLING_LINE1); + prediction.set_type(ADDRESS_HOME_LINE1); field.set_server_predictions({prediction}); EXPECT_EQ(ADDRESS_HOME_LINE1, field.Type().GetStorableType()); - EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kAddressHome, field.Type().group()); // Checks that overall_type trumps everything. - field.SetTypeTo(AutofillType(ADDRESS_BILLING_ZIP)); + field.SetTypeTo(AutofillType(ADDRESS_HOME_ZIP)); EXPECT_EQ(ADDRESS_HOME_ZIP, field.Type().GetStorableType()); - EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kAddressHome, field.Type().group()); // Checks that setting server type resets overall type. - prediction.set_type(ADDRESS_BILLING_LINE1); + prediction.set_type(ADDRESS_HOME_LINE1); field.set_server_predictions({prediction}); EXPECT_EQ(ADDRESS_HOME_LINE1, field.Type().GetStorableType()); - EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kAddressHome, field.Type().group()); // Remove the server type to make sure the heuristic type is preserved. prediction.set_type(NO_SERVER_DATA); @@ -190,9 +190,9 @@ TEST_F(AutofillFieldFillerTest, Type) { EXPECT_EQ(FieldTypeGroup::kName, field.Type().group()); // Checks that overall_type trumps everything. - field.SetTypeTo(AutofillType(ADDRESS_BILLING_ZIP)); + field.SetTypeTo(AutofillType(ADDRESS_HOME_ZIP)); EXPECT_EQ(ADDRESS_HOME_ZIP, field.Type().GetStorableType()); - EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group()); + EXPECT_EQ(FieldTypeGroup::kAddressHome, field.Type().group()); // Set the heuristic type and check it and reset overall Type. field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST); @@ -577,11 +577,6 @@ INSTANTIATE_TEST_SUITE_P( AutofillPhoneFieldFillerTestCase{HTML_TYPE_TEL_LOCAL_SUFFIX, /*field_max_length=*/0, u"4578", u"+15145554578"}, - // Filling a phone type field with a max length of 3 should fill only - // the prefix. - AutofillPhoneFieldFillerTestCase{HTML_TYPE_TEL_LOCAL, - /*field_max_length=*/3, u"555", - u"+15145554578"}, // TODO(crbug.com/581485): There should be a test case where the full // number is requested (HTML_TYPE_TEL) but a field_max_length of 3 would // fill the prefix. @@ -2058,6 +2053,31 @@ TEST_F(AutofillFieldFillerTest, FillUpperCaseAbbreviationInStateTextField) { EXPECT_EQ(u"BY", field.value); } +// Tests that Autofill does not fill the state when abbreviated data is stored +// in the profile and none of the options match with the abbreviated state. +TEST_F(AutofillFieldFillerTest, + DoNotFillStateFieldWhenAbbrStoredInProfileAndNotInOptionsList) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillUseAlternativeStateNameMap); + + test::ClearAlternativeStateNameMapForTesting(); + std::vector<const char*> kState = {"Colombia", "Connecticut", "Colifornia"}; + + AutofillField field; + test::CreateTestSelectField(kState, &field); + field.set_heuristic_type(GetActivePatternSource(), ADDRESS_HOME_STATE); + + AutofillProfile address; + address.SetRawInfo(ADDRESS_HOME_STATE, u"CO"); + address.SetRawInfo(ADDRESS_HOME_COUNTRY, u"US"); + + FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); + filler.FillFormField(field, &address, /*forced_fill_values=*/{}, &field, + /*cvc=*/std::u16string(), + mojom::RendererFormDataAction::kFill); + EXPECT_EQ(u"", field.value); +} + TEST_F(AutofillFieldFillerTest, PreviewVirtualMonth) { AutofillField field; field.form_control_type = "text"; @@ -2228,7 +2248,6 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualCVC) { } TEST_F(AutofillFieldFillerTest, PreviewVirtualCVCAmericanExpress) { - const char kAmericanExpressCard[] = "americanExpressCC"; AutofillField field; field.form_control_type = "text"; FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr); @@ -2249,7 +2268,6 @@ TEST_F(AutofillFieldFillerTest, PreviewVirtualCardNumber) { field.set_heuristic_type(GetActivePatternSource(), CREDIT_CARD_NUMBER); field.set_credit_card_number_offset(50); field.form_control_type = "text"; - const char kMasterCard[] = "masterCardCC"; CreditCard card = test::GetVirtualCard(); card.SetNumber(u"5454545454545454"); diff --git a/chromium/components/autofill/core/browser/field_types.cc b/chromium/components/autofill/core/browser/field_types.cc index 15d1af444c1..774e1e75f30 100644 --- a/chromium/components/autofill/core/browser/field_types.cc +++ b/chromium/components/autofill/core/browser/field_types.cc @@ -23,10 +23,15 @@ ServerFieldType ToSafeServerFieldType( // Shipping addresses (values [44,50]) are deprecated. !(44 <= t && t <= 50) && // Probably-account creation password (value 94) is deprecated. - !(t == 94) && - // Fax numbers (values [20,24]) are deprecated in Chrome, but still - // supported by the server. - !(t >= PHONE_FAX_NUMBER && t <= PHONE_FAX_WHOLE_NUMBER); + t != 94 && + // Billing addresses (values [37,43], 78, 80, 82, 84) are deprecated. + !(37 <= t && t <= 43) && t != 78 && t != 80 && t != 82 && t != 84 && + // Billing phone numbers (values [62,66]) are deprecated. + !(62 <= t && t <= 66) && + // Billing names (values [67,72]) are deprecated. + !(67 <= t && t <= 72) && + // Fax numbers (values [20,24]) are deprecated. + !(20 <= t && t <= 24); }; return IsValid(raw_value) ? static_cast<ServerFieldType>(raw_value) : fallback_value; @@ -80,32 +85,6 @@ bool IsFillableFieldType(ServerFieldType field_type) { case ADDRESS_HOME_FLOOR: return true; - // Billing address types that should not be returned by GetStorableType(). - case NAME_BILLING_FIRST: - case NAME_BILLING_MIDDLE: - case NAME_BILLING_LAST: - case NAME_BILLING_MIDDLE_INITIAL: - case NAME_BILLING_FULL: - case NAME_BILLING_SUFFIX: - case PHONE_BILLING_NUMBER: - case PHONE_BILLING_CITY_CODE: - case PHONE_BILLING_COUNTRY_CODE: - case PHONE_BILLING_CITY_AND_NUMBER: - case PHONE_BILLING_WHOLE_NUMBER: - case ADDRESS_BILLING_LINE1: - case ADDRESS_BILLING_LINE2: - case ADDRESS_BILLING_LINE3: - case ADDRESS_BILLING_APT_NUM: - case ADDRESS_BILLING_CITY: - case ADDRESS_BILLING_STATE: - case ADDRESS_BILLING_ZIP: - case ADDRESS_BILLING_COUNTRY: - case ADDRESS_BILLING_STREET_ADDRESS: - case ADDRESS_BILLING_SORTING_CODE: - case ADDRESS_BILLING_DEPENDENT_LOCALITY: - NOTREACHED(); - return false; - case CREDIT_CARD_NAME_FULL: case CREDIT_CARD_NAME_FIRST: case CREDIT_CARD_NAME_LAST: @@ -117,11 +96,15 @@ bool IsFillableFieldType(ServerFieldType field_type) { case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: case CREDIT_CARD_TYPE: case CREDIT_CARD_VERIFICATION_CODE: + case CREDIT_CARD_STANDALONE_VERIFICATION_CODE: return true; case UPI_VPA: return base::FeatureList::IsEnabled(features::kAutofillSaveAndFillVPA); + case IBAN_VALUE: + return base::FeatureList::IsEnabled(features::kAutofillParseIBANFields); + case COMPANY_NAME: return true; @@ -153,18 +136,13 @@ bool IsFillableFieldType(ServerFieldType field_type) { case NO_SERVER_DATA: case EMPTY_TYPE: case AMBIGUOUS_TYPE: - case PHONE_FAX_NUMBER: - case PHONE_FAX_CITY_CODE: - case PHONE_FAX_COUNTRY_CODE: - case PHONE_FAX_CITY_AND_NUMBER: - case PHONE_FAX_WHOLE_NUMBER: case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: case PRICE: case SEARCH_TERM: case BIRTHDATE_DAY: case BIRTHDATE_MONTH: - case BIRTHDATE_YEAR_4_DIGITS: + case BIRTHDATE_4_DIGIT_YEAR: case UNKNOWN_TYPE: case MAX_VALID_FIELD_TYPE: return false; @@ -205,18 +183,6 @@ base::StringPiece FieldTypeToStringPiece(ServerFieldType type) { return "NAME_FULL"; case NAME_SUFFIX: return "NAME_SUFFIX"; - case NAME_BILLING_FIRST: - return "NAME_BILLING_FIRST"; - case NAME_BILLING_MIDDLE: - return "NAME_BILLING_MIDDLE"; - case NAME_BILLING_LAST: - return "NAME_BILLING_LAST"; - case NAME_BILLING_MIDDLE_INITIAL: - return "NAME_BILLING_MIDDLE_INITIAL"; - case NAME_BILLING_FULL: - return "NAME_BILLING_FULL"; - case NAME_BILLING_SUFFIX: - return "NAME_BILLING_SUFFIX"; case EMAIL_ADDRESS: return "EMAIL_ADDRESS"; case PHONE_HOME_NUMBER: @@ -239,16 +205,6 @@ base::StringPiece FieldTypeToStringPiece(ServerFieldType type) { return "PHONE_HOME_WHOLE_NUMBER"; case PHONE_HOME_EXTENSION: return "PHONE_HOME_EXTENSION"; - case PHONE_FAX_NUMBER: - return "PHONE_FAX_NUMBER"; - case PHONE_FAX_CITY_CODE: - return "PHONE_FAX_CITY_CODE"; - case PHONE_FAX_COUNTRY_CODE: - return "PHONE_FAX_COUNTRY_CODE"; - case PHONE_FAX_CITY_AND_NUMBER: - return "PHONE_FAX_CITY_AND_NUMBER"; - case PHONE_FAX_WHOLE_NUMBER: - return "PHONE_FAX_WHOLE_NUMBER"; case ADDRESS_HOME_ADDRESS: return "ADDRESS_HOME_ADDRESS"; case ADDRESS_HOME_ADDRESS_WITH_NAME: @@ -271,27 +227,11 @@ base::StringPiece FieldTypeToStringPiece(ServerFieldType type) { return "ADDRESS_HOME_ZIP"; case ADDRESS_HOME_COUNTRY: return "ADDRESS_HOME_COUNTRY"; - case ADDRESS_BILLING_LINE1: - return "ADDRESS_BILLING_LINE1"; - case ADDRESS_BILLING_LINE2: - return "ADDRESS_BILLING_LINE2"; - case ADDRESS_BILLING_LINE3: - return "ADDRESS_BILLING_LINE3"; - case ADDRESS_BILLING_APT_NUM: - return "ADDRESS_BILLING_APT_NUM"; - case ADDRESS_BILLING_CITY: - return "ADDRESS_BILLING_CITY"; - case ADDRESS_BILLING_STATE: - return "ADDRESS_BILLING_STATE"; - case ADDRESS_BILLING_ZIP: - return "ADDRESS_BILLING_ZIP"; - case ADDRESS_BILLING_COUNTRY: - return "ADDRESS_BILLING_COUNTRY"; case BIRTHDATE_DAY: return "BIRTHDATE_DAY"; case BIRTHDATE_MONTH: return "BIRTHDATE_MONTH"; - case BIRTHDATE_YEAR_4_DIGITS: + case BIRTHDATE_4_DIGIT_YEAR: return "BIRTHDATE_YEAR_4_DIGITS"; case CREDIT_CARD_NAME_FULL: return "CREDIT_CARD_NAME_FULL"; @@ -319,16 +259,6 @@ base::StringPiece FieldTypeToStringPiece(ServerFieldType type) { return "COMPANY_NAME"; case FIELD_WITH_DEFAULT_VALUE: return "FIELD_WITH_DEFAULT_VALUE"; - case PHONE_BILLING_NUMBER: - return "PHONE_BILLING_NUMBER"; - case PHONE_BILLING_CITY_CODE: - return "PHONE_BILLING_CITY_CODE"; - case PHONE_BILLING_COUNTRY_CODE: - return "PHONE_BILLING_COUNTRY_CODE"; - case PHONE_BILLING_CITY_AND_NUMBER: - return "PHONE_BILLING_CITY_AND_NUMBER"; - case PHONE_BILLING_WHOLE_NUMBER: - return "PHONE_BILLING_WHOLE_NUMBER"; case MERCHANT_EMAIL_SIGNUP: return "MERCHANT_EMAIL_SIGNUP"; case MERCHANT_PROMO_CODE: @@ -339,16 +269,10 @@ base::StringPiece FieldTypeToStringPiece(ServerFieldType type) { return "ACCOUNT_CREATION_PASSWORD"; case ADDRESS_HOME_STREET_ADDRESS: return "ADDRESS_HOME_STREET_ADDRESS"; - case ADDRESS_BILLING_STREET_ADDRESS: - return "ADDRESS_BILLING_STREET_ADDRESS"; case ADDRESS_HOME_SORTING_CODE: return "ADDRESS_HOME_SORTING_CODE"; - case ADDRESS_BILLING_SORTING_CODE: - return "ADDRESS_BILLING_SORTING_CODE"; case ADDRESS_HOME_DEPENDENT_LOCALITY: return "ADDRESS_HOME_DEPENDENT_LOCALITY"; - case ADDRESS_BILLING_DEPENDENT_LOCALITY: - return "ADDRESS_BILLING_DEPENDENT_LOCALITY"; case NOT_ACCOUNT_CREATION_PASSWORD: return "NOT_ACCOUNT_CREATION_PASSWORD"; case USERNAME: @@ -391,6 +315,10 @@ base::StringPiece FieldTypeToStringPiece(ServerFieldType type) { return "ADDRESS_HOME_OTHER_SUBUNIT"; case AMBIGUOUS_TYPE: return "AMBIGUOUS_TYPE"; + case IBAN_VALUE: + return "IBAN_VALUE"; + case CREDIT_CARD_STANDALONE_VERIFICATION_CODE: + return "CREDIT_CARD_STANDALONE_VERIFICATION_CODE"; case MAX_VALID_FIELD_TYPE: return ""; } @@ -473,6 +401,12 @@ base::StringPiece FieldTypeToStringPiece(HtmlFieldType type) { return "HTML_TYPE_TEL_EXTENSION"; case HTML_TYPE_EMAIL: return "HTML_TYPE_EMAIL"; + case HTML_TYPE_BIRTHDATE_DAY: + return "HTML_TYPE_BIRTHDATE_DAY"; + case HTML_TYPE_BIRTHDATE_MONTH: + return "HTML_TYPE_BIRTHDATE_MONTH"; + case HTML_TYPE_BIRTHDATE_YEAR: + return "HTML_TYPE_BIRTHDATE_YEAR"; case HTML_TYPE_TRANSACTION_AMOUNT: return "HTML_TYPE_TRANSACTION_AMOUNT"; case HTML_TYPE_TRANSACTION_CURRENCY: @@ -493,6 +427,8 @@ base::StringPiece FieldTypeToStringPiece(HtmlFieldType type) { return "HTML_TYPE_ONE_TIME_CODE"; case HTML_TYPE_MERCHANT_PROMO_CODE: return "HTML_TYPE_MERCHANT_PROMO_CODE"; + case HTML_TYPE_IBAN: + return "HTML_TYPE_IBAN"; case HTML_TYPE_UNRECOGNIZED: return "HTML_TYPE_UNRECOGNIZED"; } diff --git a/chromium/components/autofill/core/browser/field_types.h b/chromium/components/autofill/core/browser/field_types.h index adc66c8bec1..d424d24955f 100644 --- a/chromium/components/autofill/core/browser/field_types.h +++ b/chromium/components/autofill/core/browser/field_types.h @@ -9,6 +9,7 @@ #include "base/strings/string_piece_forward.h" #include "components/autofill/core/common/dense_set.h" +#include "components/autofill/core/common/html_field_types.h" namespace autofill { @@ -50,15 +51,7 @@ enum ServerFieldType { PHONE_HOME_WHOLE_NUMBER = 14, // Work phone numbers (values [15,19]) are deprecated. - - // Fax numbers (values [20,24]) are deprecated in Chrome, but still supported - // by the server. - PHONE_FAX_NUMBER = 20, - PHONE_FAX_CITY_CODE = 21, - PHONE_FAX_COUNTRY_CODE = 22, - PHONE_FAX_CITY_AND_NUMBER = 23, - PHONE_FAX_WHOLE_NUMBER = 24, - + // Fax numbers (values [20,24]) are deprecated. // Cell phone numbers (values [25, 29]) are deprecated. ADDRESS_HOME_LINE1 = 30, @@ -68,15 +61,9 @@ enum ServerFieldType { ADDRESS_HOME_STATE = 34, ADDRESS_HOME_ZIP = 35, ADDRESS_HOME_COUNTRY = 36, - ADDRESS_BILLING_LINE1 = 37, - ADDRESS_BILLING_LINE2 = 38, - ADDRESS_BILLING_APT_NUM = 39, - ADDRESS_BILLING_CITY = 40, - ADDRESS_BILLING_STATE = 41, - ADDRESS_BILLING_ZIP = 42, - ADDRESS_BILLING_COUNTRY = 43, - // ADDRESS_SHIPPING values [44,50] are deprecated. + // ADDRESS_BILLING values [37, 43] are deprecated. + // ADDRESS_SHIPPING values [44, 50] are deprecated. CREDIT_CARD_NAME_FULL = 51, CREDIT_CARD_NUMBER = 52, @@ -93,18 +80,8 @@ enum ServerFieldType { // Generic type whose default value is known. FIELD_WITH_DEFAULT_VALUE = 61, - PHONE_BILLING_NUMBER = 62, - PHONE_BILLING_CITY_CODE = 63, - PHONE_BILLING_COUNTRY_CODE = 64, - PHONE_BILLING_CITY_AND_NUMBER = 65, - PHONE_BILLING_WHOLE_NUMBER = 66, - - NAME_BILLING_FIRST = 67, - NAME_BILLING_MIDDLE = 68, - NAME_BILLING_LAST = 69, - NAME_BILLING_MIDDLE_INITIAL = 70, - NAME_BILLING_FULL = 71, - NAME_BILLING_SUFFIX = 72, + // PHONE_BILLING values [62, 66] are deprecated. + // NAME_BILLING values [67, 72] are deprecated. // Field types for options generally found in merchant buyflows. Given that // these are likely to be filled out differently on a case by case basis, @@ -124,7 +101,7 @@ enum ServerFieldType { // 123 Main Street, // Apt. #42 ADDRESS_HOME_STREET_ADDRESS = 77, - ADDRESS_BILLING_STREET_ADDRESS = 78, + // ADDRESS_BILLING_STREET_ADDRESS 78 is deprecated. // A sorting code is similar to a postal code. However, whereas a postal code // normally refers to a single geographical location, a sorting code often @@ -132,17 +109,17 @@ enum ServerFieldType { // might be geographically distributed. The most prominent example of a // sorting code system is CEDEX in France. ADDRESS_HOME_SORTING_CODE = 79, - ADDRESS_BILLING_SORTING_CODE = 80, + // ADDRESS_BILLING_SORTING_CODE 80 is deprecated. // A dependent locality is a subunit of a locality, where a "locality" is // roughly equivalent to a city. Examples of dependent localities include // inner-city districts and suburbs. ADDRESS_HOME_DEPENDENT_LOCALITY = 81, - ADDRESS_BILLING_DEPENDENT_LOCALITY = 82, + // ADDRESS_BILLING_DEPENDENT_LOCALITY 82 is deprecated. // The third line of the street address. ADDRESS_HOME_LINE3 = 83, - ADDRESS_BILLING_LINE3 = 84, + // ADDRESS_BILLING_LINE3 84 is deprecated. // Inverse of ACCOUNT_CREATION_PASSWORD. Sent when there is data that // a previous upload of ACCOUNT_CREATION_PASSWORD was incorrect. @@ -161,6 +138,7 @@ enum ServerFieldType { CREDIT_CARD_NAME_FIRST = 91, CREDIT_CARD_NAME_LAST = 92, + // Extensions are detected, but not filled. PHONE_HOME_EXTENSION = 93, // PROBABLY_ACCOUNT_CREATION_PASSWORD value 94 is deprecated. @@ -246,7 +224,7 @@ enum ServerFieldType { // Types to represent a birthdate. BIRTHDATE_DAY = 118, BIRTHDATE_MONTH = 119, - BIRTHDATE_YEAR_4_DIGITS = 120, + BIRTHDATE_4_DIGIT_YEAR = 120, // Types for better trunk prefix support for phone numbers. // Like PHONE_HOME_CITY_CODE, but with a trunk prefix, if applicable in the @@ -261,97 +239,16 @@ enum ServerFieldType { PHONE_HOME_NUMBER_PREFIX = 123, PHONE_HOME_NUMBER_SUFFIX = 124, + // International Bank Account Number (IBAN) details are usually entered on + // banking and merchant websites used to make international transactions. + // See https://en.wikipedia.org/wiki/International_Bank_Account_Number. + IBAN_VALUE = 125, + + // Standalone card verification code (CVC). + CREDIT_CARD_STANDALONE_VERIFICATION_CODE = 126, // No new types can be added without a corresponding change to the Autofill // server. - MAX_VALID_FIELD_TYPE = 125, -}; - -// The list of all HTML autocomplete field type hints supported by Chrome. -// See [ http://is.gd/whatwg_autocomplete ] for the full list of specced hints. -enum HtmlFieldType { - // Default type. - HTML_TYPE_UNSPECIFIED, - - // Name types. - HTML_TYPE_NAME, - HTML_TYPE_HONORIFIC_PREFIX, - HTML_TYPE_GIVEN_NAME, - HTML_TYPE_ADDITIONAL_NAME, - HTML_TYPE_FAMILY_NAME, - - // Business types. - HTML_TYPE_ORGANIZATION, - - // Address types. - HTML_TYPE_STREET_ADDRESS, - HTML_TYPE_ADDRESS_LINE1, - HTML_TYPE_ADDRESS_LINE2, - HTML_TYPE_ADDRESS_LINE3, - HTML_TYPE_ADDRESS_LEVEL1, // For U.S. addresses, corresponds to the state. - HTML_TYPE_ADDRESS_LEVEL2, // For U.S. addresses, corresponds to the city. - HTML_TYPE_ADDRESS_LEVEL3, // An area that is more specific than LEVEL2. - HTML_TYPE_COUNTRY_CODE, // The ISO 3166-1-alpha-2 country code. - HTML_TYPE_COUNTRY_NAME, // The localized country name. - HTML_TYPE_POSTAL_CODE, - HTML_TYPE_FULL_ADDRESS, // The complete address, formatted for display. - - // Credit card types. - HTML_TYPE_CREDIT_CARD_NAME_FULL, - HTML_TYPE_CREDIT_CARD_NAME_FIRST, - HTML_TYPE_CREDIT_CARD_NAME_LAST, - HTML_TYPE_CREDIT_CARD_NUMBER, - HTML_TYPE_CREDIT_CARD_EXP, - HTML_TYPE_CREDIT_CARD_EXP_MONTH, - HTML_TYPE_CREDIT_CARD_EXP_YEAR, - HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE, - HTML_TYPE_CREDIT_CARD_TYPE, - - // Phone number types. - HTML_TYPE_TEL, - HTML_TYPE_TEL_COUNTRY_CODE, - HTML_TYPE_TEL_NATIONAL, - HTML_TYPE_TEL_AREA_CODE, - HTML_TYPE_TEL_LOCAL, - HTML_TYPE_TEL_LOCAL_PREFIX, - HTML_TYPE_TEL_LOCAL_SUFFIX, - HTML_TYPE_TEL_EXTENSION, - - // Email. - HTML_TYPE_EMAIL, - - // Transaction details. - HTML_TYPE_TRANSACTION_AMOUNT, - HTML_TYPE_TRANSACTION_CURRENCY, - - // Variants of type hints specified in the HTML specification that are - // inferred based on a field's 'maxlength' attribute. - // TODO(isherman): Remove these types, in favor of understanding maxlength - // when filling fields. See also: AutofillField::phone_part_. - HTML_TYPE_ADDITIONAL_NAME_INITIAL, - HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, - HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, - HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, - HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, - - // Universal Payment Interface - Virtual Payment Address. - HTML_TYPE_UPI_VPA, - - // Phone number verification one-time-codes. - HTML_TYPE_ONE_TIME_CODE, - - // Promo code for merchant sites. - HTML_TYPE_MERCHANT_PROMO_CODE, - - // Non-standard autocomplete types. - HTML_TYPE_UNRECOGNIZED, -}; - -// The list of all HTML autocomplete field mode hints supported by Chrome. -// See [ http://is.gd/whatwg_autocomplete ] for the full list of specced hints. -enum HtmlFieldMode { - HTML_MODE_NONE, - HTML_MODE_BILLING, - HTML_MODE_SHIPPING, + MAX_VALID_FIELD_TYPE = 127, }; enum class FieldTypeGroup { diff --git a/chromium/components/autofill/core/browser/field_types_unittest.cc b/chromium/components/autofill/core/browser/field_types_unittest.cc index f4afb70f6f9..c0b406bddc7 100644 --- a/chromium/components/autofill/core/browser/field_types_unittest.cc +++ b/chromium/components/autofill/core/browser/field_types_unittest.cc @@ -36,13 +36,6 @@ TEST(FieldTypesTest, IsValidServerFieldType) { ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_COUNTRY, - ADDRESS_BILLING_LINE1, - ADDRESS_BILLING_LINE2, - ADDRESS_BILLING_APT_NUM, - ADDRESS_BILLING_CITY, - ADDRESS_BILLING_STATE, - ADDRESS_BILLING_ZIP, - ADDRESS_BILLING_COUNTRY, CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER, CREDIT_CARD_EXP_MONTH, @@ -50,33 +43,19 @@ TEST(FieldTypesTest, IsValidServerFieldType) { CREDIT_CARD_EXP_4_DIGIT_YEAR, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, + CREDIT_CARD_STANDALONE_VERIFICATION_CODE, CREDIT_CARD_TYPE, CREDIT_CARD_VERIFICATION_CODE, COMPANY_NAME, FIELD_WITH_DEFAULT_VALUE, - PHONE_BILLING_NUMBER, - PHONE_BILLING_CITY_CODE, - PHONE_BILLING_COUNTRY_CODE, - PHONE_BILLING_CITY_AND_NUMBER, - PHONE_BILLING_WHOLE_NUMBER, - NAME_BILLING_FIRST, - NAME_BILLING_MIDDLE, - NAME_BILLING_LAST, - NAME_BILLING_MIDDLE_INITIAL, - NAME_BILLING_FULL, - NAME_BILLING_SUFFIX, MERCHANT_EMAIL_SIGNUP, MERCHANT_PROMO_CODE, PASSWORD, ACCOUNT_CREATION_PASSWORD, ADDRESS_HOME_STREET_ADDRESS, - ADDRESS_BILLING_STREET_ADDRESS, ADDRESS_HOME_SORTING_CODE, - ADDRESS_BILLING_SORTING_CODE, ADDRESS_HOME_DEPENDENT_LOCALITY, - ADDRESS_BILLING_DEPENDENT_LOCALITY, ADDRESS_HOME_LINE3, - ADDRESS_BILLING_LINE3, NOT_ACCOUNT_CREATION_PASSWORD, USERNAME, USERNAME_AND_EMAIL_ADDRESS, @@ -94,6 +73,7 @@ TEST(FieldTypesTest, IsValidServerFieldType) { SINGLE_USERNAME, NOT_USERNAME, UPI_VPA, + IBAN_VALUE, ADDRESS_HOME_STREET_NAME, ADDRESS_HOME_HOUSE_NUMBER, ADDRESS_HOME_SUBPREMISE, @@ -111,7 +91,7 @@ TEST(FieldTypesTest, IsValidServerFieldType) { NAME_FULL_WITH_HONORIFIC_PREFIX, BIRTHDATE_DAY, BIRTHDATE_MONTH, - BIRTHDATE_YEAR_4_DIGITS, + BIRTHDATE_4_DIGIT_YEAR, }; ServerFieldType kInvalidValue = static_cast<ServerFieldType>(123456); ASSERT_FALSE(kValidFieldTypes.count(kInvalidValue)); diff --git a/chromium/components/autofill/core/browser/form_data_importer.cc b/chromium/components/autofill/core/browser/form_data_importer.cc index e87e427c1e8..609323198c9 100644 --- a/chromium/components/autofill/core/browser/form_data_importer.cc +++ b/chromium/components/autofill/core/browser/form_data_importer.cc @@ -38,7 +38,6 @@ #include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/validation.h" -#include "components/autofill/core/common/autofill_clock.h" #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" @@ -80,145 +79,52 @@ bool IsValidFieldTypeAndValue(const ServerFieldTypeSet types_seen, ? field_type_group != FieldTypeGroup::kPhoneBilling && field_type_group != FieldTypeGroup::kPhoneHome : field_type != PHONE_HOME_NUMBER)) { - if (import_log_buffer) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Multiple fields of type " - << AutofillType::ServerFieldTypeToString(field_type) - << "." << CTag{}; - } + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Multiple fields of type " + << AutofillType::ServerFieldTypeToString(field_type) << "." << CTag{}; return false; } // Abandon the import if an email address value shows up in a field that is // not an email address. if (field_type != EMAIL_ADDRESS && IsValidEmailAddress(value)) { - if (import_log_buffer) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Email address found in field of different type: " - << AutofillType::ServerFieldTypeToString(field_type) - << CTag{}; - } + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Email address found in field of different type: " + << AutofillType::ServerFieldTypeToString(field_type) << CTag{}; return false; } return true; } -// Returns true if minimum requirements for import of a given |profile| have -// been met. An address submitted via a form must have at least the fields -// required as determined by its country code. -// No verification of validity of the contents is performed. This is an -// existence check only. -bool IsMinimumAddress(const AutofillProfile& profile, - const std::string& predicted_country_code, - const std::string& app_locale, - LogBuffer* import_log_buffer, - bool collect_metrics) { - AutofillCountry country(predicted_country_code, app_locale); - - // Include the details of the country to the log. - if (import_log_buffer) - *import_log_buffer << country; - - // Check the |ADDRESS_HOME_LINE1| requirement. - bool is_line1_missing = false; - if (country.requires_line1() && !profile.HasRawInfo(ADDRESS_HOME_LINE1) && - !profile.HasRawInfo(ADDRESS_HOME_STREET_NAME)) { - if (import_log_buffer) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Missing required ADDRESS_HOME_LINE1." << CTag{}; - } - is_line1_missing = true; - } - - // Check the |ADDRESS_HOME_CITY| requirement. - bool is_city_missing = false; - if (country.requires_city() && !profile.HasRawInfo(ADDRESS_HOME_CITY)) { - if (import_log_buffer) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Missing required ADDRESS_HOME_CITY." << CTag{}; - } - is_city_missing = true; - } - - // Check the |ADDRESS_HOME_STATE| requirement. - bool is_state_missing = false; - if (country.requires_state() && !profile.HasRawInfo(ADDRESS_HOME_STATE)) { - if (import_log_buffer) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Missing required ADDRESS_HOME_STATE." << CTag{}; - } - is_state_missing = true; - } - - // Check the |ADDRESS_HOME_ZIP| requirement. - bool is_zip_missing = false; - if (country.requires_zip() && !profile.HasRawInfo(ADDRESS_HOME_ZIP)) { - if (import_log_buffer) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Missing required ADDRESS_HOME_ZIP." << CTag{}; - } - is_zip_missing = true; +// |imported_credit_card| refers to credit card that was most recently submitted +// and |fetched_card_instrument_id| refers to the instrument id of the most +// recently downstreamed (fetched from the server) credit card. These need to +// match to offer virtual card enrollment for the |imported_credit_card| . +bool ShouldOfferVirtualCardEnrollment( + const CreditCard* imported_credit_card, + absl::optional<int64_t> fetched_card_instrument_id) { + if (!base::FeatureList::IsEnabled( + features::kAutofillEnableUpdateVirtualCardEnrollment)) { + return false; } - bool is_zip_or_state_requirement_violated = false; - if (country.requires_zip_or_state() && - !profile.HasRawInfo(ADDRESS_HOME_ZIP) && - !profile.HasRawInfo(ADDRESS_HOME_STATE)) { - if (import_log_buffer) { - *import_log_buffer - << LogMessage::kImportAddressProfileFromFormFailed - << "Missing required ADDRESS_HOME_ZIP or ADDRESS_HOME_STATE." - << CTag{}; - } - is_zip_or_state_requirement_violated = true; - } + if (!imported_credit_card) + return false; - bool is_line1_or_house_number_violated = false; - if (country.requires_line1_or_house_number() && - !profile.HasRawInfo(ADDRESS_HOME_LINE1) && - !profile.HasRawInfo(ADDRESS_HOME_HOUSE_NUMBER)) { - if (import_log_buffer) { - *import_log_buffer - << LogMessage::kImportAddressProfileFromFormFailed - << "Missing required ADDRESS_HOME_LINE1 or ADDRESS_HOME_HOUSE_NUMBER." - << CTag{}; - } - is_line1_or_house_number_violated = true; + if (imported_credit_card->virtual_card_enrollment_state() != + CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE) { + return false; } - // Collect metrics regarding the requirements. - if (collect_metrics) { - AutofillMetrics::LogAddressFormImportRequirementMetric( - is_line1_missing - ? AddressImportRequirement::LINE1_REQUIREMENT_VIOLATED - : AddressImportRequirement::LINE1_REQUIREMENT_FULFILLED); - - AutofillMetrics::LogAddressFormImportRequirementMetric( - is_city_missing ? AddressImportRequirement::CITY_REQUIREMENT_VIOLATED - : AddressImportRequirement::CITY_REQUIREMENT_FULFILLED); - - AutofillMetrics::LogAddressFormImportRequirementMetric( - is_state_missing - ? AddressImportRequirement::STATE_REQUIREMENT_VIOLATED - : AddressImportRequirement::STATE_REQUIREMENT_FULFILLED); - - AutofillMetrics::LogAddressFormImportRequirementMetric( - is_zip_missing ? AddressImportRequirement::ZIP_REQUIREMENT_VIOLATED - : AddressImportRequirement::ZIP_REQUIREMENT_FULFILLED); - - AutofillMetrics::LogAddressFormImportRequirementMetric( - is_zip_or_state_requirement_violated - ? AddressImportRequirement::ZIP_OR_STATE_REQUIREMENT_VIOLATED - : AddressImportRequirement::ZIP_OR_STATE_REQUIREMENT_FULFILLED); - - AutofillMetrics::LogAddressFormImportCountrySpecificFieldRequirementsMetric( - is_zip_missing, is_state_missing, is_city_missing, is_line1_missing); + if (!fetched_card_instrument_id.has_value() || + imported_credit_card->instrument_id() != + fetched_card_instrument_id.value()) { + return false; } - // Return true if all requirements are fulfilled. - return !(is_line1_missing || is_city_missing || is_state_missing || - is_zip_missing || is_zip_or_state_requirement_violated || - is_line1_or_house_number_violated); + return true; } } // namespace @@ -250,7 +156,9 @@ FormDataImporter::FormDataImporter(AutofillClient* client, virtual_card_enrollment_manager_( std::make_unique<VirtualCardEnrollmentManager>(personal_data_manager, payments_client, - client)) { + client)), + multistep_importer_(app_locale, + client_->GetVariationConfigCountryCode()) { if (personal_data_manager_) personal_data_manager_->AddObserver(this); } @@ -291,6 +199,7 @@ void FormDataImporter::ImportFormData(const FormStructure& submitted_form, bool cc_prompt_potentially_shown = ProcessCreditCardImportCandidate( submitted_form, std::move(imported_credit_card), detected_upi_id, credit_card_autofill_enabled, is_credit_card_upstream_enabled); + fetched_card_instrument_id_.reset(); // If a prompt for credit cards is potentially shown, do not allow for a // second address profile import dialog. @@ -305,44 +214,6 @@ CreditCard FormDataImporter::ExtractCreditCardFromForm( } // static -std::string FormDataImporter::GetPredictedCountryCode( - const AutofillProfile& profile, - const std::string& variation_country_code, - const std::string& app_locale, - LogBuffer* import_log_buffer) { - // Try to acquire the country code form the filled form. - std::string country_code = - base::UTF16ToASCII(profile.GetRawInfo(ADDRESS_HOME_COUNTRY)); - - if (import_log_buffer && !country_code.empty()) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormCountrySource - << "Country entry in form." << CTag{}; - } - - // As a fallback, use the variation service state to get a country code. - if (country_code.empty() && !variation_country_code.empty()) { - country_code = variation_country_code; - if (import_log_buffer) { - *import_log_buffer - << LogMessage::kImportAddressProfileFromFormCountrySource - << "Variations service." << CTag{}; - } - } - - // As the last resort, derive the country code from the app_locale. - if (country_code.empty()) { - country_code = AutofillCountry::CountryCodeForLocale(app_locale); - if (import_log_buffer && !country_code.empty()) { - *import_log_buffer - << LogMessage::kImportAddressProfileFromFormCountrySource - << "App locale." << CTag{}; - } - } - - return country_code; -} - -// static bool FormDataImporter::IsValidLearnableProfile( const AutofillProfile& profile, const std::string& predicted_country_code, @@ -352,32 +223,28 @@ bool FormDataImporter::IsValidLearnableProfile( bool is_email_invalid = false; std::u16string email = profile.GetRawInfo(EMAIL_ADDRESS); if (!email.empty() && !IsValidEmailAddress(email)) { - if (import_log_buffer) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Invalid email address." << CTag{}; - } + LOG_AF(import_log_buffer) << LogMessage::kImportAddressProfileFromFormFailed + << "Invalid email address." << CTag{}; is_email_invalid = true; } // Reject profiles with an invalid |HOME_ADDRESS_STATE| entry. bool is_state_invalid = false; if (profile.IsPresentButInvalid(ADDRESS_HOME_STATE)) { - if (import_log_buffer) - *import_log_buffer - << LogMessage::kImportAddressProfileFromFormFailed - << "Invalid state as of AutofillProfile::IsPresentButInvalid()." - << CTag{}; + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Invalid state as of AutofillProfile::IsPresentButInvalid()." + << CTag{}; is_state_invalid = true; } // Reject profiles with an invalid |HOME_ADDRESS_ZIP| entry. bool is_zip_invalid = false; if (profile.IsPresentButInvalid(ADDRESS_HOME_ZIP)) { - if (import_log_buffer) - *import_log_buffer - << LogMessage::kImportAddressProfileFromFormFailed - << "Invalid ZIP as of AutofillProfile::IsPresentButInvalid()." - << CTag{}; + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Invalid ZIP as of AutofillProfile::IsPresentButInvalid()." + << CTag{}; is_zip_invalid = true; } @@ -484,6 +351,10 @@ void FormDataImporter::CacheFetchedVirtualCard( fetched_virtual_cards_.insert(last_four); } +void FormDataImporter::SetFetchedCardInstrumentId(int64_t instrument_id) { + fetched_card_instrument_id_ = instrument_id; +} + bool FormDataImporter::ImportFormData( const FormStructure& submitted_form, bool profile_autofill_enabled, @@ -528,11 +399,12 @@ bool FormDataImporter::ImportAddressProfiles( const FormStructure& form, std::vector<AddressProfileImportCandidate>& import_candidates) { // Create a buffer to collect logging output for the autofill-internals. - LogBuffer import_log_buffer; - import_log_buffer << LoggingScope::kAddressProfileFormImport; + LogManager* log_manager = client_->GetLogManager(); + LogBuffer import_log_buffer(IsLoggingActive(log_manager)); + LOG_AF(import_log_buffer) << LoggingScope::kAddressProfileFormImport; // Print the full form into the logging scope. - import_log_buffer << LogMessage::kImportAddressProfileFromForm << form - << CTag{}; + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromForm << form << CTag{}; // We save a maximum of 2 profiles per submitted form (e.g. for shipping and // billing). @@ -540,31 +412,32 @@ bool FormDataImporter::ImportAddressProfiles( size_t num_complete_profiles = 0; if (!form.field_count()) { - import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Form is empty." << CTag{}; + LOG_AF(import_log_buffer) << LogMessage::kImportAddressProfileFromFormFailed + << "Form is empty." << CTag{}; } else { // Relevant sections for address fields. - std::set<std::string> sections; + std::set<Section> sections; for (const auto& field : form) { if (field->Type().group() != FieldTypeGroup::kCreditCard) sections.insert(field->section); } - for (const std::string& section : sections) { + for (const Section& section : sections) { if (num_complete_profiles == kMaxNumAddressProfilesSaved) break; // Log the output from a section in a separate div for readability. - import_log_buffer << Tag{"div"} - << Attrib{"class", "profile_import_from_form_section"}; - import_log_buffer << LogMessage::kImportAddressProfileFromFormSection - << section << CTag{}; + LOG_AF(import_log_buffer) + << Tag{"div"} << Attrib{"class", "profile_import_from_form_section"}; + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormSection << section + << CTag{}; // Try to import an address profile from the form fields of this section. // Only allow for a prompt if no other complete profile was found so far. if (ImportAddressProfileForSection(form, section, import_candidates, &import_log_buffer)) num_complete_profiles++; // And close the div of the section import log. - import_log_buffer << CTag{"div"}; + LOG_AF(import_log_buffer) << CTag{"div"}; } // Run the import on the union of the section if the import was not // successful and if there is more than one section. @@ -573,7 +446,7 @@ bool FormDataImporter::ImportAddressProfiles( AutofillMetrics::AddressProfileImportStatusMetric::REGULAR_IMPORT); } else if (sections.size() > 1) { // Try to import by combining all sections. - if (ImportAddressProfileForSection(form, "", import_candidates, + if (ImportAddressProfileForSection(form, absl::nullopt, import_candidates, &import_log_buffer)) { num_complete_profiles++; AutofillMetrics::LogAddressFormImportStatusMetric( @@ -586,20 +459,19 @@ bool FormDataImporter::ImportAddressProfiles( AutofillMetrics::AddressProfileImportStatusMetric::NO_IMPORT); } } - import_log_buffer << LogMessage::kImportAddressProfileFromFormNumberOfImports - << num_complete_profiles << CTag{}; + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormNumberOfImports + << num_complete_profiles << CTag{}; // Write log buffer to autofill-internals. - LogManager* log_manager = client_->GetLogManager(); - if (log_manager) - log_manager->Log() << std::move(import_log_buffer); + LOG_AF(log_manager) << std::move(import_log_buffer); return num_complete_profiles > 0; } bool FormDataImporter::ImportAddressProfileForSection( const FormStructure& form, - const std::string& section, + const absl::optional<Section>& section, std::vector<AddressProfileImportCandidate>& import_candidates, LogBuffer* import_log_buffer) { // The candidate for profile import. There are many ways for the candidate to @@ -639,8 +511,8 @@ bool FormDataImporter::ImportAddressProfileForSection( // Go through each |form| field and attempt to constitute a valid profile. for (const auto& field : form) { // Reject fields that are not within the specified |section|. - // If section is empty, use all fields. - if (field->section != section && !section.empty()) + // If no section is passed, use all fields. + if (section && field->section != *section) continue; std::u16string value; @@ -673,11 +545,9 @@ bool FormDataImporter::ImportAddressProfileForSection( if (server_field_type == EMAIL_ADDRESS && types_seen.count(server_field_type) && candidate_profile.GetRawInfo(EMAIL_ADDRESS) != value) { - if (import_log_buffer) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Multiple different email addresses present." - << CTag{}; - } + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Multiple different email addresses present." << CTag{}; has_multiple_distinct_email_addresses = true; } @@ -737,10 +607,9 @@ bool FormDataImporter::ImportAddressProfileForSection( } // Check if the country code was still not determined correctly. if (!candidate_profile.HasRawInfo(ADDRESS_HOME_COUNTRY)) { - if (import_log_buffer) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Missing country." << CTag{}; - } + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Missing country." << CTag{}; has_invalid_country = true; } } @@ -760,10 +629,9 @@ bool FormDataImporter::ImportAddressProfileForSection( import_metadata.did_remove_invalid_phone_number = true; } else { has_invalid_phone_number = true; - if (import_log_buffer) { - *import_log_buffer << LogMessage::kImportAddressProfileFromFormFailed - << "Invalid phone number." << CTag{}; - } + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Invalid phone number." << CTag{}; } } @@ -784,8 +652,9 @@ bool FormDataImporter::ImportAddressProfileForSection( // This requires the profile to be finalized to apply the merging logic. if (finalized_import && has_address_related_fields && !has_invalid_information) { - ProcessMultiStepImport(candidate_profile, import_metadata, - url::Origin::Create(form.source_url())); + multistep_importer_.ProcessMultiStepImport( + candidate_profile, import_metadata, + url::Origin::Create(form.source_url())); // The predicted country code has possibly changed, if |candidate_profile| // was merged with a profile containing country information. predicted_country_code = @@ -925,15 +794,11 @@ bool FormDataImporter::ProcessCreditCardImportCandidate( if (client_->IsAutofillAssistantShowing()) return false; - if (base::FeatureList::IsEnabled( - features::kAutofillEnableUpdateVirtualCardEnrollment)) { - if (imported_credit_card && - imported_credit_card->virtual_card_enrollment_state() == - CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE) { - virtual_card_enrollment_manager_->InitVirtualCardEnroll( - *imported_credit_card, VirtualCardEnrollmentSource::kDownstream); - return true; - } + if (ShouldOfferVirtualCardEnrollment(imported_credit_card.get(), + fetched_card_instrument_id_)) { + virtual_card_enrollment_manager_->InitVirtualCardEnroll( + *imported_credit_card, VirtualCardEnrollmentSource::kDownstream); + return true; } #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) @@ -1217,114 +1082,16 @@ bool FormDataImporter::ShouldOfferUploadCardOrLocalCardSave( return true; } -void FormDataImporter::ProcessMultiStepImport( - AutofillProfile& profile, - ProfileImportMetadata& import_metadata, - const url::Origin& origin) { - if (!base::FeatureList::IsEnabled( - features::kAutofillEnableMultiStepImports)) { - return; - } - - RemoveOutdatedMultiStepCandidates(origin); - bool has_min_address_requirements = - MergeProfileWithMultiStepCandidates(profile, import_metadata, origin); - - if (!has_min_address_requirements || - features::kAutofillEnableMultiStepImportComplements.Get()) { - // Add |profile| as a |multistep_candidate|. This happens for incomplete - // profiles, which can then be complemented in later steps. When - // |kAutofillEnableMultiStepImportComplements| is enabled, complete profiles - // are stored too, which enables updating them in later steps. - // In the latter case, Autofill tries to import the `profile`. This logs - // metrics depending on `import_metadata`. To prevent double counting, - // an we store an empty `ProfileImportMetadata` object in this case. - multistep_candidates_.push_front(MultiStepFormProfileCandidate{ - .profile = profile, - .import_metadata = has_min_address_requirements - ? ProfileImportMetadata() - : import_metadata, - .timestamp = AutofillClock::Now()}); - multistep_candidates_origin_ = origin; - } -} - -void FormDataImporter::RemoveOutdatedMultiStepCandidates( - const url::Origin& origin) { - // All |multistep_candidates| share |multistep_candidates_origin|. - if (multistep_candidates_origin_.has_value() && - multistep_candidates_origin_.value() != origin) { - multistep_candidates_.clear(); - } else { - // Remove candidates that reached their TTL. - const base::TimeDelta ttl = - features::kAutofillMultiStepImportCandidateTTL.Get(); - const base::Time now = AutofillClock::Now(); - while (!multistep_candidates_.empty() && - now - multistep_candidates_.back().timestamp > ttl) { - multistep_candidates_.pop_back(); - } - } - if (multistep_candidates_.empty()) { - multistep_candidates_origin_.reset(); - } -} - -bool FormDataImporter::MergeProfileWithMultiStepCandidates( - AutofillProfile& profile, - ProfileImportMetadata& import_metadata, - const url::Origin& origin) { - // Greedily merge with a prefix of |multistep_candidates|. - AutofillProfileComparator comparator(app_locale_); - std::deque<MultiStepFormProfileCandidate>::iterator merge_candidate = - multistep_candidates_.begin(); - AutofillProfile completed_profile = profile; - ProfileImportMetadata completed_metadata = import_metadata; - // Country completion has not happened yet, so this field can be ignored. - DCHECK(!completed_metadata.did_remove_invalid_phone_number); - while ( - merge_candidate != multistep_candidates_.end() && - comparator.AreMergeable(completed_profile, merge_candidate->profile) && - completed_profile.MergeDataFrom(merge_candidate->profile, app_locale_)) { - // ProfileImportMetadata is only relevant for metrics. If the phone number - // was removed from a partial profile, we still want that removal to appear - // in the metrics, because it would have hindered that partial profile from - // import and merging. - completed_metadata.did_remove_invalid_phone_number |= - merge_candidate->import_metadata.did_remove_invalid_phone_number; - merge_candidate++; - } - - // The minimum address requirements depend on the country, which has possibly - // changed as a result of the merge. - if (IsMinimumAddress( - completed_profile, - GetPredictedCountryCode(completed_profile, - client_->GetVariationConfigCountryCode(), - app_locale_, /*import_log_buffer=*/nullptr), - app_locale_, - /*import_log_buffer=*/nullptr, /*collect_metrics=*/false)) { - profile = std::move(completed_profile); - import_metadata = std::move(completed_metadata); - multistep_candidates_.clear(); - return true; - } else { - // Remove all profiles that couldn't be merged. - multistep_candidates_.erase(merge_candidate, multistep_candidates_.end()); - return false; - } -} - void FormDataImporter::OnBrowsingHistoryCleared( const history::DeletionInfo& deletion_info) { // Delete all multi-step import candidates when: // - The entire browsing history is cleared, or - // - At least one URL from the same origin as `multistep_candidates_origin_` + // - At least one URL from the same origin as `multistep_importer_` // is deleted. if (deletion_info.IsAllHistory() || - (multistep_candidates_origin_.has_value() && + (multistep_importer_.Origin() && base::Contains(deletion_info.deleted_rows(), - *multistep_candidates_origin_, + *multistep_importer_.Origin(), [](const history::URLRow& url_row) { return url::Origin::Create(url_row.url()); }))) { diff --git a/chromium/components/autofill/core/browser/form_data_importer.h b/chromium/components/autofill/core/browser/form_data_importer.h index 70abf5e3f42..5a27bb327e2 100644 --- a/chromium/components/autofill/core/browser/form_data_importer.h +++ b/chromium/components/autofill/core/browser/form_data_importer.h @@ -5,7 +5,6 @@ #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_DATA_IMPORTER_H_ #define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_DATA_IMPORTER_H_ -#include <deque> #include <map> #include <memory> #include <string> @@ -13,10 +12,10 @@ #include "base/gtest_prod_util.h" #include "base/memory/raw_ptr.h" -#include "base/time/time.h" #include "build/build_config.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_profile_import_process.h" +#include "components/autofill/core/browser/form_data_importer_utils.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/payments/credit_card_save_manager.h" #include "components/autofill/core/browser/payments/local_card_migration_manager.h" @@ -73,19 +72,6 @@ class FormDataImporter : public PersonalDataManagerObserver { // duplicated field types in the form. CreditCard ExtractCreditCardFromForm(const FormStructure& form); - // Tries to infer the country |profile| is from, which can be useful to - // verify whether the data is sensible. Returns a two-letter ISO country code - // by considering, in decreasing order of priority: - // - The country specified in |profile| - // - The country determined by the variation service stored in - // |variation_country_code| - // - The country code corresponding to |app_locale| - static std::string GetPredictedCountryCode( - const AutofillProfile& profile, - const std::string& variation_country_code, - const std::string& app_locale, - LogBuffer* import_log_buffer); - // Checks suitability of |profile| for adding to the user's set of profiles. static bool IsValidLearnableProfile(const AutofillProfile& profile, const std::string& predicted_country_code, @@ -106,10 +92,10 @@ class FormDataImporter : public PersonalDataManagerObserver { return virtual_card_enrollment_manager_.get(); } - void ClearMultiStepImportCandidates() { - multistep_candidates_.clear(); - multistep_candidates_origin_.reset(); - } + void ClearMultiStepImportCandidates() { multistep_importer_.Clear(); } + + // See comment for |fetched_card_instrument_id_|. + void SetFetchedCardInstrumentId(int64_t instrument_id); // PersonalDataManagerObserver void OnBrowsingHistoryCleared( @@ -130,6 +116,12 @@ class FormDataImporter : public PersonalDataManagerObserver { } #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) + // The instrument id of the card that has been most recently retrieved via + // Autofill Downstream (card retrieval from server). This can be used to + // decide whether the card submitted is the same card retrieved. This field is + // optional and is set when an Autofill Downstream has happened. + absl::optional<int64_t> fetched_card_instrument_id_; + private: // Defines a candidate for address profile import. struct AddressProfileImportCandidate { @@ -170,11 +162,11 @@ class FormDataImporter : public PersonalDataManagerObserver { std::vector<AddressProfileImportCandidate>& import_candidates); // Helper method for ImportAddressProfiles which only considers the fields for - // a specified |section|. If |section| is the empty string, the import is - // performed on the union of all sections. + // a specified |section|. If no section is passed, the import is performed on + // the union of all sections. bool ImportAddressProfileForSection( const FormStructure& form, - const std::string& section, + const absl::optional<Section>& section, std::vector<AddressProfileImportCandidate>& import_candidates, LogBuffer* import_log_buffer); @@ -262,33 +254,6 @@ class FormDataImporter : public PersonalDataManagerObserver { AutofillProfile& profile, const std::string& predicted_country_code); - // Removes updated multi-step candidates, merges |profile| with multi-step - // candidates and potentially stores it as a multi-step candidate itself. - // |profile| and |import_metadata| are updated accordingly, if the profile can - // be merged. See |MergeProfileWithMultiStepCandidates()| for details. - // Only applicable when |kAutofillEnableMultiStepImports| is enabled. - void ProcessMultiStepImport(AutofillProfile& profile, - ProfileImportMetadata& import_metadata, - const url::Origin& origin); - - // Removes any MultiStepFormProfileCandidate from |multistep_candidates_| that - // reached their TTL or have a different |origin|. - void RemoveOutdatedMultiStepCandidates(const url::Origin& origin); - - // Merges a given |profile| stepwise with |multistep_candidates_| to - // complete it. |profile| is assumed to contain no invalid information. - // Returns true if the resulting profile satisfies the minimum address - // requirements. |profile| and |import_metadata| are updated in this case with - // the result of merging all relevant candidates. - // Returns false otherwise and leaves |profile| and |import_metadata| - // unchanged. - // Any merged or colliding |multistep_candidates_| are cleared. - // |origin|: The origin of the form where |profile| was imported from. - bool MergeProfileWithMultiStepCandidates( - AutofillProfile& profile, - ProfileImportMetadata& import_metadata, - const url::Origin& origin); - // Whether a dynamic change form is imported. bool from_dynamic_change_form_ = false; @@ -333,22 +298,8 @@ class FormDataImporter : public PersonalDataManagerObserver { std::unique_ptr<VirtualCardEnrollmentManager> virtual_card_enrollment_manager_; - // Represents a submitted form, stored to be considered as a merge candidate - // for other candidate profiles in future submits in a multi-step import flow. - struct MultiStepFormProfileCandidate { - // The import candidate. - AutofillProfile profile; - // Metadata about how |profile| was constructed. - ProfileImportMetadata import_metadata; - // Timestamp when the submit happened. - base::Time timestamp; - }; - // Current multi-step import candidates, in increasing order of their - // |timestamp|. - std::deque<MultiStepFormProfileCandidate> multistep_candidates_; - // All |multistep_candidates_| share the same origin. Has a value iff - // |multistep_candidates_| is not empty. - absl::optional<url::Origin> multistep_candidates_origin_; + // Enables importing from multi-step import flows. + MultiStepImportMerger multistep_importer_; friend class AutofillMergeTest; friend class FormDataImporterTest; @@ -360,6 +311,9 @@ class FormDataImporter : public PersonalDataManagerObserver { FRIEND_TEST_ALL_PREFIXES(AutofillMergeTest, MergeProfiles); FRIEND_TEST_ALL_PREFIXES(FormDataImporterNonParameterizedTest, ProcessCreditCardImportCandidate_EmptyCreditCard); + FRIEND_TEST_ALL_PREFIXES( + FormDataImporterNonParameterizedTest, + ProcessCreditCardImportCandidate_VirtualCardEligible); FRIEND_TEST_ALL_PREFIXES(FormDataImporterNonParameterizedTest, ShouldOfferUploadCardOrLocalCardSave); FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, 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 feeaed88ff5..1f3bceaa28a 100644 --- a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc +++ b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc @@ -15,7 +15,6 @@ #include <vector> #include "base/callback_helpers.h" -#include "base/command_line.h" #include "base/feature_list.h" #include "base/guid.h" #include "base/memory/raw_ptr.h" @@ -34,6 +33,7 @@ #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" +#include "components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.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" @@ -44,12 +44,11 @@ #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_prefs.h" -#include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" -#include "components/os_crypt/os_crypt_mocker.h" #include "components/prefs/pref_service.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" #include "components/sync/driver/test_sync_service.h" #include "components/webdata/common/web_data_service_base.h" #include "components/webdata/common/web_database_service.h" @@ -57,6 +56,7 @@ #include "testing/gtest/include/gtest/gtest.h" using base::UTF8ToUTF16; +using testing::_; namespace autofill { namespace { @@ -72,6 +72,7 @@ constexpr char kDefaultZip[] = "94102"; constexpr char kDefaultCity[] = "Los Angeles"; constexpr char kDefaultDependentLocality[] = "Nob Hill"; constexpr char kDefaultState[] = "California"; +constexpr char kDefaultCountry[] = "US"; constexpr char kDefaultPhone[] = "+1 650-555-0000"; constexpr char kDefaultPhoneAlternativeFormatting[] = "650-555-0000"; constexpr char kDefaultPhoneDomesticFormatting[] = "(650) 555-0000"; @@ -104,7 +105,10 @@ constexpr char kThirdDependentLocality[] = "Down Town"; constexpr char kThirdState[] = "Oregon"; constexpr char kThirdPhone[] = "+1 851-777-2222"; +constexpr char kDefaultCreditCardName[] = "Biggie Smalls"; constexpr char kDefaultCreditCardNumber[] = "4111 1111 1111 1111"; +constexpr char kDefaultCreditCardExpMonth[] = "01"; +constexpr char kDefaultCreditCardExpYear[] = "2999"; // For a given ServerFieldType |type| returns a pair of field name and label // that should be parsed into this type by our field type parsers. @@ -125,7 +129,10 @@ std::pair<std::string, std::string> GetLabelAndNameForType( {ADDRESS_HOME_DEPENDENT_LOCALITY, {"Neighborhood:", "neighborhood"}}, {ADDRESS_HOME_COUNTRY, {"Country:", "country"}}, {PHONE_HOME_WHOLE_NUMBER, {"Phone:", "phone"}}, + {CREDIT_CARD_NAME_FULL, {"Name on card:", "name_on_card"}}, {CREDIT_CARD_NUMBER, {"Credit Card Number:", "card_number"}}, + {CREDIT_CARD_EXP_MONTH, {"Exp Month:", "exp_month"}}, + {CREDIT_CARD_EXP_4_DIGIT_YEAR, {"Exp Year:", "exp_year"}}, }; auto it = name_type_map.find(type); if (it == name_type_map.end()) { @@ -213,9 +220,25 @@ GetDefaultProfileTypeValuePairs() { {ADDRESS_HOME_CITY, kDefaultCity}, {ADDRESS_HOME_STATE, kDefaultState}, {ADDRESS_HOME_ZIP, kDefaultZip}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}, }; } +// Wraps `GetDefaultProfileTypeValuePairs()` but replaces `kDefaultCountry` with +// `country`. If `country` is empty, ADDRESS_HOME_COUNTRY is removed entirely. +std::vector<std::pair<ServerFieldType, std::string>> +GetDefaultProfileTypeValuePairsWithOverriddenCountry( + const std::string& country) { + constexpr int kCountryIndex = 9; + auto pairs = GetDefaultProfileTypeValuePairs(); + DCHECK_EQ(pairs[kCountryIndex].first, ADDRESS_HOME_COUNTRY); + if (country.empty()) + pairs.erase(pairs.begin() + kCountryIndex); + else + pairs[kCountryIndex].second = country; + return pairs; +} + // Same as |GetDefaultProfileTypeValuePairs()|, but split into two parts to test // multi-step imports. No part by itself satisfies the import requirements. // |part| specifies the requested half and can be either 1 or 2. @@ -224,9 +247,12 @@ GetSplitDefaultProfileTypeValuePairs(int part) { DCHECK(part == 1 || part == 2); if (part == 1) { return { - {NAME_FIRST, kDefaultFirstName}, {NAME_LAST, kDefaultLastName}, - {EMAIL_ADDRESS, kDefaultMail}, {ADDRESS_HOME_CITY, kDefaultCity}, + {NAME_FIRST, kDefaultFirstName}, + {NAME_LAST, kDefaultLastName}, + {EMAIL_ADDRESS, kDefaultMail}, + {ADDRESS_HOME_CITY, kDefaultCity}, {ADDRESS_HOME_STATE, kDefaultState}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}, }; } else { return { @@ -238,15 +264,6 @@ GetSplitDefaultProfileTypeValuePairs(int part) { } } -// Same as |GetDefaultProfileTypeValuePairs()| but with ADDRESS_HOME_COUNTRY -// set to |country|. -std::vector<std::pair<ServerFieldType, std::string>> -GetDefaultProfileTypeValuePairsWithCountry(const std::string& country) { - auto profile_typed_value_pairs = GetDefaultProfileTypeValuePairs(); - profile_typed_value_pairs.emplace_back(ADDRESS_HOME_COUNTRY, country); - return profile_typed_value_pairs; -} - // Same as |GetDefaultProfileTypeValuePairs()| but with the second profile // information. std::vector<std::pair<ServerFieldType, std::string>> @@ -261,6 +278,7 @@ GetSecondProfileTypeValuePairs() { {ADDRESS_HOME_CITY, kSecondCity}, {ADDRESS_HOME_STATE, kSecondState}, {ADDRESS_HOME_ZIP, kSecondZip}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}, }; } @@ -278,6 +296,18 @@ GetThirdProfileTypeValuePairs() { {ADDRESS_HOME_CITY, kThirdCity}, {ADDRESS_HOME_STATE, kThirdState}, {ADDRESS_HOME_ZIP, kThirdZip}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}, + }; +} + +// Same as `GetDefaultProfileTypeValuePairs()`, but for credit cards. +std::vector<std::pair<ServerFieldType, std::string>> +GetDefaultCreditCardTypeValuePairs() { + return { + {CREDIT_CARD_NAME_FULL, kDefaultCreditCardName}, + {CREDIT_CARD_NUMBER, kDefaultCreditCardNumber}, + {CREDIT_CARD_EXP_MONTH, kDefaultCreditCardExpMonth}, + {CREDIT_CARD_EXP_4_DIGIT_YEAR, kDefaultCreditCardExpYear}, }; } @@ -286,10 +316,12 @@ AutofillProfile ConstructDefaultProfile() { return ConstructProfileFromTypeValuePairs(GetDefaultProfileTypeValuePairs()); } -// Same as |ConstructDefaultProfile()| but with |country|. -AutofillProfile ConstructDefaultProfileWithCountry(const std::string& country) { +// Wraps `ConstructDefaultProfile()`, but overrides ADDRESS_HOME_COUNTRY with +// `country`. +AutofillProfile ConstructDefaultProfileWithOverriddenCountry( + const std::string& country) { return ConstructProfileFromTypeValuePairs( - GetDefaultProfileTypeValuePairsWithCountry(country)); + GetDefaultProfileTypeValuePairsWithOverriddenCountry(country)); } // Returns the second AutofillProfile used in this test file. @@ -318,13 +350,6 @@ std::unique_ptr<FormStructure> ConstructSplitDefaultProfileFormStructure( GetSplitDefaultProfileTypeValuePairs(part)); } -// Same as |ConstructDefaultFormStructure()| but with |country|. -std::unique_ptr<FormStructure> ConstructDefaultProfileFormStructureWithCountry( - const std::string& country) { - return ConstructFormStructureFromTypeValuePairs( - GetDefaultProfileTypeValuePairsWithCountry(country)); -} - // Same as |ConstructDefaultFormStructure()| but for the second profile. std::unique_ptr<FormStructure> ConstructSecondProfileFormStructure() { return ConstructFormStructureFromTypeValuePairs( @@ -337,6 +362,12 @@ std::unique_ptr<FormStructure> ConstructThirdProfileFormStructure() { GetThirdProfileTypeValuePairs()); } +// Same as `ConstructDefaultFormStructure()` but for credit cards. +std::unique_ptr<FormStructure> ConstructDefaultCreditCardFormStructure() { + return ConstructFormStructureFromTypeValuePairs( + GetDefaultCreditCardTypeValuePairs()); +} + // Constructs a |FormData| instance that carries the information of the default // profile. FormData ConstructDefaultFormData() { @@ -394,6 +425,32 @@ auto UnorderedElementsCompareEqual(Matchers... matchers) { } // anonymous namespace +class MockVirtualCardEnrollmentManager + : public TestVirtualCardEnrollmentManager { + public: + MockVirtualCardEnrollmentManager( + TestPersonalDataManager* personal_data_manager, + payments::TestPaymentsClient* payments_client, + TestAutofillClient* autofill_client) + : TestVirtualCardEnrollmentManager(personal_data_manager, + payments_client, + autofill_client){}; + MOCK_METHOD( + void, + InitVirtualCardEnroll, + (const CreditCard& credit_card, + VirtualCardEnrollmentSource virtual_card_enrollment_source, + absl::optional< + payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails> + get_details_for_enrollment_response_details, + const raw_ptr<PrefService> user_prefs, + VirtualCardEnrollmentManager::RiskAssessmentFunction + risk_assessment_function, + VirtualCardEnrollmentManager::VirtualCardEnrollmentFieldsLoadedCallback + virtual_card_enrollment_fields_loaded_callback), + (override)); +}; + class FormDataImporterTestBase { protected: FormDataImporterTestBase() : autofill_table_(nullptr) {} @@ -404,6 +461,9 @@ class FormDataImporterTestBase { // reference to `personal_data_manager_` that otherwise points to garbage. form_data_importer_.reset(); + if (personal_data_manager_) { + personal_data_manager_->Shutdown(); + } personal_data_manager_ = std::make_unique<PersonalDataManager>("en", "US"); personal_data_manager_->set_auto_accept_address_imports_for_testing(true); personal_data_manager_->Init( @@ -411,7 +471,7 @@ class FormDataImporterTestBase { /*account_database=*/nullptr, /*pref_service=*/prefs_.get(), /*local_state=*/prefs_.get(), - /*identity_manager=*/nullptr, + /*identity_manager=*/identity_test_env_.identity_manager(), /*history_service=*/nullptr, /*strike_database=*/nullptr, /*image_fetcher=*/nullptr, @@ -427,6 +487,12 @@ class FormDataImporterTestBase { std::make_unique<FormDataImporter>(autofill_client_.get(), /*payments::PaymentsClient=*/nullptr, personal_data_manager_.get(), "en"); + auto virtual_card_enrollment_manager = + std::make_unique<MockVirtualCardEnrollmentManager>( + nullptr, nullptr, autofill_client_.get()); + virtual_card_enrollment_manager_ = virtual_card_enrollment_manager.get(); + form_data_importer_->virtual_card_enrollment_manager_ = + std::move(virtual_card_enrollment_manager); } void SetUpHelper() { @@ -456,6 +522,12 @@ class FormDataImporterTestBase { prefs::kAutofillLastVersionDeduped, 0); } + void TearDownHelper() { + if (personal_data_manager_) { + personal_data_manager_->Shutdown(); + } + } + // Helper method that will add credit card fields in |form|, according to the // specified values. If a value is nullptr, the corresponding field won't get // added (empty string will add a field with an empty string as the value). @@ -621,6 +693,7 @@ class FormDataImporterTestBase { base::test::SingleThreadTaskEnvironment task_environment_{ base::test::SingleThreadTaskEnvironment::MainThreadType::UI}; std::unique_ptr<PrefService> prefs_; + signin::IdentityTestEnvironment identity_test_env_; scoped_refptr<AutofillWebDataService> autofill_database_service_; scoped_refptr<WebDatabaseService> web_database_; raw_ptr<AutofillTable> autofill_table_; // weak ref @@ -628,6 +701,7 @@ class FormDataImporterTestBase { std::unique_ptr<TestAutofillClient> autofill_client_; std::unique_ptr<PersonalDataManager> personal_data_manager_; std::unique_ptr<FormDataImporter> form_data_importer_; + MockVirtualCardEnrollmentManager* virtual_card_enrollment_manager_; base::test::ScopedFeatureList scoped_feature_list_; }; @@ -653,6 +727,8 @@ class FormDataImporterTest SetUpHelper(); } + void TearDown() override { TearDownHelper(); } + void InitializeFeatures() { support_for_apartment_numbers_ = std::get<0>(GetParam()); support_for_depending_locality_ = std::get<1>(GetParam()); @@ -685,56 +761,38 @@ class FormDataImporterTest bool consider_variation_country_code_for_phone_numbers_; }; -TEST_P(FormDataImporterTest, GetPredictedCountryCode) { - const AutofillProfile us_profile = - ConstructProfileFromTypeValuePairs({{ADDRESS_HOME_COUNTRY, "US"}}); - const AutofillProfile empty_profile; - // Test prioritization: profile > variation service state > app locale - EXPECT_EQ(FormDataImporter::GetPredictedCountryCode(us_profile, "DE", "de-AT", - nullptr), - "US"); - EXPECT_EQ(FormDataImporter::GetPredictedCountryCode(us_profile, "", "de-AT", - nullptr), - "US"); - EXPECT_EQ(FormDataImporter::GetPredictedCountryCode(empty_profile, "DE", - "de-AT", nullptr), - "DE"); - EXPECT_EQ(FormDataImporter::GetPredictedCountryCode(empty_profile, "", - "de-AT", nullptr), - "AT"); -} - TEST_P(FormDataImporterTest, ComplementCountry) { base::test::ScopedFeatureList complement_country_feature; complement_country_feature.InitAndEnableFeature( features::kAutofillComplementCountryCodeOnImport); - auto import_with_country = + auto ImportWithCountry = [this](const std::string& form_country, const std::vector<AutofillProfile>& expected_profiles) { // Remove existing profiles, to prevent an update instead of an import. personal_data_manager_->ClearAllLocalData(); std::unique_ptr<FormStructure> form_structure = - form_country.empty() - ? ConstructDefaultProfileFormStructure() - : ConstructDefaultProfileFormStructureWithCountry(form_country); + ConstructFormStructureFromTypeValuePairs( + GetDefaultProfileTypeValuePairsWithOverriddenCountry( + form_country)); ImportAddressProfilesAndVerifyExpectation(*form_structure, expected_profiles); }; // Country part of the form: // If a valid country was entered, use that. - import_with_country("Germany", {ConstructDefaultProfileWithCountry("DE")}); + ImportWithCountry("Germany", + {ConstructDefaultProfileWithOverriddenCountry("DE")}); // Reject the profile if an invalid country was entered. - import_with_country("Somewhere", {}); + ImportWithCountry("Somewhere", {}); // Country not part of the form: Complement using // FormDataImporter::GetPredictedCountryCode // If no variation config country code is available, default to locale (US) - import_with_country("", {ConstructDefaultProfileWithCountry("US")}); + ImportWithCountry("", {ConstructDefaultProfileWithOverriddenCountry("US")}); // Prefer variation config country code over locale autofill_client_->SetVariationConfigCountryCode("DE"); - import_with_country("", {ConstructDefaultProfileWithCountry("DE")}); + ImportWithCountry("", {ConstructDefaultProfileWithOverriddenCountry("DE")}); } TEST_P(FormDataImporterTest, InvalidPhoneNumber) { @@ -784,9 +842,12 @@ TEST_P(FormDataImporterTest, PhoneNumberRegionMetrics) { // Remove existing profiles, to prevent an update instead of an import. personal_data_manager_->ClearAllLocalData(); + // In order to test the phone number region deduction via the variation + // country code and app local, the form cannot contain a country field. + // Passing an empty country archives that. std::vector<std::pair<ServerFieldType, std::string>> profile_with_invalid_phone_number = - GetDefaultProfileTypeValuePairs(); + GetDefaultProfileTypeValuePairsWithOverriddenCountry(""); ASSERT_EQ(profile_with_invalid_phone_number[3].first, PHONE_HOME_WHOLE_NUMBER); profile_with_invalid_phone_number[3].second = number; @@ -1134,7 +1195,8 @@ TEST_P(FormDataImporterTest, ImportAddressProfileFromUnifiedSection) { ConstructDefaultProfileFormStructure(); // Assign the address field another section than the other fields. - form_structure->field(4)->section = "another_section"; + form_structure->field(4)->section.SetPrefixFromAutocomplete( + {.section = "another_section", .mode = HtmlFieldMode::HTML_MODE_NONE}); ImportAddressProfileAndVerifyImportOfDefaultProfile(*form_structure); } @@ -1225,7 +1287,9 @@ TEST_P(FormDataImporterTest, {{NAME_FIRST, kDefaultFirstName}, {NAME_LAST, kDefaultLastName}, {EMAIL_ADDRESS, kDefaultMail}, - // Add six phone number fields. + // Add two phone number fields, split across 3 fields each. + // They are all declared as PHONE_HOME_WHOLE_NUMBER, which only affects + // the label. Local heuristics will classify them correctly. {PHONE_HOME_WHOLE_NUMBER, kDefaultPhoneAreaCode}, {PHONE_HOME_WHOLE_NUMBER, kDefaultPhonePrefix}, {PHONE_HOME_WHOLE_NUMBER, kDefaultPhoneSuffix}, @@ -1236,7 +1300,8 @@ TEST_P(FormDataImporterTest, {ADDRESS_HOME_DEPENDENT_LOCALITY, kDefaultDependentLocality}, {ADDRESS_HOME_CITY, kDefaultCity}, {ADDRESS_HOME_STATE, kDefaultState}, - {ADDRESS_HOME_ZIP, kDefaultZip}}); + {ADDRESS_HOME_ZIP, kDefaultZip}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}}); form_data.fields[3].max_length = 3; form_data.fields[4].max_length = 3; @@ -1260,7 +1325,8 @@ TEST_P(FormDataImporterTest, {ADDRESS_HOME_LINE1, kDefaultAddressLine1}, {ADDRESS_HOME_CITY, kDefaultCity}, {ADDRESS_HOME_STATE, kDefaultState}, - {ADDRESS_HOME_ZIP, kDefaultZip}})}); + {ADDRESS_HOME_ZIP, kDefaultZip}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}})}); } // Tests that not enough filled fields will result in not importing an address. @@ -1340,7 +1406,8 @@ TEST_P(FormDataImporterTest, {ADDRESS_HOME_DEPENDENT_LOCALITY, kDefaultDependentLocality}, {ADDRESS_HOME_CITY, kDefaultCity}, {ADDRESS_HOME_STATE, kDefaultState}, - {ADDRESS_HOME_ZIP, kDefaultZip}}); + {ADDRESS_HOME_ZIP, kDefaultZip}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}}); // Define the length of the phone number fields to allow the parser to // identify them as area code, prefix and suffix. @@ -1361,7 +1428,8 @@ TEST_P(FormDataImporterTest, {ADDRESS_HOME_DEPENDENT_LOCALITY, kDefaultDependentLocality}, {ADDRESS_HOME_CITY, kDefaultCity}, {ADDRESS_HOME_STATE, kDefaultState}, - {ADDRESS_HOME_ZIP, kDefaultZip}})}); + {ADDRESS_HOME_ZIP, kDefaultZip}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}})}); } TEST_P(FormDataImporterTest, ImportAddressProfiles_UnFocussableFields) { @@ -1505,6 +1573,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_SameProfileWithConflict) { {ADDRESS_HOME_CITY, kDefaultCity}, {ADDRESS_HOME_STATE, kDefaultState}, {ADDRESS_HOME_ZIP, kDefaultZip}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}, {PHONE_HOME_WHOLE_NUMBER, kDefaultPhoneDomesticFormatting}, }; AutofillProfile initial_profile = @@ -1563,6 +1632,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_MissingInfoInOld) { {ADDRESS_HOME_CITY, kDefaultCity}, {ADDRESS_HOME_STATE, kDefaultState}, {ADDRESS_HOME_ZIP, kDefaultZip}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}, {PHONE_HOME_WHOLE_NUMBER, kDefaultPhone}, }; AutofillProfile initial_profile = @@ -1600,6 +1670,7 @@ TEST_P(FormDataImporterTest, ImportAddressProfiles_MissingInfoInNew) { {ADDRESS_HOME_CITY, kDefaultCity}, {ADDRESS_HOME_STATE, kDefaultState}, {ADDRESS_HOME_ZIP, kDefaultZip}, + {ADDRESS_HOME_COUNTRY, kDefaultCountry}, {PHONE_HOME_WHOLE_NUMBER, kDefaultPhone}, }); // Create a superset that includes a new email address. @@ -2082,18 +2153,11 @@ TEST_P(FormDataImporterTest, // Tests that a valid credit card is extracted. TEST_P(FormDataImporterTest, ImportCreditCard_Valid) { - // Add a single valid credit card form. - FormData form; - form.url = GURL("https://wwww.foo.com"); - - AddFullCreditCardForm(&form, "Biggie Smalls", "4111-1111-1111-1111", "01", - "2999"); - - FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(nullptr, nullptr); + std::unique_ptr<FormStructure> form_structure = + ConstructDefaultCreditCardFormStructure(); std::unique_ptr<CreditCard> imported_credit_card; base::HistogramTester histogram_tester; - EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); + EXPECT_TRUE(ImportCreditCard(*form_structure, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); histogram_tester.ExpectUniqueSample( "Autofill.SubmittedCardState", @@ -2213,16 +2277,10 @@ TEST_P(FormDataImporterTest, ImportCreditCard_MonthSelectInvalidText) { TEST_P(FormDataImporterTest, ImportCreditCard_TwoValidCards) { // Start with a single valid credit card form. - FormData form1; - form1.url = GURL("https://wwww.foo.com"); - - AddFullCreditCardForm(&form1, "Biggie Smalls", "4111-1111-1111-1111", "01", - "2999"); - - FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(nullptr, nullptr); + std::unique_ptr<FormStructure> form_structure1 = + ConstructDefaultCreditCardFormStructure(); std::unique_ptr<CreditCard> imported_credit_card; - EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); + EXPECT_TRUE(ImportCreditCard(*form_structure1, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card); @@ -3165,6 +3223,8 @@ TEST_P(FormDataImporterTest, ImportFormData_OneAddressOneCreditCard) { form.fields.push_back(field); test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); + test::CreateTestFormField("Country:", "country", "US", "text", &field); + form.fields.push_back(field); // Credit card section. AddFullCreditCardForm(&form, "Biggie Smalls", "4111-1111-1111-1111", "01", @@ -3189,7 +3249,7 @@ TEST_P(FormDataImporterTest, ImportFormData_OneAddressOneCreditCard) { AutofillProfile expected_address(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected_address, "George", nullptr, "Washington", "theprez@gmail.com", nullptr, "21 Laussat St", nullptr, - "San Francisco", "California", "94102", "", nullptr); + "San Francisco", "California", "94102", "US", nullptr); const std::vector<AutofillProfile*>& results_addr = personal_data_manager_->GetProfiles(); ASSERT_EQ(1U, results_addr.size()); @@ -3427,6 +3487,8 @@ TEST_P(FormDataImporterTest, ImportFormData_OneAddressCreditCardDisabled) { form.fields.push_back(field); test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); + test::CreateTestFormField("Country:", "country", "US", "text", &field); + form.fields.push_back(field); // Credit card section. AddFullCreditCardForm(&form, "Biggie Smalls", "4111-1111-1111-1111", "01", @@ -3450,7 +3512,7 @@ TEST_P(FormDataImporterTest, ImportFormData_OneAddressCreditCardDisabled) { AutofillProfile expected_address(base::GenerateGUID(), test::kEmptyOrigin); test::SetProfileInfo(&expected_address, "George", nullptr, "Washington", "theprez@gmail.com", nullptr, "21 Laussat St", nullptr, - "San Francisco", "California", "94102", "", nullptr); + "San Francisco", "California", "94102", "US", nullptr); const std::vector<AutofillProfile*>& results_addr = personal_data_manager_->GetProfiles(); ASSERT_EQ(1U, results_addr.size()); @@ -3618,19 +3680,12 @@ TEST_P(FormDataImporterTest, ImportFormData_HiddenCreditCardFormAfterEntered) { // UPI ID. TEST_P(FormDataImporterTest, ImportFormData_DontSetUpiIdWhenOnlyCreditCardExists) { - // Simulate a form submission with a new credit card. - FormData form; - form.url = GURL("https://wwww.foo.com"); - - AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01", - "2999"); - - FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(nullptr, nullptr); + std::unique_ptr<FormStructure> form_structure = + ConstructDefaultCreditCardFormStructure(); std::unique_ptr<CreditCard> imported_credit_card; absl::optional<std::string> imported_upi_id; EXPECT_TRUE(ImportFormDataAndProcessAddressCandidates( - form_structure, /*profile_autofill_enabled=*/true, + *form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, /*should_return_local_card=*/true, &imported_credit_card, &imported_upi_id)); @@ -4448,13 +4503,14 @@ class FormDataImporterNonParameterizedTest : public FormDataImporterTestBase, public testing::Test { private: void SetUp() override { SetUpHelper(); } + void TearDown() override { TearDownHelper(); } }; TEST_F(FormDataImporterNonParameterizedTest, ProcessCreditCardImportCandidate_EmptyCreditCard) { std::unique_ptr<CreditCard> imported_credit_card; - FormData form; - AddFullCreditCardForm(&form, "Clyde Barrow", "378282246310005", "04", "2999"); + std::unique_ptr<FormStructure> form_structure = + ConstructDefaultCreditCardFormStructure(); // |form_data_importer_|'s |imported_credit_card_record_type_| is set to // LOCAL_CARD because we need to make sure we do not return early in the @@ -4469,13 +4525,59 @@ TEST_F(FormDataImporterNonParameterizedTest, personal_data_manager_->OnSyncServiceInitialized(&sync_service); EXPECT_FALSE(form_data_importer_->ProcessCreditCardImportCandidate( - FormStructure(form), std::move(imported_credit_card), + *form_structure, std::move(imported_credit_card), /*detected_upi_id=*/"", /*credit_card_autofill_enabled=*/true, /*is_credit_card_upstream_enabled=*/true)); personal_data_manager_->OnSyncServiceInitialized(nullptr); } +#if !BUILDFLAG(IS_IOS) +TEST_F(FormDataImporterNonParameterizedTest, + ProcessCreditCardImportCandidate_VirtualCardEligible) { + CreditCard imported_credit_card = test::GetMaskedServerCard(); + imported_credit_card.SetNetworkForMaskedCard(kAmericanExpressCard); + imported_credit_card.set_instrument_id(1111); + imported_credit_card.set_virtual_card_enrollment_state( + CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE); + std::unique_ptr<FormStructure> form_structure = + ConstructDefaultCreditCardFormStructure(); + + form_data_importer_->imported_credit_card_record_type_ = + FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD; + form_data_importer_->SetFetchedCardInstrumentId(2222); + + // We need a sync service so that + // LocalCardMigrationManager::ShouldOfferLocalCardMigration() does not + // crash. + syncer::TestSyncService sync_service; + personal_data_manager_->OnSyncServiceInitialized(&sync_service); + + EXPECT_CALL(*virtual_card_enrollment_manager_, + InitVirtualCardEnroll(_, VirtualCardEnrollmentSource::kDownstream, + _, _, _, _)) + .Times(0); + EXPECT_FALSE(form_data_importer_->ProcessCreditCardImportCandidate( + *form_structure, std::make_unique<CreditCard>(imported_credit_card), + /*detected_upi_id=*/"", + /*credit_card_autofill_enabled=*/true, + /*is_credit_card_upstream_enabled=*/true)); + + form_data_importer_->SetFetchedCardInstrumentId(1111); + EXPECT_CALL(*virtual_card_enrollment_manager_, + InitVirtualCardEnroll(_, VirtualCardEnrollmentSource::kDownstream, + _, _, _, _)) + .Times(1); + EXPECT_TRUE(form_data_importer_->ProcessCreditCardImportCandidate( + *form_structure, std::make_unique<CreditCard>(imported_credit_card), + /*detected_upi_id=*/"", + /*credit_card_autofill_enabled=*/true, + /*is_credit_card_upstream_enabled=*/true)); + + personal_data_manager_->OnSyncServiceInitialized(nullptr); +} +#endif + TEST_F(FormDataImporterNonParameterizedTest, ShouldOfferUploadCardOrLocalCardSave) { // Should not offer save for null cards. diff --git a/chromium/components/autofill/core/browser/form_data_importer_utils.cc b/chromium/components/autofill/core/browser/form_data_importer_utils.cc new file mode 100644 index 00000000000..978b2d8be02 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_data_importer_utils.cc @@ -0,0 +1,244 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/form_data_importer_utils.h" + +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/data_model/autofill_profile_comparator.h" +#include "components/autofill/core/browser/geo/autofill_country.h" +#include "components/autofill/core/browser/logging/log_manager.h" +#include "components/autofill/core/browser/metrics/autofill_metrics.h" +#include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_internals/log_message.h" + +namespace autofill { + +namespace { + +using AddressImportRequirement = + AutofillMetrics::AddressProfileImportRequirementMetric; + +} // anonymous namespace + +bool IsMinimumAddress(const AutofillProfile& profile, + const std::string& predicted_country_code, + const std::string& app_locale, + LogBuffer* import_log_buffer, + bool collect_metrics) { + AutofillCountry country(predicted_country_code, app_locale); + + // Include the details of the country to the log. + LOG_AF(import_log_buffer) << country; + + // Check the |ADDRESS_HOME_LINE1| requirement. + bool is_line1_missing = false; + if (country.requires_line1() && !profile.HasRawInfo(ADDRESS_HOME_LINE1) && + !profile.HasRawInfo(ADDRESS_HOME_STREET_NAME)) { + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Missing required ADDRESS_HOME_LINE1." << CTag{}; + is_line1_missing = true; + } + + // Check the |ADDRESS_HOME_CITY| requirement. + bool is_city_missing = false; + if (country.requires_city() && !profile.HasRawInfo(ADDRESS_HOME_CITY)) { + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Missing required ADDRESS_HOME_CITY." << CTag{}; + is_city_missing = true; + } + + // Check the |ADDRESS_HOME_STATE| requirement. + bool is_state_missing = false; + if (country.requires_state() && !profile.HasRawInfo(ADDRESS_HOME_STATE)) { + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Missing required ADDRESS_HOME_STATE." << CTag{}; + is_state_missing = true; + } + + // Check the |ADDRESS_HOME_ZIP| requirement. + bool is_zip_missing = false; + if (country.requires_zip() && !profile.HasRawInfo(ADDRESS_HOME_ZIP)) { + LOG_AF(import_log_buffer) << LogMessage::kImportAddressProfileFromFormFailed + << "Missing required ADDRESS_HOME_ZIP." << CTag{}; + is_zip_missing = true; + } + + bool is_zip_or_state_requirement_violated = false; + if (country.requires_zip_or_state() && + !profile.HasRawInfo(ADDRESS_HOME_ZIP) && + !profile.HasRawInfo(ADDRESS_HOME_STATE)) { + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Missing required ADDRESS_HOME_ZIP or ADDRESS_HOME_STATE." << CTag{}; + is_zip_or_state_requirement_violated = true; + } + + bool is_line1_or_house_number_violated = false; + if (country.requires_line1_or_house_number() && + !profile.HasRawInfo(ADDRESS_HOME_LINE1) && + !profile.HasRawInfo(ADDRESS_HOME_HOUSE_NUMBER)) { + LOG_AF(import_log_buffer) + << LogMessage::kImportAddressProfileFromFormFailed + << "Missing required ADDRESS_HOME_LINE1 or ADDRESS_HOME_HOUSE_NUMBER." + << CTag{}; + is_line1_or_house_number_violated = true; + } + + // Collect metrics regarding the requirements. + if (collect_metrics) { + AutofillMetrics::LogAddressFormImportRequirementMetric( + is_line1_missing + ? AddressImportRequirement::LINE1_REQUIREMENT_VIOLATED + : AddressImportRequirement::LINE1_REQUIREMENT_FULFILLED); + + AutofillMetrics::LogAddressFormImportRequirementMetric( + is_city_missing ? AddressImportRequirement::CITY_REQUIREMENT_VIOLATED + : AddressImportRequirement::CITY_REQUIREMENT_FULFILLED); + + AutofillMetrics::LogAddressFormImportRequirementMetric( + is_state_missing + ? AddressImportRequirement::STATE_REQUIREMENT_VIOLATED + : AddressImportRequirement::STATE_REQUIREMENT_FULFILLED); + + AutofillMetrics::LogAddressFormImportRequirementMetric( + is_zip_missing ? AddressImportRequirement::ZIP_REQUIREMENT_VIOLATED + : AddressImportRequirement::ZIP_REQUIREMENT_FULFILLED); + + AutofillMetrics::LogAddressFormImportRequirementMetric( + is_zip_or_state_requirement_violated + ? AddressImportRequirement::ZIP_OR_STATE_REQUIREMENT_VIOLATED + : AddressImportRequirement::ZIP_OR_STATE_REQUIREMENT_FULFILLED); + + AutofillMetrics::LogAddressFormImportCountrySpecificFieldRequirementsMetric( + is_zip_missing, is_state_missing, is_city_missing, is_line1_missing); + } + + // Return true if all requirements are fulfilled. + return !(is_line1_missing || is_city_missing || is_state_missing || + is_zip_missing || is_zip_or_state_requirement_violated || + is_line1_or_house_number_violated); +} + +std::string GetPredictedCountryCode(const AutofillProfile& profile, + const std::string& variation_country_code, + const std::string& app_locale, + LogBuffer* import_log_buffer) { + // Try to acquire the country code form the filled form. + std::string country_code = + base::UTF16ToASCII(profile.GetRawInfo(ADDRESS_HOME_COUNTRY)); + + if (import_log_buffer && !country_code.empty()) { + *import_log_buffer << LogMessage::kImportAddressProfileFromFormCountrySource + << "Country entry in form." << CTag{}; + } + + // As a fallback, use the variation service state to get a country code. + if (country_code.empty() && !variation_country_code.empty()) { + country_code = variation_country_code; + if (import_log_buffer) { + *import_log_buffer + << LogMessage::kImportAddressProfileFromFormCountrySource + << "Variations service." << CTag{}; + } + } + + // As the last resort, derive the country code from the app_locale. + if (country_code.empty()) { + country_code = AutofillCountry::CountryCodeForLocale(app_locale); + if (import_log_buffer && !country_code.empty()) { + *import_log_buffer + << LogMessage::kImportAddressProfileFromFormCountrySource + << "App locale." << CTag{}; + } + } + + return country_code; +} + +MultiStepImportMerger::MultiStepImportMerger( + const std::string& app_locale, + const std::string& variation_country_code) + : app_locale_(app_locale), + variation_country_code_(variation_country_code) {} +MultiStepImportMerger::~MultiStepImportMerger() {} + +void MultiStepImportMerger::ProcessMultiStepImport( + AutofillProfile& profile, + ProfileImportMetadata& import_metadata, + const url::Origin& origin) { + if (!base::FeatureList::IsEnabled( + features::kAutofillEnableMultiStepImports)) { + return; + } + + multistep_candidates_.RemoveOutdatedItems( + features::kAutofillMultiStepImportCandidateTTL.Get(), origin); + bool has_min_address_requirements = + MergeProfileWithMultiStepCandidates(profile, import_metadata, origin); + + if (!has_min_address_requirements || + features::kAutofillEnableMultiStepImportComplements.Get()) { + // Add `profile| as a `multistep_candidate`. This happens for incomplete + // profiles, which can then be complemented in later steps. When + // `kAutofillEnableMultiStepImportComplements` is enabled, complete profiles + // are stored too, which enables updating them in later steps. + // In the latter case, Autofill tries to import the `profile`. This logs + // metrics depending on `import_metadata`. To prevent double counting, + // an we store an empty `ProfileImportMetadata` object in this case. + multistep_candidates_.Push({.profile = profile, + .import_metadata = has_min_address_requirements + ? ProfileImportMetadata() + : import_metadata}, + origin); + } +} + +bool MultiStepImportMerger::MergeProfileWithMultiStepCandidates( + AutofillProfile& profile, + ProfileImportMetadata& import_metadata, + const url::Origin& origin) { + // Greedily merge with a prefix of |multistep_candidates|. + AutofillProfileComparator comparator(app_locale_); + auto candidate = multistep_candidates_.begin(); + AutofillProfile completed_profile = profile; + ProfileImportMetadata completed_metadata = import_metadata; + // Country completion has not happened yet, so this field can be ignored. + DCHECK(!completed_metadata.did_complement_country); + while (candidate != multistep_candidates_.end()) { + if (!comparator.AreMergeable(completed_profile, candidate->profile) || + completed_profile.MergeDataFrom(candidate->profile, app_locale_)) { + break; + } + // ProfileImportMetadata is only relevant for metrics. If the phone number + // was removed from a partial profile, we still want that removal to appear + // in the metrics, because it would have hindered that partial profile from + // import and merging. + completed_metadata.did_remove_invalid_phone_number |= + candidate->import_metadata.did_remove_invalid_phone_number; + candidate++; + } + + // The minimum address requirements depend on the country, which has possibly + // changed as a result of the merge. + if (IsMinimumAddress( + completed_profile, + GetPredictedCountryCode(completed_profile, variation_country_code_, + app_locale_, /*import_log_buffer=*/nullptr), + app_locale_, + /*import_log_buffer=*/nullptr, /*collect_metrics=*/false)) { + profile = std::move(completed_profile); + import_metadata = std::move(completed_metadata); + multistep_candidates_.Clear(); + return true; + } else { + // Remove all profiles that couldn't be merged. + multistep_candidates_.erase(candidate, multistep_candidates_.end()); + return false; + } +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_data_importer_utils.h b/chromium/components/autofill/core/browser/form_data_importer_utils.h new file mode 100644 index 00000000000..ee2b64596ad --- /dev/null +++ b/chromium/components/autofill/core/browser/form_data_importer_utils.h @@ -0,0 +1,171 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_DATA_IMPORTER_UTILS_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_DATA_IMPORTER_UTILS_H_ + +#include <iterator> +#include <list> +#include <string> +#include <utility> + +#include "base/time/time.h" +#include "components/autofill/core/browser/autofill_profile_import_process.h" +#include "components/autofill/core/browser/data_model/autofill_profile.h" +#include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/logging/log_buffer.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "url/origin.h" + +namespace autofill { + +// Encapsulates a list of Ts, ordered by the time they were added (newest +// first). All Ts share the same `origin`. This is useful for tracking +// relationships between submitted forms on the same origin, within a small +// period of time. +template <class T> +class TimestampedSameOriginQueue { + public: + // The queue stores Ts augmented with a timestamp. + struct value_type : public T { + value_type(T t, base::Time timestamp) + : T(std::move(t)), timestamp(std::move(timestamp)) {} + + const base::Time timestamp; + }; + using const_iterator = typename std::list<value_type>::const_iterator; + + // Pushes `item` at the current timestamp. + void Push(T item, const url::Origin& item_origin) { + DCHECK(!origin_ || *origin_ == item_origin); + items_.emplace_front(std::move(item), AutofillClock::Now()); + origin_ = item_origin; + } + + // Removes the oldest element from the queue. + void Pop() { erase(std::prev(end()), end()); } + + // Removes all `items` from a different `origin` or older than `ttl`. + // This is not done as part of `Push()`, as outdated items (for example in the + // multi-step import use-case) should be deleted as soon as possible for + // privacy reasons, even when no `Push()` happens. + void RemoveOutdatedItems(const base::TimeDelta& ttl, + const url::Origin& new_origin) { + if (origin_ && *origin_ != new_origin) { + Clear(); + } else { + const base::Time now = AutofillClock::Now(); + while (!empty() && now - items_.back().timestamp > ttl) + Pop(); + } + } + + // Returns the origin shared by the elements in the queue. Or nullopt, if + // the queue is currently `Empty()`. + const absl::optional<url::Origin>& Origin() const { return origin_; } + + size_t size() const { return items_.size(); } + bool empty() const { return items_.empty(); } + + // Removes the items [first, last[. + void erase(const_iterator first, const_iterator last) { + items_.erase(first, last); + if (empty()) + origin_.reset(); + } + + void Clear() { erase(begin(), end()); } + + // The elements are ordered from newest to latest. + const_iterator begin() const { return items_.begin(); } + const_iterator end() const { return items_.end(); } + + private: + std::list<value_type> items_; + // If the queue is not `empty()`, this represents the origin of all `items_`. + absl::optional<url::Origin> origin_; +}; + +// Returns true if minimum requirements for import of a given `profile` have +// been met. An address submitted via a form must have at least the fields +// required as determined by its country code. +// No verification of validity of the contents is performed. This is an +// existence check only. +bool IsMinimumAddress(const AutofillProfile& profile, + const std::string& predicted_country_code, + const std::string& app_locale, + LogBuffer* import_log_buffer, + bool collect_metrics); + +// Tries to infer the country `profile` is from, which can be useful to +// verify whether the data is sensible. Returns a two-letter ISO country code +// by considering, in decreasing order of priority: +// - The country specified in `profile`. +// - The country determined by the variation service stored in +// `variation_country_code`. +// - The country code corresponding to `app_locale`. +std::string GetPredictedCountryCode(const AutofillProfile& profile, + const std::string& variation_country_code, + const std::string& app_locale, + LogBuffer* import_log_buffer); + +// Stores recently submitted profile fragments, which are merged against future +// import candidates to construct a complete profile. This enables importing +// from multi-step import flows. +class MultiStepImportMerger { + public: + MultiStepImportMerger(const std::string& app_locale, + const std::string& variation_country_code); + ~MultiStepImportMerger(); + + // Removes updated multi-step candidates, merges `profile` with multi-step + // candidates and potentially stores it as a multi-step candidate itself. + // `profile` and `import_metadata` are updated accordingly, if the profile + // can be merged. See `MergeProfileWithMultiStepCandidates()` for details. + // Only applicable when `kAutofillEnableMultiStepImports` is enabled. + void ProcessMultiStepImport(AutofillProfile& profile, + ProfileImportMetadata& import_metadata, + const url::Origin& origin); + + const absl::optional<url::Origin>& Origin() const { + return multistep_candidates_.Origin(); + } + + void Clear() { multistep_candidates_.Clear(); } + + private: + // Merges a given `profile` stepwise with `multistep_candidates_` to + // complete it. `profile` is assumed to contain no invalid information. + // Returns true if the resulting profile satisfies the minimum address + // requirements. `profile` and `import_metadata` are updated in this case + // with the result of merging all relevant candidates. + // Returns false otherwise and leaves `profile` and `import_metadata` + // unchanged. Any merged or colliding `multistep_candidates_` are cleared. + // `origin`: The origin of the form where `profile` was imported from. + bool MergeProfileWithMultiStepCandidates( + AutofillProfile& profile, + ProfileImportMetadata& import_metadata, + const url::Origin& origin); + + // Needed to predict the country code of a merged import candidate, to + // ultimately decide if the profile meets the minimum import requirements. + std::string app_locale_; + std::string variation_country_code_; + + // Represents a submitted form, stored to be considered as a merge candidate + // for other candidate profiles in future submits in a multi-step import + // flow. + struct MultiStepFormProfileCandidate { + // The import candidate. + AutofillProfile profile; + // Metadata about how `profile` was constructed. + ProfileImportMetadata import_metadata; + }; + TimestampedSameOriginQueue<MultiStepFormProfileCandidate> + multistep_candidates_; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_DATA_IMPORTER_UTILS_H_ diff --git a/chromium/components/autofill/core/browser/form_data_importer_utils_unittest.cc b/chromium/components/autofill/core/browser/form_data_importer_utils_unittest.cc new file mode 100644 index 00000000000..f20b2073d42 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_data_importer_utils_unittest.cc @@ -0,0 +1,85 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/form_data_importer_utils.h" + +#include <vector> + +#include "base/time/time.h" +#include "components/autofill/core/browser/test_autofill_clock.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +namespace { + +// As TimestampedSameOriginQueue cannot be initialized with primitive types, +// this wrapper is used for testing. +struct IntWrapper { + int value; +}; +bool operator==(IntWrapper x, int y) { + return x.value == y; +} + +} // anonymous namespace + +// TimestampedSameOriginQueue's queue-like functionality works as expected. +TEST(FormDataImporterUtilsTest, TimestampedSameOriginQueue) { + TimestampedSameOriginQueue<IntWrapper> queue; + EXPECT_TRUE(queue.empty()); + const url::Origin irrelevant_origin; + for (int i = 0; i < 4; i++) + queue.Push({i}, irrelevant_origin); + EXPECT_EQ(queue.size(), 4u); + EXPECT_THAT(queue, testing::ElementsAre(3, 2, 1, 0)); + queue.Pop(); + EXPECT_THAT(queue, testing::ElementsAre(3, 2, 1)); + queue.erase(std::next(queue.begin()), queue.end()); + EXPECT_THAT(queue, testing::ElementsAre(3)); + queue.Clear(); + EXPECT_TRUE(queue.empty()); +} + +// RemoveOutdatedItems clears the queue if the origin doesn't match. +TEST(FormDataImporterUtilsTest, TimestampedSameOriginQueue_DifferentOrigins) { + TimestampedSameOriginQueue<IntWrapper> queue; + auto foo_origin = url::Origin::Create(GURL("http://foo.com")); + queue.Push({0}, foo_origin); + EXPECT_EQ(queue.Origin(), foo_origin); + // The TTL or 1 hour is irrelevant here. + queue.RemoveOutdatedItems(base::Hours(1), + url::Origin::Create(GURL("http://bar.com"))); + EXPECT_EQ(queue.Origin(), absl::nullopt); + EXPECT_TRUE(queue.empty()); +} + +// RemoveOutdatedItems clears items past their TTL. +TEST(FormDataImporterUtilsTest, TimestampedSameOriginQueue_TTL) { + TimestampedSameOriginQueue<IntWrapper> queue; + const url::Origin irrelevant_origin; + TestAutofillClock test_clock; + for (int i = 0; i < 4; i++) { + queue.Push({i}, irrelevant_origin); + test_clock.Advance(base::Minutes(1)); + } + // Remove all items older than 2.5 min. + queue.RemoveOutdatedItems(base::Seconds(150), irrelevant_origin); + EXPECT_THAT(queue, testing::ElementsAre(3, 2)); +} + +TEST(FormDataImporterUtilsTest, GetPredictedCountryCode) { + AutofillProfile us_profile; + us_profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"US"); + AutofillProfile empty_profile; + // Test prioritization: profile > variation service state > app locale + EXPECT_EQ(GetPredictedCountryCode(us_profile, "DE", "de-AT", nullptr), "US"); + EXPECT_EQ(GetPredictedCountryCode(us_profile, "", "de-AT", nullptr), "US"); + EXPECT_EQ(GetPredictedCountryCode(empty_profile, "DE", "de-AT", nullptr), + "DE"); + EXPECT_EQ(GetPredictedCountryCode(empty_profile, "", "de-AT", nullptr), "AT"); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/address_field.cc b/chromium/components/autofill/core/browser/form_parsing/address_field.cc index 32cacb9bace..01bf673df74 100644 --- a/chromium/components/autofill/core/browser/form_parsing/address_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/address_field.cc @@ -13,11 +13,11 @@ #include "base/check.h" #include "base/strings/string_util.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/browser/form_parsing/regex_patterns.h" #include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_regex_constants.h" namespace autofill { @@ -193,7 +193,7 @@ AddressField::AddressField(LogManager* log_manager) : log_manager_(log_manager) {} void AddressField::AddClassifications( - FieldCandidatesMap* field_candidates) const { + FieldCandidatesMap& field_candidates) const { // The page can request the address lines as a single textarea input or as // multiple text fields (or not at all), but it shouldn't be possible to // request both. 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 5b485650c75..9ed051d6d8e 100644 --- a/chromium/components/autofill/core/browser/form_parsing/address_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/address_field.h @@ -33,7 +33,7 @@ class AddressField : public FormField { AddressField& operator=(const AddressField&) = delete; protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: // When parsing a field's label and name separately with a given pattern: diff --git a/chromium/components/autofill/core/browser/form_parsing/birthdate_field.cc b/chromium/components/autofill/core/browser/form_parsing/birthdate_field.cc new file mode 100644 index 00000000000..005a4b87646 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/birthdate_field.cc @@ -0,0 +1,118 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/form_parsing/birthdate_field.h" + +#include "base/memory/ptr_util.h" +#include "base/ranges/algorithm.h" +#include "base/strings/string_number_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/form_parsing/regex_patterns.h" +#include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_regex_constants.h" + +namespace autofill { + +namespace { + +// Checks if the `option`'s content or value represents a number with value +// contained in [a, b]. +bool IsSelectValueBetween(const SelectOption& option, int a, int b) { + auto IsValueBetween = [&](base::StringPiece16 string) { + int value; + return base::StringToInt(string, &value) && a <= value && value <= b; + }; + return IsValueBetween(option.content) || IsValueBetween(option.value); +} + +} // namespace + +BirthdateField::BirthdateField(const AutofillField* day, + const AutofillField* month, + const AutofillField* year) + : day_(day), month_(month), year_(year) {} + +// static +std::unique_ptr<FormField> BirthdateField::Parse( + AutofillScanner* scanner, + const LanguageCode& page_language, + PatternSource pattern_source, + LogManager* log_manager) { + // Currently only <select> elements are considered. + AutofillField* day = nullptr; + AutofillField* month = nullptr; + AutofillField* year = nullptr; + // Expect at most 31 days/12 months plus one placeholder. + if (FormField::ParseInAnyOrder( + scanner, + {{&day, base::BindRepeating(&IsSelectWithIncreasingValues, scanner, + 28, 32)}, + {&month, base::BindRepeating(&IsSelectWithIncreasingValues, scanner, + 12, 13)}, + {&year, base::BindRepeating(&IsLikelyBirthdateYearSelectField, + scanner)}})) { + return base::WrapUnique(new BirthdateField(day, month, year)); + } + return nullptr; +} + +// static +bool BirthdateField::IsSelectWithIncreasingValues(AutofillScanner* scanner, + int max_value, + size_t max_options) { + AutofillField* field = scanner->Cursor(); + if (!MatchesFormControlType(field->form_control_type, + {MatchFieldType::kSelect})) { + return false; + } + auto options = field->options; + if (options.empty() || options.size() > max_options) + return false; + auto it = options.cbegin(); + // Skip a possible placeholder. + if (!IsSelectValueBetween(*it, 1, 1)) + it++; + // Check that there are the enough options remaining. + if (options.cend() - it < max_value) + return false; + for (int i = 1; i <= max_value; i++) { + if (!IsSelectValueBetween(*it, i, i)) + return false; + it++; + } + return true; +} + +// static +bool BirthdateField::IsLikelyBirthdateYearSelectField( + AutofillScanner* scanner) { + AutofillField* field = scanner->Cursor(); + if (!MatchesFormControlType(field->form_control_type, + {MatchFieldType::kSelect})) { + return false; + } + auto options = field->options; + base::Time::Exploded current_date; + AutofillClock::Now().UTCExplode(¤t_date); + DCHECK(current_date.HasValidValues()); + return !options.empty() && + base::ranges::all_of(options.begin() + 1, options.end(), + [&](const SelectOption& option) { + return IsSelectValueBetween(option, 1900, + current_date.year); + }); +} + +void BirthdateField::AddClassifications( + FieldCandidatesMap& field_candidates) const { + AddClassification(day_, BIRTHDATE_DAY, kBaseBirthdateParserScore, + field_candidates); + AddClassification(month_, BIRTHDATE_MONTH, kBaseBirthdateParserScore, + field_candidates); + AddClassification(year_, BIRTHDATE_4_DIGIT_YEAR, kBaseBirthdateParserScore, + field_candidates); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/birthdate_field.h b/chromium/components/autofill/core/browser/form_parsing/birthdate_field.h new file mode 100644 index 00000000000..8a91ee0ab77 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/birthdate_field.h @@ -0,0 +1,56 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_BIRTHDATE_FIELD_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_BIRTHDATE_FIELD_H_ + +#include <vector> + +#include "base/memory/raw_ptr.h" +#include "components/autofill/core/browser/form_parsing/form_field.h" +#include "components/autofill/core/common/language_code.h" + +namespace autofill { + +// Birthdate fields are currently not filled, but identifying them will help to +// reduce the number of false positive credit card expiration dates. +class BirthdateField : public FormField { + public: + static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, + const LanguageCode& page_language, + PatternSource pattern_source, + LogManager* log_manager); + + BirthdateField(const BirthdateField&) = delete; + BirthdateField& operator=(const BirthdateField&) = delete; + + protected: + void AddClassifications(FieldCandidatesMap& field_candidates) const override; + + private: + BirthdateField(const AutofillField* day, + const AutofillField* month, + const AutofillField* year); + + // Checks if the scanner's current field is a <select> and if its options + // contains the values [1, `max_value`] in increasing order, possibly after a + // placeholder. Moreover checks that at most max_options options are present. + static bool IsSelectWithIncreasingValues(AutofillScanner* scanner, + int max_value, + size_t max_options); + + // Checks if the scanner's current field is a <select> and if all but the + // first of its options represents a numerical value in [1900, current-year]. + // The first option might contain a placeholder. + static bool IsLikelyBirthdateYearSelectField(AutofillScanner* scanner); + + private: + raw_ptr<const AutofillField> day_; + raw_ptr<const AutofillField> month_; + raw_ptr<const AutofillField> year_; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_BIRTHDATE_FIELD_H_ diff --git a/chromium/components/autofill/core/browser/form_parsing/birthdate_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/birthdate_field_unittest.cc new file mode 100644 index 00000000000..56abba77f8e --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/birthdate_field_unittest.cc @@ -0,0 +1,132 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/form_parsing/birthdate_field.h" + +#include <vector> + +#include "base/strings/string_number_conversions.h" +#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h" + +namespace autofill { + +namespace { + +// Returns a vector of consecutive SelectOption items with values and content in +// [`low`, `high`] in either in- or decreasing order. +std::vector<SelectOption> GetSelectOptionRange(int low, + int high, + bool increasing) { + std::vector<SelectOption> options; + for (int i = 0; i <= high - low; i++) { + std::u16string value = + base::NumberToString16(increasing ? low + i : high - i); + options.push_back({value, value}); + } + return options; +} + +std::vector<SelectOption> GetDays() { + return GetSelectOptionRange(1, 31, /*increasing=*/true); +} +std::vector<SelectOption> GetMonths() { + return GetSelectOptionRange(1, 12, /*increasing=*/true); +} +std::vector<SelectOption> GetYears() { + return GetSelectOptionRange(1900, 2022, /*increasing=*/false); +} + +} // namespace + +class BirthdateFieldTest + : public FormFieldTestBase, + public testing::TestWithParam<PatternProviderFeatureState> { + public: + BirthdateFieldTest() : FormFieldTestBase(GetParam()) {} + BirthdateFieldTest(const BirthdateFieldTest&) = delete; + BirthdateFieldTest& operator=(const BirthdateFieldTest&) = delete; + + protected: + std::unique_ptr<FormField> Parse( + AutofillScanner* scanner, + const LanguageCode& page_language = LanguageCode("en")) override { + return BirthdateField::Parse(scanner, page_language, + GetActivePatternSource(), + /*log_manager=*/nullptr); + } +}; + +INSTANTIATE_TEST_SUITE_P( + BirthdateFieldTest, + BirthdateFieldTest, + ::testing::ValuesIn(PatternProviderFeatureState::All())); + +TEST_P(BirthdateFieldTest, ParseDMY) { + AddSelectOneFormFieldData("", "", GetDays(), BIRTHDATE_DAY); + AddSelectOneFormFieldData("", "", GetMonths(), BIRTHDATE_MONTH); + AddSelectOneFormFieldData("", "", GetYears(), BIRTHDATE_4_DIGIT_YEAR); + ClassifyAndVerify(ParseResult::PARSED); +} + +TEST_P(BirthdateFieldTest, ParseYMD) { + AddSelectOneFormFieldData("", "", GetYears(), BIRTHDATE_4_DIGIT_YEAR); + AddSelectOneFormFieldData("", "", GetMonths(), BIRTHDATE_MONTH); + AddSelectOneFormFieldData("", "", GetDays(), BIRTHDATE_DAY); + ClassifyAndVerify(ParseResult::PARSED); +} + +TEST_P(BirthdateFieldTest, DefaultOptions) { + auto days = GetDays(); + days.insert(days.begin(), {u"", u"Day"}); + auto months = GetMonths(); + months.insert(months.begin(), {u"", u"Month"}); + auto years = GetYears(); + years.insert(years.begin(), {u"", u"Year"}); + AddSelectOneFormFieldData("", "", days, BIRTHDATE_DAY); + AddSelectOneFormFieldData("", "", months, BIRTHDATE_MONTH); + AddSelectOneFormFieldData("", "", years, BIRTHDATE_4_DIGIT_YEAR); + ClassifyAndVerify(ParseResult::PARSED); +} + +TEST_P(BirthdateFieldTest, LeadingZeros) { + // Replace the first 9 day entries with 01-09. + auto days = GetDays(); + ASSERT_GE(days.size(), 9u); + for (int i = 0; i < 9; i++) { + std::u16string value = u"0" + base::NumberToString16(i + 1); + days[i] = {value, value}; + } + AddSelectOneFormFieldData("", "", days, BIRTHDATE_DAY); + AddSelectOneFormFieldData("", "", GetMonths(), BIRTHDATE_MONTH); + AddSelectOneFormFieldData("", "", GetYears(), BIRTHDATE_4_DIGIT_YEAR); + ClassifyAndVerify(ParseResult::PARSED); +} + +TEST_P(BirthdateFieldTest, TooManyOptions) { + auto days = GetDays(); + days.insert(days.begin(), {u"", u"Hello"}); + days.insert(days.begin(), {u"", u"World"}); + EXPECT_GT(days.size(), 13u); // Too many options for a day selector. + AddSelectOneFormFieldData("", "", days, BIRTHDATE_DAY); + AddSelectOneFormFieldData("", "", GetMonths(), BIRTHDATE_MONTH); + AddSelectOneFormFieldData("", "", GetYears(), BIRTHDATE_4_DIGIT_YEAR); + ClassifyAndVerify(ParseResult::NOT_PARSED); +} + +TEST_P(BirthdateFieldTest, MissingYear) { + AddSelectOneFormFieldData("", "", GetDays(), BIRTHDATE_DAY); + AddSelectOneFormFieldData("", "", GetMonths(), BIRTHDATE_MONTH); + ClassifyAndVerify(ParseResult::NOT_PARSED); +} + +TEST_P(BirthdateFieldTest, IncompleteMonth) { + auto months = GetMonths(); + months.resize(5); + AddSelectOneFormFieldData("", "", GetDays(), BIRTHDATE_DAY); + AddSelectOneFormFieldData("", "", months, BIRTHDATE_MONTH); + AddSelectOneFormFieldData("", "", GetYears(), BIRTHDATE_4_DIGIT_YEAR); + ClassifyAndVerify(ParseResult::NOT_PARSED); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc b/chromium/components/autofill/core/browser/form_parsing/credit_card_field.cc index 806c8b74ae7..d025e90600b 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 @@ -15,8 +15,6 @@ #include "base/strings/string_util.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" @@ -24,6 +22,8 @@ #include "components/autofill/core/browser/form_parsing/regex_patterns.h" #include "components/autofill/core/common/autofill_clock.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/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" @@ -302,18 +302,18 @@ bool CreditCardField::LikelyCardMonthSelectField(AutofillScanner* scanner) { // Filter out years. const std::u16string kNumericalYearRe = u"[1-9][0-9][0-9][0-9]"; for (const auto& option : field->options) { - if (MatchesPattern(option.value, kNumericalYearRe)) + if (MatchesRegexWithCache(option.value, kNumericalYearRe)) return false; } for (const auto& option : field->options) { - if (MatchesPattern(option.content, kNumericalYearRe)) + if (MatchesRegexWithCache(option.content, kNumericalYearRe)) return false; } // Look for numerical months. const std::u16string kNumericalMonthRe = u"12"; - if (MatchesPattern(field->options.back().value, kNumericalMonthRe) || - MatchesPattern(field->options.back().content, kNumericalMonthRe)) { + if (MatchesRegexWithCache(field->options.back().value, kNumericalMonthRe) || + MatchesRegexWithCache(field->options.back().content, kNumericalMonthRe)) { return true; } @@ -343,7 +343,7 @@ bool CreditCardField::LikelyCardYearSelectField( // numbers 1 to 9 as well in them, which we can filter on. const std::u16string kSingleDigitDateRe = u"\\b[1-9]\\b"; for (const auto& option : field->options) { - if (MatchesPattern(option.content, kSingleDigitDateRe)) { + if (MatchesRegexWithCache(option.content, kSingleDigitDateRe)) { return false; } } @@ -361,7 +361,7 @@ bool CreditCardField::LikelyCardYearSelectField( // expiration year, but show it in the context of a birth year selector. const std::u16string kBirthYearRe = u"(1999|99)"; for (const auto& option : field->options) { - if (MatchesPattern(option.content, kBirthYearRe)) { + if (MatchesRegexWithCache(option.content, kBirthYearRe)) { return false; } } @@ -472,9 +472,9 @@ CreditCardField::CreditCardField(LogManager* log_manager) CreditCardField::~CreditCardField() {} void CreditCardField::AddClassifications( - FieldCandidatesMap* field_candidates) const { - for (size_t index = 0; index < numbers_.size(); ++index) { - AddClassification(numbers_[index], CREDIT_CARD_NUMBER, + FieldCandidatesMap& field_candidates) const { + for (auto* number : numbers_) { + AddClassification(number, CREDIT_CARD_NUMBER, kBaseCreditCardParserScore, field_candidates); } @@ -528,23 +528,18 @@ bool CreditCardField::ParseExpirationDate(AutofillScanner* scanner, // First try to parse split month/year expiration fields by looking for a // pair of select fields that look like month/year. - size_t month_year_saved_cursor = scanner->SaveCursor(); - - if (LikelyCardMonthSelectField(scanner)) { - expiration_month_ = scanner->Cursor(); - scanner->Advance(); - if (LikelyCardYearSelectField(scanner, log_manager, page_language, - pattern_source)) { - expiration_year_ = scanner->Cursor(); - scanner->Advance(); - return true; - } - expiration_month_ = nullptr; - expiration_year_ = nullptr; + if (ParseInAnyOrder( + scanner, {{&expiration_month_, + base::BindRepeating(&LikelyCardMonthSelectField, scanner)}, + {&expiration_year_, + base::BindRepeating(&LikelyCardYearSelectField, scanner, + log_manager, page_language, + pattern_source)}})) { + return true; } // If that fails, do a general regex search. - scanner->RewindTo(month_year_saved_cursor); + size_t month_year_saved_cursor = scanner->SaveCursor(); const auto kMatchCCType = kDefaultMatchParamsWith<MatchFieldType::kNumber, MatchFieldType::kTelephone, 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 b2b8dc7c100..9b0055e0476 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 @@ -34,7 +34,7 @@ class CreditCardField : public FormField { LogManager* log_manager); protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: friend class CreditCardFieldTestBase; 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 15bf02bde14..bc2387b46be 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 @@ -4,6 +4,7 @@ #include "components/autofill/core/browser/form_parsing/credit_card_field.h" +#include <algorithm> #include <memory> #include <vector> @@ -105,7 +106,7 @@ class CreditCardFieldTestBase : public FormFieldTestBase { if (field_ == nullptr) { scanner.Advance(); } else { - field_->AddClassificationsForTesting(&field_candidates_map_); + field_->AddClassificationsForTesting(field_candidates_map_); } } TestClassificationExpectations(); @@ -167,13 +168,16 @@ struct CreditCardFieldYearTestCase { class CreditCardFieldYearTest : public CreditCardFieldTestBase, public testing::TestWithParam<std::tuple<PatternProviderFeatureState, - CreditCardFieldYearTestCase>> { + CreditCardFieldYearTestCase, + bool>> { public: CreditCardFieldYearTest() : CreditCardFieldTestBase(std::get<0>(GetParam())) {} bool with_noise() const { return std::get<1>(GetParam()).with_noise; } + bool ShouldSwapMonthAndYear() const { return std::get<2>(GetParam()); } + ServerFieldType expected_type() const { return std::get<1>(GetParam()).expected_type; } @@ -201,6 +205,9 @@ TEST_P(CreditCardFieldYearTest, ParseMinimumCreditCardWithExpiryDateOptions) { expected_type() == CREDIT_CARD_EXP_2_DIGIT_YEAR ? 2 : 4, MakeOptionVector(), expected_type()); + if (ShouldSwapMonthAndYear()) + std::swap(list_[1], list_[2]); + ClassifyAndVerify(ParseResult::PARSED); } @@ -213,7 +220,8 @@ INSTANTIATE_TEST_SUITE_P( CreditCardFieldYearTestCase{false, CREDIT_CARD_EXP_2_DIGIT_YEAR}, CreditCardFieldYearTestCase{false, CREDIT_CARD_EXP_4_DIGIT_YEAR}, CreditCardFieldYearTestCase{true, CREDIT_CARD_EXP_2_DIGIT_YEAR}, - CreditCardFieldYearTestCase{true, CREDIT_CARD_EXP_4_DIGIT_YEAR}))); + CreditCardFieldYearTestCase{true, CREDIT_CARD_EXP_4_DIGIT_YEAR}), + testing::Bool())); TEST_P(CreditCardFieldTest, ParseFullCreditCard) { AddTextFormFieldData("name_on_card", "Name on Card", CREDIT_CARD_NAME_FULL); 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 864642a4893..85d78a6c719 100644 --- a/chromium/components/autofill/core/browser/form_parsing/email_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/email_field.cc @@ -4,9 +4,9 @@ #include "components/autofill/core/browser/form_parsing/email_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/browser/form_parsing/regex_patterns.h" +#include "components/autofill/core/common/autofill_regex_constants.h" namespace autofill { @@ -30,7 +30,7 @@ std::unique_ptr<FormField> EmailField::Parse(AutofillScanner* scanner, EmailField::EmailField(const AutofillField* field) : field_(field) {} void EmailField::AddClassifications( - FieldCandidatesMap* field_candidates) const { + FieldCandidatesMap& field_candidates) const { AddClassification(field_, EMAIL_ADDRESS, kBaseEmailParserScore, field_candidates); } 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 8fc4af8dde7..b21f21a4f86 100644 --- a/chromium/components/autofill/core/browser/form_parsing/email_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/email_field.h @@ -28,7 +28,7 @@ class EmailField : public FormField { EmailField& operator=(const EmailField&) = delete; protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: raw_ptr<const AutofillField> field_; 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 9cd4d3699a9..d185d20b8a9 100644 --- a/chromium/components/autofill/core/browser/form_parsing/form_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/form_field.cc @@ -8,32 +8,40 @@ #include <cstddef> #include <iterator> #include <memory> +#include <numeric> #include <string> #include <utility> #include "base/feature_list.h" #include "base/strings/string_piece.h" #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_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/address_field.h" #include "components/autofill/core/browser/form_parsing/autofill_parsing_utils.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" +#include "components/autofill/core/browser/form_parsing/birthdate_field.h" #include "components/autofill/core/browser/form_parsing/credit_card_field.h" #include "components/autofill/core/browser/form_parsing/email_field.h" +#include "components/autofill/core/browser/form_parsing/iban_field.h" #include "components/autofill/core/browser/form_parsing/merchant_promo_code_field.h" #include "components/autofill/core/browser/form_parsing/name_field.h" #include "components/autofill/core/browser/form_parsing/phone_field.h" #include "components/autofill/core/browser/form_parsing/price_field.h" #include "components/autofill/core/browser/form_parsing/search_field.h" +#include "components/autofill/core/browser/form_parsing/standalone_cvc_field.h" #include "components/autofill/core/browser/form_parsing/travel_field.h" +#include "components/autofill/core/common/autocomplete_parsing_util.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/common/autofill_constants.h" #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_payments_features.h" +#include "components/autofill/core/common/autofill_regexes.h" #include "components/autofill/core/common/autofill_util.h" namespace autofill { @@ -47,121 +55,146 @@ constexpr bool IsEmpty(const char16_t* s) { } // namespace // static -FieldCandidatesMap FormField::ParseFormFields( +bool FormField::MatchesRegexWithCache(base::StringPiece16 input, + base::StringPiece16 pattern, + std::vector<std::u16string>* groups) { + // TODO(crbug.com/1309848): If ParseForm() is called from the same thread, + // use a thread-unsafe parser. + static base::NoDestructor<AutofillRegexCache> cache(ThreadSafe(true)); + const icu::RegexPattern* regex_pattern = cache->GetRegexPattern(pattern); + return autofill::MatchesRegex(input, *regex_pattern, groups); +} + +// static +void FormField::ParseFormFields( const std::vector<std::unique_ptr<AutofillField>>& fields, const LanguageCode& page_language, bool is_form_tag, PatternSource pattern_source, + FieldCandidatesMap& field_candidates, LogManager* log_manager) { std::vector<AutofillField*> processed_fields = RemoveCheckableFields(fields); - FieldCandidatesMap field_candidates; // Email pass. - ParseFormFieldsPass(EmailField::Parse, processed_fields, &field_candidates, + ParseFormFieldsPass(EmailField::Parse, processed_fields, field_candidates, page_language, pattern_source, log_manager); const size_t email_count = field_candidates.size(); - // Merchant promo code pass. - ParseFormFieldsPass(MerchantPromoCodeField::Parse, processed_fields, - &field_candidates, page_language, pattern_source, - log_manager); - const size_t promo_code_count = field_candidates.size() - email_count; + // Single fields pass. + ParseSingleFieldForms(fields, page_language, is_form_tag, pattern_source, + field_candidates, log_manager); + const size_t fillable_single_fields = field_candidates.size() - email_count; // Phone pass. - ParseFormFieldsPass(PhoneField::Parse, processed_fields, &field_candidates, + ParseFormFieldsPass(PhoneField::Parse, processed_fields, field_candidates, page_language, pattern_source, log_manager); // Travel pass. - ParseFormFieldsPass(TravelField::Parse, processed_fields, &field_candidates, + ParseFormFieldsPass(TravelField::Parse, processed_fields, field_candidates, page_language, pattern_source, log_manager); // Address pass. - ParseFormFieldsPass(AddressField::Parse, processed_fields, &field_candidates, + ParseFormFieldsPass(AddressField::Parse, processed_fields, field_candidates, page_language, pattern_source, log_manager); + // Birthdate pass. + if (base::FeatureList::IsEnabled(features::kAutofillEnableBirthdateParsing)) { + ParseFormFieldsPass(BirthdateField::Parse, processed_fields, + field_candidates, page_language, pattern_source, + log_manager); + } + // Credit card pass. ParseFormFieldsPass(CreditCardField::Parse, processed_fields, - &field_candidates, page_language, pattern_source, + field_candidates, page_language, pattern_source, log_manager); // Price pass. - ParseFormFieldsPass(PriceField::Parse, processed_fields, &field_candidates, + ParseFormFieldsPass(PriceField::Parse, processed_fields, field_candidates, page_language, pattern_source, log_manager); // Name pass. - ParseFormFieldsPass(NameField::Parse, processed_fields, &field_candidates, + ParseFormFieldsPass(NameField::Parse, processed_fields, field_candidates, page_language, pattern_source, log_manager); // Search pass. - ParseFormFieldsPass(SearchField::Parse, processed_fields, &field_candidates, + ParseFormFieldsPass(SearchField::Parse, processed_fields, field_candidates, page_language, pattern_source, log_manager); + // Deduce `field_candidates` for the `processed_fields` by parsing their + // `parsable_name()` as an autocomplete attribute. + if (base::FeatureList::IsEnabled( + features::kAutofillParseNameAsAutocompleteType)) { + ParseUsingAutocompleteAttributes(processed_fields, field_candidates); + } + size_t fillable_fields = 0; - if (base::FeatureList::IsEnabled(features::kAutofillFixFillableFieldTypes)) { - for (const auto& [field_id, candidates] : field_candidates) { - if (IsFillableFieldType(candidates.BestHeuristicType())) - ++fillable_fields; - } - } else { - fillable_fields = field_candidates.size(); + for (const auto& [field_id, candidates] : field_candidates) { + if (IsFillableFieldType(candidates.BestHeuristicType())) + ++fillable_fields; } // Do not autofill a form if there aren't enough fields. Otherwise, it is // very easy to have false positives. See http://crbug.com/447332 // For <form> tags, make an exception for email fields, which are commonly // the only recognized field on account registration sites. Also make an - // exception for promo code fields, which are often a single field in its own - // form. + // exception for single-field Autofillable types, even when the form contains + // less than kMinRequiredFieldsForHeuristics fields in its form signature. if (fillable_fields < kMinRequiredFieldsForHeuristics) { - if ((is_form_tag && email_count > 0) || promo_code_count > 0) { + if ((is_form_tag && email_count > 0) || fillable_single_fields > 0) { base::EraseIf(field_candidates, [&](const auto& candidate) { return !(candidate.second.BestHeuristicType() == EMAIL_ADDRESS || - candidate.second.BestHeuristicType() == MERCHANT_PROMO_CODE); + FormField::IsSingleFieldParseableType( + candidate.second.BestHeuristicType())); }); } else { - if (log_manager) { - LogBuffer table_rows; - for (const auto& field : fields) { - table_rows << Tr{} << "Field:" << *field; - } - for (const auto& [field_id, candidates] : field_candidates) { - LogBuffer name; - name << "Type candidate for frame and renderer ID: " << field_id; - LogBuffer description; - ServerFieldType field_type = candidates.BestHeuristicType(); - description << "BestHeuristicType: " - << AutofillType::ServerFieldTypeToString(field_type) - << ", is fillable: " << IsFillableFieldType(field_type); - table_rows << Tr{} << std::move(name) << std::move(description); - } - log_manager->Log() - << LoggingScope::kParsing - << LogMessage::kLocalHeuristicDidNotFindEnoughFillableFields - << Tag{"table"} << Attrib{"class", "form"} << std::move(table_rows) - << CTag{"table"}; + LogBuffer table_rows(IsLoggingActive(log_manager)); + for (const auto& field : fields) + LOG_AF(table_rows) << Tr{} << "Field:" << *field; + for (const auto& [field_id, candidates] : field_candidates) { + LogBuffer name(IsLoggingActive(log_manager)); + name << "Type candidate for frame and renderer ID: " << field_id; + LogBuffer description(IsLoggingActive(log_manager)); + LOG_AF(description) + << "BestHeuristicType: " + << AutofillType::ServerFieldTypeToString( + candidates.BestHeuristicType()) + << ", is fillable: " + << IsFillableFieldType(candidates.BestHeuristicType()); + LOG_AF(table_rows) << Tr{} << std::move(name) << std::move(description); } + LOG_AF(log_manager) + << LoggingScope::kParsing + << LogMessage::kLocalHeuristicDidNotFindEnoughFillableFields + << Tag{"table"} << Attrib{"class", "form"} << std::move(table_rows) + << CTag{"table"}; field_candidates.clear(); } } - - return field_candidates; } -FieldCandidatesMap FormField::ParseFormFieldsForPromoCodes( +void FormField::ParseSingleFieldForms( const std::vector<std::unique_ptr<AutofillField>>& fields, const LanguageCode& page_language, bool is_form_tag, PatternSource pattern_source, + FieldCandidatesMap& field_candidates, LogManager* log_manager) { std::vector<AutofillField*> processed_fields = RemoveCheckableFields(fields); - FieldCandidatesMap field_candidates; // Merchant promo code pass. - ParseFormFieldsPass(MerchantPromoCodeField::Parse, processed_fields, - &field_candidates, page_language, pattern_source, - log_manager); + if (base::FeatureList::IsEnabled( + features::kAutofillParseMerchantPromoCodeFields)) { + ParseFormFieldsPass(MerchantPromoCodeField::Parse, processed_fields, + field_candidates, page_language, pattern_source, + log_manager); + } - return field_candidates; + // IBAN pass. + if (base::FeatureList::IsEnabled(features::kAutofillParseIBANFields)) { + ParseFormFieldsPass(IBANField::Parse, processed_fields, field_candidates, + page_language, pattern_source, log_manager); + } } // static @@ -244,6 +277,7 @@ bool FormField::ParseFieldSpecificsWithNewPatterns( return false; } +// static bool FormField::ParseFieldSpecifics( AutofillScanner* scanner, base::StringPiece16 pattern, @@ -260,6 +294,42 @@ bool FormField::ParseFieldSpecifics( } // static +bool FormField::ParseInAnyOrder( + AutofillScanner* scanner, + std::vector<std::pair<AutofillField**, base::RepeatingCallback<bool()>>> + fields_and_parsers) { + if (scanner->IsEnd()) + return fields_and_parsers.empty(); + auto original_pos = scanner->SaveCursor(); + // The implementation tries matching every permutation `p` of parsers with the + // scanners fields. While this has a terrible runtime for general n, the only + // planned use cases are dates (2 or 3 components). + // If necessary, bipartite matching could be used for general n. + DCHECK(fields_and_parsers.size() <= 3); + std::vector<int> p(fields_and_parsers.size()); + std::iota(p.begin(), p.end(), 0); + do { + bool matches = true; + for (int i : p) { + const auto& [field, parser] = fields_and_parsers[i]; + if (!scanner->IsEnd() && parser.Run()) { + *field = scanner->Cursor(); + scanner->Advance(); + } else { + matches = false; + break; + } + } + if (matches) + return true; + scanner->RewindTo(original_pos); + } while (std::next_permutation(p.begin(), p.end())); + for (const auto& [field, _] : fields_and_parsers) + *field = nullptr; + return false; +} + +// static bool FormField::ParseEmptyLabel(AutofillScanner* scanner, AutofillField** match) { return ParseFieldSpecificsWithLegacyPattern( @@ -272,12 +342,12 @@ bool FormField::ParseEmptyLabel(AutofillScanner* scanner, void FormField::AddClassification(const AutofillField* field, ServerFieldType type, float score, - FieldCandidatesMap* field_candidates) { + FieldCandidatesMap& field_candidates) { // Several fields are optional. if (field == nullptr) return; - FieldCandidates& candidates = (*field_candidates)[field->global_id()]; + FieldCandidates& candidates = field_candidates[field->global_id()]; candidates.AddFieldCandidate(type, score); } @@ -341,19 +411,21 @@ bool FormField::Match(const AutofillField* field, const bool match_label = match_type.attributes.contains(MatchAttribute::kLabel); - if (match_label && MatchesPattern(label, pattern, capture_destination)) { + if (match_label && + MatchesRegexWithCache(label, pattern, capture_destination)) { found_match = true; match_type_string = "Match in label"; value = label; } else if (match_type.attributes.contains(MatchAttribute::kName) && - MatchesPattern(name, pattern, capture_destination)) { + MatchesRegexWithCache(name, pattern, capture_destination)) { found_match = true; match_type_string = "Match in name"; value = name; } else if (match_label && base::FeatureList::IsEnabled( features::kAutofillConsiderPlaceholderForParsing) && - MatchesPattern(field->placeholder, pattern, capture_destination)) { + MatchesRegexWithCache(field->placeholder, pattern, + capture_destination)) { // TODO(crbug.com/1317961): The label and placeholder cases should logically // be grouped together. Placeholder is currently last, because for the finch // study we want the group assignment to happen as late as possible. @@ -363,15 +435,16 @@ bool FormField::Match(const AutofillField* field, value = field->placeholder; } - if (found_match && logging.log_manager) { - LogBuffer table_rows; - table_rows << Tr{} << "Match type:" << match_type_string; - table_rows << Tr{} << "RegEx:" << logging.regex_name; - table_rows << Tr{} << "Value: " << HighlightValue(value, matches[0]); + if (found_match) { + LogBuffer table_rows(IsLoggingActive(logging.log_manager)); + LOG_AF(table_rows) << Tr{} << "Match type:" << match_type_string; + LOG_AF(table_rows) << Tr{} << "RegEx:" << logging.regex_name; + LOG_AF(table_rows) << Tr{} + << "Value: " << HighlightValue(value, matches[0]); // The matched substring is reported once more as the highlighting is not // particularly copy&paste friendly. - table_rows << Tr{} << "Matched substring: " << matches[0]; - logging.log_manager->Log() + LOG_AF(table_rows) << Tr{} << "Matched substring: " << matches[0]; + LOG_AF(logging.log_manager) << LoggingScope::kParsing << LogMessage::kLocalHeuristicRegExMatched << Tag{"table"} << std::move(table_rows) << CTag{"table"}; } @@ -382,7 +455,7 @@ bool FormField::Match(const AutofillField* field, // static void FormField::ParseFormFieldsPass(ParseFunction parse, const std::vector<AutofillField*>& fields, - FieldCandidatesMap* field_candidates, + FieldCandidatesMap& field_candidates, const LanguageCode& page_language, PatternSource pattern_source, LogManager* log_manager) { @@ -430,4 +503,27 @@ bool FormField::MatchesFormControlType(base::StringPiece type, return false; } +// static +bool FormField::IsSingleFieldParseableType(ServerFieldType field_type) { + return field_type == MERCHANT_PROMO_CODE || field_type == IBAN_VALUE || + field_type == CREDIT_CARD_STANDALONE_VERIFICATION_CODE; +} + +// static +void FormField::ParseUsingAutocompleteAttributes( + const std::vector<AutofillField*>& fields, + FieldCandidatesMap& field_candidates) { + for (const AutofillField* field : fields) { + HtmlFieldType html_type = FieldTypeFromAutocompleteAttributeValue( + base::UTF16ToUTF8(field->parseable_name()), *field); + // The HTML_MODE is irrelevant when converting to a ServerFieldType. + ServerFieldType type = + AutofillType(html_type, HTML_MODE_NONE).GetStorableType(); + if (type != UNKNOWN_TYPE) { + AddClassification(field, type, kBaseAutocompleteParserScore, + field_candidates); + } + } +} + } // namespace autofill 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 12af574a32e..6cd50974db3 100644 --- a/chromium/components/autofill/core/browser/form_parsing/form_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/form_field.h @@ -7,8 +7,10 @@ #include <memory> #include <string> +#include <utility> #include <vector> +#include "base/callback.h" #include "base/gtest_prod_util.h" #include "base/memory/raw_ptr.h" #include "base/strings/string_piece.h" @@ -44,28 +46,45 @@ class FormField { virtual ~FormField() = default; // Classifies each field in |fields| with its heuristically detected type. - // Each field has a derived unique name that is used as the key into the - // returned FieldCandidatesMap. - static FieldCandidatesMap ParseFormFields( + // Each field has a derived unique name that is used as the key into + // |field_candidates|. + static void ParseFormFields( const std::vector<std::unique_ptr<AutofillField>>& fields, const LanguageCode& page_language, bool is_form_tag, PatternSource pattern_source, + FieldCandidatesMap& field_candidates, LogManager* log_manager = nullptr); - // Looks for a promo code field in |fields|. Each field has a derived unique - // name that is used as the key into the returned FieldCandidatesMap. - static FieldCandidatesMap ParseFormFieldsForPromoCodes( + // Looks for types that are allowed to appear in solitary (such as merchant + // promo codes) inside |fields|. Each field has a derived unique name that is + // used as the key into |field_candidates|. + static void ParseSingleFieldForms( const std::vector<std::unique_ptr<AutofillField>>& fields, const LanguageCode& page_language, bool is_form_tag, PatternSource pattern_source, + FieldCandidatesMap& field_candidates, LogManager* log_manager = nullptr); #if defined(UNIT_TEST) + static bool MatchForTesting(const AutofillField* field, + base::StringPiece16 pattern, + MatchParams match_type, + const RegExLogging& logging = {}) { + return FormField::Match(field, pattern, match_type, logging); + } + + static bool ParseInAnyOrderForTesting( + AutofillScanner* scanner, + std::vector<std::pair<AutofillField**, base::RepeatingCallback<bool()>>> + fields_and_parsers) { + return FormField::ParseInAnyOrder(scanner, fields_and_parsers); + } + // Assign types to the fields for the testing purposes. void AddClassificationsForTesting( - FieldCandidatesMap* field_candidates_for_testing) const { + FieldCandidatesMap& field_candidates_for_testing) const { AddClassifications(field_candidates_for_testing); } #endif @@ -74,23 +93,33 @@ class FormField { // Initial values assigned to FieldCandidates by their corresponding parsers. // There's an implicit precedence determined by the values assigned here. // Email is currently the most important followed by Phone, Travel, Address, - // Credit Card, Price, Name, Merchant promo code, and Search. + // Birthdate, Credit Card, IBAN, Price, Name, Merchant promo code, and Search. static constexpr float kBaseEmailParserScore = 1.4f; static constexpr float kBasePhoneParserScore = 1.3f; static constexpr float kBaseTravelParserScore = 1.2f; static constexpr float kBaseAddressParserScore = 1.1f; + static constexpr float kBaseBirthdateParserScore = 1.05f; static constexpr float kBaseCreditCardParserScore = 1.0f; + static constexpr float kBaseIBANParserScore = 0.975f; static constexpr float kBasePriceParserScore = 0.95f; static constexpr float kBaseNameParserScore = 0.9f; static constexpr float kBaseMerchantPromoCodeParserScore = 0.85f; static constexpr float kBaseSearchParserScore = 0.8f; + static constexpr float kBaseAutocompleteParserScore = 0.05f; // Only derived classes may instantiate. FormField() = default; + // Calls MatchesRegex() with a thread-safe cache. + // Should not be called from the UI thread as it may be blocked on a worker + // thread. + static bool MatchesRegexWithCache( + base::StringPiece16 input, + base::StringPiece16 pattern, + std::vector<std::u16string>* groups = nullptr); + // Attempts to parse a form field with the given pattern. Returns true on // success and fills |match| with a pointer to the field. - static bool ParseField(AutofillScanner* scanner, base::StringPiece16 pattern, base::span<const MatchPatternRef> patterns, @@ -111,29 +140,41 @@ class FormField { // on success and fills |match| with a pointer to the field. static bool ParseEmptyLabel(AutofillScanner* scanner, AutofillField** match); + // Attempts to parse several fields using the specified parsing functions in + // arbitrary order. This is useful e.g. when parsing dates, where both dd/mm + // and mm/dd makes sense. + // Returns true if all fields were parsed successfully. In this case, the + // fields are assigned with the matching ones. + // If no order is matched every parser, false is returned, all fields are + // reset to nullptr and the scanner is rewound to it's original position. + static bool ParseInAnyOrder( + AutofillScanner* scanner, + std::vector<std::pair<AutofillField**, base::RepeatingCallback<bool()>>> + fields_and_parsers); + // Adds an association between a |field| and a |type| into |field_candidates|. // This association is weighted by |score|, the higher the stronger the // association. static void AddClassification(const AutofillField* field, ServerFieldType type, float score, - FieldCandidatesMap* field_candidates); + FieldCandidatesMap& field_candidates); // Returns true iff |type| matches |match_type|. static bool MatchesFormControlType(base::StringPiece type, DenseSet<MatchFieldType> match_type); + // Returns true if |field_type| is a single field parseable type. + static bool IsSingleFieldParseableType(ServerFieldType field_type); + // Derived classes must implement this interface to supply field type // information. |ParseFormFields| coordinates the parsing and extraction // of types from an input vector of |AutofillField| objects and delegates // the type extraction via this method. virtual void AddClassifications( - FieldCandidatesMap* field_candidates) const = 0; + FieldCandidatesMap& field_candidates) const = 0; private: - FRIEND_TEST_ALL_PREFIXES(FormFieldTest, Match); - FRIEND_TEST_ALL_PREFIXES(FormFieldTest, TestParseableLabels); - // Function pointer type for the parsing function that should be passed to the // ParseFormFieldsPass() helper function. typedef std::unique_ptr<FormField> ParseFunction( @@ -190,10 +231,16 @@ class FormField { // |field_candidates|. static void ParseFormFieldsPass(ParseFunction parse, const std::vector<AutofillField*>& fields, - FieldCandidatesMap* field_candidates, + FieldCandidatesMap& field_candidates, const LanguageCode& page_language, PatternSource pattern_source, LogManager* log_manager); + + // Interpret the fields' `parsable_name()` (id or name attribute) as an + // autocomplete type and classify them by it. E.g. <input id=given-name>. + static void ParseUsingAutocompleteAttributes( + const std::vector<AutofillField*>& fields, + FieldCandidatesMap& field_candidates); }; } // namespace autofill 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 60c40e83b39..0199d105e3d 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 @@ -9,304 +9,303 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/browser/form_parsing/autofill_scanner.h" #include "components/autofill/core/browser/form_parsing/buildflags.h" #include "components/autofill/core/browser/form_parsing/form_field.h" +#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_payments_features.h" +#include "testing/gmock/include/gmock/gmock-matchers.h" #include "testing/gtest/include/gtest/gtest.h" -using autofill::features::kAutofillFixFillableFieldTypes; - namespace autofill { -namespace { -FieldRendererId MakeFieldRendererId() { - static uint64_t id_counter_ = 0; - return FieldRendererId(++id_counter_); -} - -// Sets both the field label and parseable label to |label|. -void SetFieldLabels(AutofillField* field, const std::u16string& label) { - field->label = label; - field->set_parseable_label(label); -} - -} // namespace - class FormFieldTest - : public testing::TestWithParam<std::tuple<bool, PatternSource>> { + : public FormFieldTestBase, + public ::testing::TestWithParam<PatternProviderFeatureState> { public: - FormFieldTest() { - scoped_feature_list_.InitWithFeatureState( - features::kAutofillParsingPatternProvider, - enable_parsing_pattern_provider()); - } + FormFieldTest() : FormFieldTestBase(GetParam()) {} FormFieldTest(const FormFieldTest&) = delete; FormFieldTest& operator=(const FormFieldTest&) = delete; - ~FormFieldTest() override = default; - bool enable_parsing_pattern_provider() const { - return std::get<0>(GetParam()); + protected: + // Parses all added fields using `ParseFormFields`. + // Returns the number of fields parsed. + int ParseFormFields() { + FormField::ParseFormFields(list_, LanguageCode(""), + /*is_form_tag=*/true, GetActivePatternSource(), + field_candidates_map_, + /*log_manager=*/nullptr); + return field_candidates_map_.size(); } - PatternSource pattern_source() const { return std::get<1>(GetParam()); } + // Like `ParseFormFields()`, but using `ParseSingleFieldForms()` instead. + int ParseSingleFieldForms() { + FormField::ParseSingleFieldForms( + list_, LanguageCode(""), + /*is_form_tag=*/true, GetActivePatternSource(), field_candidates_map_); + return field_candidates_map_.size(); + } - private: - base::test::ScopedFeatureList scoped_feature_list_; + // FormFieldTestBase: + // This function is unused in these unit tests, because FormField is not a + // parser itself, but the infrastructure combining them. + std::unique_ptr<FormField> Parse(AutofillScanner* scanner, + const LanguageCode& page_language) override { + return nullptr; + } +}; + +INSTANTIATE_TEST_SUITE_P( + FormFieldTest, + FormFieldTest, + ::testing::ValuesIn(PatternProviderFeatureState::All())); + +struct MatchTestCase { + std::u16string label; + std::vector<std::u16string> positive_patterns; + std::vector<std::u16string> negative_patterns; }; +class MatchTest : public testing::TestWithParam<MatchTestCase> {}; + +const MatchTestCase kMatchTestCases[]{ + // Empty strings match empty patterns, but not non-empty ones. + {u"", {u"", u"^$"}, {u"a"}}, + // Non-empty strings don't match empty patterns. + {u"a", {u""}, {u"^$"}}, + // Beginning and end of the line and exact matches. + {u"head_tail", + {u"^head", u"tail$", u"^head_tail$"}, + {u"head$", u"^tail", u"^head$", u"^tail$"}}, + // Escaped dots. + // Note: The unescaped "." characters are wild cards. + {u"m.i.", {u"m.i.", u"m\\.i\\."}}, + {u"mXiX", {u"m.i."}, {u"m\\.i\\."}}, + // Repetition. + {u"headtail", {u"head.*tail"}, {u"head.+tail"}}, + {u"headXtail", {u"head.*tail", u"head.+tail"}}, + {u"headXXXtail", {u"head.*tail", u"head.+tail"}}, + // Alternation. + {u"head_tail", {u"head|other", u"tail|other"}, {u"bad|good"}}, + // Case sensitivity. + {u"xxxHeAd_tAiLxxx", {u"head_tail"}}, + // Word boundaries. + {u"contains word:", {u"\\bword\\b"}, {u"\\bcon\\b"}}, + // Make sure the circumflex in 'crêpe' is not treated as a word boundary. + {u"crêpe", {}, {u"\\bcr\\b"}}}; + INSTANTIATE_TEST_SUITE_P(FormFieldTest, - FormFieldTest, - ::testing::Combine(::testing::Bool(), - ::testing::Values( -#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS) - PatternSource::kDefault, - PatternSource::kExperimental, - PatternSource::kNextGen, -#endif - PatternSource::kLegacy))); - -TEST_P(FormFieldTest, Match) { - constexpr MatchParams kMatchLabel{{MatchAttribute::kLabel}, {}}; + MatchTest, + testing::ValuesIn(kMatchTestCases)); +TEST_P(MatchTest, Match) { + const auto& [label, positive_patterns, negative_patterns] = GetParam(); + constexpr MatchParams kMatchLabel{{MatchAttribute::kLabel}, {}}; AutofillField field; - - // Empty strings match. - EXPECT_TRUE(FormField::Match(&field, std::u16string(), kMatchLabel)); - - // Empty pattern matches non-empty string. - SetFieldLabels(&field, u"a"); - EXPECT_TRUE(FormField::Match(&field, std::u16string(), kMatchLabel)); - - // Strictly empty pattern matches empty string. - SetFieldLabels(&field, u""); - EXPECT_TRUE(FormField::Match(&field, u"^$", kMatchLabel)); - - // Strictly empty pattern does not match non-empty string. - SetFieldLabels(&field, u"a"); - EXPECT_FALSE(FormField::Match(&field, u"^$", kMatchLabel)); - - // Non-empty pattern doesn't match empty string. - SetFieldLabels(&field, u""); - EXPECT_FALSE(FormField::Match(&field, u"a", kMatchLabel)); - - // Beginning of line. - SetFieldLabels(&field, u"head_tail"); - EXPECT_TRUE(FormField::Match(&field, u"^head", kMatchLabel)); - EXPECT_FALSE(FormField::Match(&field, u"^tail", kMatchLabel)); - - // End of line. - SetFieldLabels(&field, u"head_tail"); - EXPECT_FALSE(FormField::Match(&field, u"head$", kMatchLabel)); - EXPECT_TRUE(FormField::Match(&field, u"tail$", kMatchLabel)); - - // Exact. - SetFieldLabels(&field, u"head_tail"); - EXPECT_FALSE(FormField::Match(&field, u"^head$", kMatchLabel)); - EXPECT_FALSE(FormField::Match(&field, u"^tail$", kMatchLabel)); - EXPECT_TRUE(FormField::Match(&field, u"^head_tail$", kMatchLabel)); - - // Escaped dots. - SetFieldLabels(&field, u"m.i."); - // Note: This pattern is misleading as the "." characters are wild cards. - EXPECT_TRUE(FormField::Match(&field, u"m.i.", kMatchLabel)); - EXPECT_TRUE(FormField::Match(&field, u"m\\.i\\.", kMatchLabel)); - SetFieldLabels(&field, u"mXiX"); - EXPECT_TRUE(FormField::Match(&field, u"m.i.", kMatchLabel)); - EXPECT_FALSE(FormField::Match(&field, u"m\\.i\\.", kMatchLabel)); - - // Repetition. - SetFieldLabels(&field, u"headtail"); - EXPECT_TRUE(FormField::Match(&field, u"head.*tail", kMatchLabel)); - SetFieldLabels(&field, u"headXtail"); - EXPECT_TRUE(FormField::Match(&field, u"head.*tail", kMatchLabel)); - SetFieldLabels(&field, u"headXXXtail"); - EXPECT_TRUE(FormField::Match(&field, u"head.*tail", kMatchLabel)); - SetFieldLabels(&field, u"headtail"); - EXPECT_FALSE(FormField::Match(&field, u"head.+tail", kMatchLabel)); - SetFieldLabels(&field, u"headXtail"); - EXPECT_TRUE(FormField::Match(&field, u"head.+tail", kMatchLabel)); - SetFieldLabels(&field, u"headXXXtail"); - EXPECT_TRUE(FormField::Match(&field, u"head.+tail", kMatchLabel)); - - // Alternation. - SetFieldLabels(&field, u"head_tail"); - EXPECT_TRUE(FormField::Match(&field, u"head|other", kMatchLabel)); - EXPECT_TRUE(FormField::Match(&field, u"tail|other", kMatchLabel)); - EXPECT_FALSE(FormField::Match(&field, u"bad|good", kMatchLabel)); - - // Case sensitivity. - SetFieldLabels(&field, u"xxxHeAd_tAiLxxx"); - EXPECT_TRUE(FormField::Match(&field, u"head_tail", kMatchLabel)); - - // Word boundaries. - SetFieldLabels(&field, u"contains word:"); - EXPECT_TRUE(FormField::Match(&field, u"\\bword\\b", kMatchLabel)); - EXPECT_FALSE(FormField::Match(&field, u"\\bcon\\b", kMatchLabel)); - // Make sure the circumflex in 'crêpe' is not treated as a word boundary. - field.label = u"crêpe"; - EXPECT_FALSE(FormField::Match(&field, u"\\bcr\\b", kMatchLabel)); + SCOPED_TRACE("label = " + base::UTF16ToUTF8(label)); + field.label = label; + field.set_parseable_label(label); + for (const auto& pattern : positive_patterns) { + SCOPED_TRACE("positive_pattern = " + base::UTF16ToUTF8(pattern)); + EXPECT_TRUE(FormField::MatchForTesting(&field, pattern, kMatchLabel)); + } + for (const auto& pattern : negative_patterns) { + SCOPED_TRACE("negative_pattern = " + base::UTF16ToUTF8(pattern)); + EXPECT_FALSE(FormField::MatchForTesting(&field, pattern, kMatchLabel)); + } } // Test that we ignore checkable elements. -TEST_P(FormFieldTest, ParseFormFields) { - std::vector<std::unique_ptr<AutofillField>> fields; - FormFieldData field_data; - field_data.form_control_type = "text"; - - field_data.check_status = FormFieldData::CheckStatus::kCheckableButUnchecked; - field_data.label = u"Is PO Box"; - field_data.unique_renderer_id = MakeFieldRendererId(); - fields.push_back(std::make_unique<AutofillField>(field_data)); - - // Does not parse since there are only field and it's checkable. - // An empty page_language means the language is unknown and patterns of all - // languages are used. - EXPECT_TRUE(FormField::ParseFormFields(fields, LanguageCode(""), - /*is_form_tag=*/true, pattern_source(), - /*log_manager=*/nullptr) - .empty()); - - // reset |is_checkable| to false. - field_data.check_status = FormFieldData::CheckStatus::kNotCheckable; - field_data.label = u"Address line1"; - field_data.unique_renderer_id = MakeFieldRendererId(); - fields.push_back(std::make_unique<AutofillField>(field_data)); - - // Parse a single address line 1 field. - ASSERT_EQ(0u, - FormField::ParseFormFields(fields, LanguageCode(""), - /*is_form_tag=*/true, pattern_source(), - /*log_manager=*/nullptr) - .size()); - - // Parses address line 1 and 2. - field_data.label = u"Address line2"; - field_data.unique_renderer_id = MakeFieldRendererId(); - fields.push_back(std::make_unique<AutofillField>(field_data)); - - // An empty page_language means the language is unknown and patterns of - // all languages are used. - ASSERT_EQ(0u, - FormField::ParseFormFields(fields, LanguageCode(""), - /*is_form_tag=*/true, pattern_source(), - /*log_manager=*/nullptr) - .size()); +TEST_P(FormFieldTest, ParseFormFieldsIgnoreCheckableElements) { + AddFormFieldData("checkbox", "", "Is PO Box", UNKNOWN_TYPE); + // Add 3 dummy fields to reach kMinRequiredFieldsForHeuristics = 3. + AddTextFormFieldData("", "Address line 1", ADDRESS_HOME_LINE1); + AddTextFormFieldData("", "Address line 2", ADDRESS_HOME_LINE2); + AddTextFormFieldData("", "Address line 3", ADDRESS_HOME_LINE3); + EXPECT_EQ(3, ParseFormFields()); + TestClassificationExpectations(); } // Test that the minimum number of required fields for the heuristics considers // whether a field is actually fillable. -TEST_P(FormFieldTest, ParseFormFieldEnforceMinFillableFields) { - std::vector<std::unique_ptr<AutofillField>> fields; - FormFieldData field_data; - field_data.form_control_type = "text"; - - field_data.label = u"Address line 1"; - field_data.unique_renderer_id = MakeFieldRendererId(); - fields.push_back(std::make_unique<AutofillField>(field_data)); - - field_data.label = u"Address line 2"; - field_data.unique_renderer_id = MakeFieldRendererId(); - fields.push_back(std::make_unique<AutofillField>(field_data)); - - // Don't parse forms with 2 fields. - // An empty page_language means the language is unknown and patterns of all - // languages are used. - EXPECT_EQ(0u, - FormField::ParseFormFields(fields, LanguageCode(""), - /*is_form_tag=*/true, pattern_source(), - /*log_manager=*/nullptr) - .size()); - - field_data.label = u"Search"; - field_data.unique_renderer_id = MakeFieldRendererId(); - fields.push_back(std::make_unique<AutofillField>(field_data)); - - // Before the fix in kAutofillFixFillableFieldTypes, we would parse the form - // now, although a search field is not fillable. - { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature(kAutofillFixFillableFieldTypes); - // An empty page_language means the language is unknown and patterns of all - // languages are used. - EXPECT_EQ(3u, FormField::ParseFormFields( - fields, LanguageCode(""), /*is_form_tag=*/true, - pattern_source(), /*log_manager=*/nullptr) - .size()); - } +TEST_P(FormFieldTest, ParseFormFieldsEnforceMinFillableFields) { + AddTextFormFieldData("", "Address line 1", ADDRESS_HOME_LINE1); + AddTextFormFieldData("", "Address line 2", ADDRESS_HOME_LINE2); + AddTextFormFieldData("", "Search", SEARCH_TERM); + // We don't parse the form because search fields are not fillable (therefore, + // the form has only 2 fillable fields). + EXPECT_EQ(0, ParseFormFields()); +} - // With the fix, we don't parse the form because search fields are not - // fillable (therefore, the form has only 2 fillable fields). - { - base::test::ScopedFeatureList feature_list; - 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 = FormField::ParseFormFields( - fields, LanguageCode(""), /*is_form_tag=*/true, pattern_source(), - /*log_manager=*/nullptr); - EXPECT_EQ(0u, FormField::ParseFormFields( - fields, LanguageCode(""), /*is_form_tag=*/true, - pattern_source(), /*log_manager=*/nullptr) - .size()); - } +// Tests that the `parseable_name()` is parsed as an autocomplete type. +TEST_P(FormFieldTest, ParseNameAsAutocompleteType) { + base::test::ScopedFeatureList autocomplete_feature; + autocomplete_feature.InitAndEnableFeature( + features::kAutofillParseNameAsAutocompleteType); + + AddTextFormFieldData("given-name", "", NAME_FIRST); + AddTextFormFieldData("family-name", "", NAME_LAST); + AddTextFormFieldData("cc-exp-month", "", CREDIT_CARD_EXP_MONTH); + // The label is not parsed as an autocomplete type. + AddTextFormFieldData("", "cc-exp-month", UNKNOWN_TYPE); + EXPECT_EQ(3, ParseFormFields()); + TestClassificationExpectations(); } // Test that the parseable label is used when the feature is enabled. TEST_P(FormFieldTest, TestParseableLabels) { - FormFieldData field_data; - field_data.form_control_type = "text"; - - field_data.label = u"not a parseable label"; - field_data.unique_renderer_id = MakeFieldRendererId(); - auto autofill_field = std::make_unique<AutofillField>(field_data); + AddTextFormFieldData("", "not a parseable label", UNKNOWN_TYPE); + AutofillField* autofill_field = list_.back().get(); autofill_field->set_parseable_label(u"First Name"); + + constexpr MatchParams kMatchLabel{{MatchAttribute::kLabel}, {}}; { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( features::kAutofillEnableSupportForParsingWithSharedLabels); - EXPECT_TRUE(FormField::Match(autofill_field.get(), u"First Name", - MatchParams({MatchAttribute::kLabel}, {}))); + EXPECT_TRUE( + FormField::MatchForTesting(autofill_field, u"First Name", kMatchLabel)); } { base::test::ScopedFeatureList feature_list; feature_list.InitAndDisableFeature( features::kAutofillEnableSupportForParsingWithSharedLabels); - EXPECT_FALSE(FormField::Match(autofill_field.get(), u"First Name", - MatchParams({MatchAttribute::kLabel}, {}))); + EXPECT_FALSE( + FormField::MatchForTesting(autofill_field, u"First Name", kMatchLabel)); } } -// Test that |ParseFormFieldsForPromoCodes| parses single field promo codes. -TEST_P(FormFieldTest, ParseFormFieldsForPromoCodes) { +// Tests that `ParseSingleFieldForms` is called as part of `ParseFormFields`. +TEST_P(FormFieldTest, ParseSingleFieldFormsInsideParseFormField) { base::test::ScopedFeatureList scoped_feature; scoped_feature.InitAndEnableFeature( features::kAutofillParseMerchantPromoCodeFields); - std::vector<std::unique_ptr<AutofillField>> fields; - FormFieldData field_data; - field_data.form_control_type = "text"; + AddTextFormFieldData("", "Phone", PHONE_HOME_WHOLE_NUMBER); + AddTextFormFieldData("", "Email", EMAIL_ADDRESS); + AddTextFormFieldData("", "Promo code", MERCHANT_PROMO_CODE); + + // `ParseSingleFieldForms` should detect the promo code. + EXPECT_EQ(3, ParseFormFields()); + TestClassificationExpectations(); +} + +// Test that `ParseSingleFieldForms` parses single field promo codes. +TEST_P(FormFieldTest, ParseFormFieldsForSingleFieldPromoCode) { + base::test::ScopedFeatureList scoped_feature; + scoped_feature.InitAndEnableFeature( + features::kAutofillParseMerchantPromoCodeFields); // Parse single field promo code. - field_data.label = u"Promo code"; - field_data.unique_renderer_id = MakeFieldRendererId(); - fields.push_back(std::make_unique<AutofillField>(field_data)); + AddTextFormFieldData("", "Promo code", MERCHANT_PROMO_CODE); + EXPECT_EQ(1, ParseSingleFieldForms()); + TestClassificationExpectations(); - EXPECT_EQ( - 1u, FormField::ParseFormFieldsForPromoCodes( - fields, LanguageCode(""), /*is_form_tag=*/true, pattern_source()) - .size()); + // Don't parse other fields. + // UNKNOWN_TYPE is used as the expected type, which prevents it from being + // part of the expectations in `TestClassificationExpectations()`. + AddTextFormFieldData("", "Address line 1", UNKNOWN_TYPE); + EXPECT_EQ(1, ParseSingleFieldForms()); + TestClassificationExpectations(); +} + +// Test that `ParseSingleFieldForms` parses single field IBAN. +TEST_P(FormFieldTest, ParseSingleFieldFormsIban) { + base::test::ScopedFeatureList scoped_feature; + scoped_feature.InitAndEnableFeature(features::kAutofillParseIBANFields); + + // Parse single field IBAN. + AddTextFormFieldData("", "IBAN", IBAN_VALUE); + EXPECT_EQ(1, ParseSingleFieldForms()); + TestClassificationExpectations(); // Don't parse other fields. - field_data.label = u"Address line 1"; - field_data.unique_renderer_id = MakeFieldRendererId(); - fields.push_back(std::make_unique<AutofillField>(field_data)); - - // Still only the promo code field should be parsed. - EXPECT_EQ( - 1u, FormField::ParseFormFieldsForPromoCodes( - fields, LanguageCode(""), /*is_form_tag=*/true, pattern_source()) - .size()); + // UNKNOWN_TYPE is used as the expected type, which prevents it from being + // part of the expectations in `TestClassificationExpectations()`. + AddTextFormFieldData("", "Address line 1", UNKNOWN_TYPE); + EXPECT_EQ(1, ParseSingleFieldForms()); + TestClassificationExpectations(); +} + +struct ParseInAnyOrderTestcase { + // An nxn matrix, describing that field i is matched by parser j. + std::vector<std::vector<bool>> field_matches_parser; + // The expected order in which the n fields are matched, or empty, if the + // matching is expected to fail. + std::vector<int> expected_permutation; +}; + +class ParseInAnyOrderTest + : public testing::TestWithParam<ParseInAnyOrderTestcase> {}; + +const ParseInAnyOrderTestcase kParseInAnyOrderTestcases[]{ + // Field i is only matched by parser i -> matched in order. + {{{true, false, false}, {false, true, false}, {false, false, true}}, + {0, 1, 2}}, + // Opposite order. + {{{false, true}, {true, false}}, {1, 0}}, + // The second field has to go first, because it is only matched by the first + // parser. + {{{true, true}, {true, false}}, {1, 0}}, + // The second parser doesn't match any field, thus no match. + {{{true, false}, {true, false}}, {}}, + // No field matches. + {{{false, false}, {false, false}}, {}}}; + +INSTANTIATE_TEST_SUITE_P(FormFieldTest, + ParseInAnyOrderTest, + testing::ValuesIn(kParseInAnyOrderTestcases)); + +TEST_P(ParseInAnyOrderTest, ParseInAnyOrder) { + auto testcase = GetParam(); + bool expect_success = !testcase.expected_permutation.empty(); + size_t n = testcase.field_matches_parser.size(); + + std::vector<std::unique_ptr<AutofillField>> fields; + // Creates n fields and encodes their ids in `max_length`, as `id_attribute` + // is a string. + for (size_t i = 0; i < n; i++) { + FormFieldData form_field_data; + form_field_data.max_length = i; + fields.push_back(std::make_unique<AutofillField>(form_field_data)); + } + + // Checks if `matching_ids` of the `scanner`'s current position is true. + // This is used to simulate different parsers, as described by + // `testcase.field_matches_parser`. + auto Matches = [](AutofillScanner* scanner, + const std::vector<bool>& matching_ids) -> bool { + return matching_ids[scanner->Cursor()->max_length]; + }; + + // Construct n parsers from `testcase.field_matches_parser`. + AutofillScanner scanner(fields); + std::vector<AutofillField*> matched_fields(n); + std::vector<std::pair<AutofillField**, base::RepeatingCallback<bool()>>> + fields_and_parsers; + for (size_t i = 0; i < n; i++) { + fields_and_parsers.emplace_back( + &matched_fields[i], + base::BindRepeating(Matches, &scanner, + testcase.field_matches_parser[i])); + } + + EXPECT_EQ(FormField::ParseInAnyOrderForTesting(&scanner, fields_and_parsers), + expect_success); + + if (expect_success) { + EXPECT_TRUE(scanner.IsEnd()); + ASSERT_EQ(testcase.expected_permutation.size(), n); + for (size_t i = 0; i < n; i++) { + EXPECT_EQ(matched_fields[i], + fields[testcase.expected_permutation[i]].get()); + } + } else { + EXPECT_EQ(scanner.CursorPosition(), 0u); + EXPECT_THAT(matched_fields, ::testing::Each(nullptr)); + } } + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/iban_field.cc b/chromium/components/autofill/core/browser/form_parsing/iban_field.cc new file mode 100644 index 00000000000..deff52803d1 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/iban_field.cc @@ -0,0 +1,42 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/form_parsing/iban_field.h" + +#include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/common/autofill_regex_constants.h" +#include "components/autofill/core/browser/form_parsing/autofill_scanner.h" +#include "components/autofill/core/common/autofill_payments_features.h" + +namespace autofill { + +// static +std::unique_ptr<FormField> IBANField::Parse(AutofillScanner* scanner, + const LanguageCode& page_language, + PatternSource pattern_source, + LogManager* log_manager) { + if (!base::FeatureList::IsEnabled(features::kAutofillParseIBANFields)) + return nullptr; + + AutofillField* field; + base::span<const MatchPatternRef> iban_patterns = + GetMatchPatterns(IBAN_VALUE, page_language, pattern_source); + + if (ParseFieldSpecifics(scanner, kIBANRe, + kDefaultMatchParamsWith<MatchFieldType::kNumber, + MatchFieldType::kTextArea>, + iban_patterns, &field, {log_manager, "kIBANRe"})) { + return std::make_unique<IBANField>(field); + } + + return nullptr; +} + +IBANField::IBANField(const AutofillField* field) : field_(field) {} + +void IBANField::AddClassifications(FieldCandidatesMap& field_candidates) const { + AddClassification(field_, IBAN_VALUE, kBaseIBANParserScore, field_candidates); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/iban_field.h b/chromium/components/autofill/core/browser/form_parsing/iban_field.h new file mode 100644 index 00000000000..d8b433961b2 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/iban_field.h @@ -0,0 +1,42 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_IBAN_FIELD_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_IBAN_FIELD_H_ + +#include <memory> + +#include "base/memory/raw_ptr.h" +#include "components/autofill/core/browser/form_parsing/form_field.h" +#include "components/autofill/core/common/language_code.h" + +namespace autofill { + +class AutofillField; +class AutofillScanner; +class LogManager; + +// A form field that accepts International Bank Account Number (IBAN). +class IBANField : public FormField { + public: + static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, + const LanguageCode& page_language, + PatternSource pattern_source, + LogManager* log_manager); + + explicit IBANField(const AutofillField* field); + + IBANField(const IBANField&) = delete; + IBANField& operator=(const IBANField&) = delete; + + protected: + void AddClassifications(FieldCandidatesMap& field_candidates) const override; + + private: + raw_ptr<const AutofillField> field_; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_IBAN_FIELD_H_ diff --git a/chromium/components/autofill/core/browser/form_parsing/iban_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/iban_field_unittest.cc new file mode 100644 index 00000000000..f5a39d16071 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/iban_field_unittest.cc @@ -0,0 +1,68 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/form_parsing/iban_field.h" + +#include "base/test/scoped_feature_list.h" +#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h" +#include "components/autofill/core/common/autofill_payments_features.h" + +namespace autofill { + +class IBANFieldTest + : public FormFieldTestBase, + public testing::TestWithParam<PatternProviderFeatureState> { + public: + IBANFieldTest() : FormFieldTestBase(GetParam()) {} + IBANFieldTest(const IBANFieldTest&) = delete; + IBANFieldTest& operator=(const IBANFieldTest&) = delete; + + void SetUp() override { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillParseIBANFields); + } + + protected: + std::unique_ptr<FormField> Parse( + AutofillScanner* scanner, + const LanguageCode& page_language = LanguageCode("en")) override { + return IBANField::Parse(scanner, page_language, GetActivePatternSource(), + /*log_manager=*/nullptr); + } + + base::test::ScopedFeatureList scoped_feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P( + IBANFieldTest, + IBANFieldTest, + ::testing::ValuesIn(PatternProviderFeatureState::All())); + +// Match IBAN +TEST_P(IBANFieldTest, ParseIban) { + AddTextFormFieldData("iban-field", "Enter account number", IBAN_VALUE); + + ClassifyAndVerify(ParseResult::PARSED); +} + +TEST_P(IBANFieldTest, ParseIbanBanks) { + AddTextFormFieldData("accountNumber", "IBAN*", IBAN_VALUE); + + ClassifyAndVerify(ParseResult::PARSED); +} + +TEST_P(IBANFieldTest, ParseNonIban) { + AddTextFormFieldData("other-field", "Field for Account Number", UNKNOWN_TYPE); + + ClassifyAndVerify(ParseResult::NOT_PARSED); +} + +TEST_P(IBANFieldTest, ParseIbanFlagOff) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndDisableFeature(features::kAutofillParseIBANFields); + AddTextFormFieldData("iban-field", "Enter IBAN here", IBAN_VALUE); + + ClassifyAndVerify(ParseResult::NOT_PARSED); +} +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc index aa18677c381..14d755d8bfd 100644 --- a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.cc @@ -5,9 +5,9 @@ #include "components/autofill/core/browser/form_parsing/merchant_promo_code_field.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_payments_features.h" +#include "components/autofill/core/common/autofill_regex_constants.h" namespace autofill { @@ -41,7 +41,7 @@ MerchantPromoCodeField::MerchantPromoCodeField(const AutofillField* field) : field_(field) {} void MerchantPromoCodeField::AddClassifications( - FieldCandidatesMap* field_candidates) const { + FieldCandidatesMap& field_candidates) const { AddClassification(field_, MERCHANT_PROMO_CODE, kBaseMerchantPromoCodeParserScore, field_candidates); } diff --git a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h index 1b918aea03f..61e02bad6c7 100644 --- a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field.h @@ -33,7 +33,7 @@ class MerchantPromoCodeField : public FormField { MerchantPromoCodeField& operator=(const MerchantPromoCodeField&) = delete; protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: FRIEND_TEST_ALL_PREFIXES(MerchantPromoCodeFieldTest, ParsePromoCode); diff --git a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc index f16d20fe52e..5edb5687e25 100644 --- a/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/merchant_promo_code_field_unittest.cc @@ -45,7 +45,7 @@ INSTANTIATE_TEST_SUITE_P( // Match promo(tion|tional)?[-_. ]*code TEST_P(MerchantPromoCodeFieldTest, ParsePromoCode) { - AddTextFormFieldData("Enter promo code here", "promoCodeField", + AddTextFormFieldData("promoCodeField", "Enter promo code here", MERCHANT_PROMO_CODE); ClassifyAndVerify(ParseResult::PARSED); @@ -53,7 +53,7 @@ TEST_P(MerchantPromoCodeFieldTest, ParsePromoCode) { // Match promo(tion|tional)?[-_. ]*code TEST_P(MerchantPromoCodeFieldTest, ParsePromotionalCode) { - AddTextFormFieldData("Use the promotional code here", "promoCodeField", + AddTextFormFieldData("promoCodeField", "Use the promotional code here", MERCHANT_PROMO_CODE); ClassifyAndVerify(ParseResult::PARSED); @@ -69,7 +69,7 @@ TEST_P(MerchantPromoCodeFieldTest, ParsePromoCodeWithPrefixAndSuffix) { // Match coupon[-_. ]*code TEST_P(MerchantPromoCodeFieldTest, ParseCouponCode) { - AddTextFormFieldData("Enter new coupon__code", "couponCodeField", + AddTextFormFieldData("couponCodeField", "Enter new coupon__code", MERCHANT_PROMO_CODE); ClassifyAndVerify(ParseResult::PARSED); @@ -77,7 +77,7 @@ TEST_P(MerchantPromoCodeFieldTest, ParseCouponCode) { // Match gift[-_. ]*code TEST_P(MerchantPromoCodeFieldTest, ParseGiftCode) { - AddTextFormFieldData("Check out with gift.codes", "giftCodeField", + AddTextFormFieldData("giftCodeField", "Check out with gift.codes", MERCHANT_PROMO_CODE); ClassifyAndVerify(ParseResult::PARSED); @@ -85,7 +85,7 @@ TEST_P(MerchantPromoCodeFieldTest, ParseGiftCode) { // Match discount[-_. ]*code TEST_P(MerchantPromoCodeFieldTest, ParseDiscountCode) { - AddTextFormFieldData("Check out with discount-code", "discountCodeField", + AddTextFormFieldData("discountCodeField", "Check out with discount-code", MERCHANT_PROMO_CODE); ClassifyAndVerify(ParseResult::PARSED); @@ -93,7 +93,7 @@ TEST_P(MerchantPromoCodeFieldTest, ParseDiscountCode) { TEST_P(MerchantPromoCodeFieldTest, ParseNonPromoCode) { // Regex relies on "promo/coupon/gift" + "code" together. - AddTextFormFieldData("Field for gift card or promo details", "otherField", + AddTextFormFieldData("otherField", "Field for gift card or promo details", UNKNOWN_TYPE); ClassifyAndVerify(ParseResult::NOT_PARSED); @@ -103,7 +103,7 @@ TEST_P(MerchantPromoCodeFieldTest, ParsePromoCodeFlagOff) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndDisableFeature( features::kAutofillParseMerchantPromoCodeFields); - AddTextFormFieldData("Enter promo code here", "promoCodeField", + AddTextFormFieldData("promoCodeField", "Enter promo code here", MERCHANT_PROMO_CODE); ClassifyAndVerify(ParseResult::NOT_PARSED); 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 858750b27bb..3aeb7204c9e 100644 --- a/chromium/components/autofill/core/browser/form_parsing/name_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/name_field.cc @@ -10,11 +10,11 @@ #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" #include "base/strings/string_util.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/browser/form_parsing/regex_patterns.h" #include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_regex_constants.h" namespace autofill { namespace { @@ -32,7 +32,7 @@ class FullNameField : public NameField { FullNameField& operator=(const FullNameField&) = delete; protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: raw_ptr<AutofillField> field_; @@ -57,7 +57,7 @@ class FirstTwoLastNamesField : public NameField { FirstTwoLastNamesField& operator=(const FirstTwoLastNamesField&) = delete; protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: FirstTwoLastNamesField(); @@ -111,7 +111,7 @@ class FirstLastNameField : public NameField { FirstLastNameField& operator=(const FirstLastNameField&) = delete; protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: FirstLastNameField(); @@ -152,8 +152,8 @@ std::unique_ptr<FormField> NameField::Parse(AutofillScanner* scanner, return field; } -// This is overriden in concrete subclasses. -void NameField::AddClassifications(FieldCandidatesMap* field_candidates) const { +// This is overridden in concrete subclasses. +void NameField::AddClassifications(FieldCandidatesMap& field_candidates) const { } // static @@ -192,7 +192,7 @@ std::unique_ptr<FullNameField> FullNameField::Parse( } void FullNameField::AddClassifications( - FieldCandidatesMap* field_candidates) const { + FieldCandidatesMap& field_candidates) const { AddClassification(field_, NAME_FULL, kBaseNameParserScore, field_candidates); } @@ -303,7 +303,7 @@ FirstTwoLastNamesField::ParseComponentNames(AutofillScanner* scanner, } void FirstTwoLastNamesField::AddClassifications( - FieldCandidatesMap* field_candidates) const { + FieldCandidatesMap& field_candidates) const { AddClassification(honorific_prefix_, NAME_HONORIFIC_PREFIX, kBaseNameParserScore, field_candidates); AddClassification(first_name_, NAME_FIRST, kBaseNameParserScore, @@ -539,7 +539,7 @@ std::unique_ptr<FirstLastNameField> FirstLastNameField::Parse( FirstLastNameField::FirstLastNameField() = default; void FirstLastNameField::AddClassifications( - FieldCandidatesMap* field_candidates) const { + FieldCandidatesMap& field_candidates) const { AddClassification(honorific_prefix_, NAME_HONORIFIC_PREFIX, kBaseNameParserScore, field_candidates); AddClassification(first_name_, NAME_FIRST, kBaseNameParserScore, 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 d3b23f5c5da..5abd312f09a 100644 --- a/chromium/components/autofill/core/browser/form_parsing/name_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/name_field.h @@ -32,7 +32,7 @@ class NameField : public FormField { protected: NameField() = default; - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; }; } // 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 db913d825ae..08855900810 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 @@ -8,10 +8,10 @@ #include <vector> #include "base/test/scoped_feature_list.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/parsing_test_utils.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" namespace autofill { @@ -39,9 +39,9 @@ INSTANTIATE_TEST_SUITE_P( ::testing::ValuesIn(PatternProviderFeatureState::All())); TEST_P(NameFieldTest, FirstMiddleLast) { - AddTextFormFieldData("First Name", "First", NAME_FIRST); - AddTextFormFieldData("Name Middle", "Middle", NAME_MIDDLE); - AddTextFormFieldData("Last Name", "Last", NAME_LAST); + AddTextFormFieldData("First", "First Name", NAME_FIRST); + AddTextFormFieldData("Middle", "Name Middle", NAME_MIDDLE); + AddTextFormFieldData("Last", "Last Name", NAME_LAST); ClassifyAndVerify(ParseResult::PARSED); } @@ -185,9 +185,9 @@ TEST_P(NameFieldTest, FirstNameAndOptionalMiddleNameAndHispanicLastNames) { features::kAutofillEnableSupportForMoreStructureInNames); AddTextFormFieldData("nombre", "nombre", NAME_FIRST); - AddTextFormFieldData("middle name", "middle_name", NAME_MIDDLE); - AddTextFormFieldData("apellido paterno", "apellido_paterno", NAME_LAST_FIRST); - AddTextFormFieldData("segunda apellido", "segunda_apellido", + AddTextFormFieldData("middle_name", "middle name", NAME_MIDDLE); + AddTextFormFieldData("apellido_paterno", "apellido paterno", NAME_LAST_FIRST); + AddTextFormFieldData("segunda_apellido", "segunda apellido", NAME_LAST_SECOND); ClassifyAndVerify(ParseResult::PARSED); @@ -221,18 +221,18 @@ TEST_P(NameFieldTest, HispanicLastNameRegexConverage) { for (const auto& string : first_last_name_strings) { SCOPED_TRACE(string); - EXPECT_TRUE(MatchesPattern(string, kNameLastFirstRe, nullptr)); + EXPECT_TRUE(MatchesRegex<kNameLastFirstRe>(string)); } for (const auto& string : second_last_name_strings) { SCOPED_TRACE(string); - EXPECT_TRUE(MatchesPattern(string, kNameLastSecondRe, nullptr)); + EXPECT_TRUE(MatchesRegex<kNameLastSecondRe>(string)); } for (const auto& string : neither_first_or_second_last_name_strings) { SCOPED_TRACE(string); - EXPECT_FALSE(MatchesPattern(string, kNameLastFirstRe, nullptr)); - EXPECT_FALSE(MatchesPattern(string, kNameLastSecondRe, nullptr)); + EXPECT_FALSE(MatchesRegex<kNameLastFirstRe>(string)); + EXPECT_FALSE(MatchesRegex<kNameLastSecondRe>(string)); } } diff --git a/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc b/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc index e4930640356..8089ceab8e9 100644 --- a/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc +++ b/chromium/components/autofill/core/browser/form_parsing/parsing_test_utils.cc @@ -105,38 +105,28 @@ void FormFieldTestBase::ClassifyAndVerify(ParseResult parse_result, return; } ASSERT_NE(nullptr, field_.get()); - field_->AddClassificationsForTesting(&field_candidates_map_); + field_->AddClassificationsForTesting(field_candidates_map_); TestClassificationExpectations(); } void FormFieldTestBase::TestClassificationExpectations() { - for (const auto [field_id, field_type] : expected_classifications_) { - if (field_type != UNKNOWN_TYPE) { - SCOPED_TRACE(testing::Message() - << "Found type " - << AutofillType::ServerFieldTypeToString( - field_candidates_map_[field_id].BestHeuristicType()) - << ", expected type " - << AutofillType::ServerFieldTypeToString(field_type)); - - ASSERT_TRUE(field_candidates_map_.find(field_id) != - field_candidates_map_.end()); - EXPECT_EQ(field_type, - field_candidates_map_[field_id].BestHeuristicType()); - } else { - SCOPED_TRACE( - testing::Message() - << "Expected type UNKNOWN_TYPE but got " - << AutofillType::ServerFieldTypeToString( - field_candidates_map_.find(field_id) != - field_candidates_map_.end() - ? field_candidates_map_[field_id].BestHeuristicType() - : UNKNOWN_TYPE)); - EXPECT_EQ(field_candidates_map_.find(field_id), - field_candidates_map_.end()); - } + size_t num_classifications = 0; + for (const auto [field_id, expected_field_type] : expected_classifications_) { + ServerFieldType actual_field_type = + field_candidates_map_.contains(field_id) + ? field_candidates_map_[field_id].BestHeuristicType() + : UNKNOWN_TYPE; + SCOPED_TRACE(testing::Message() + << "Found type " + << AutofillType::ServerFieldTypeToString(actual_field_type) + << ", expected type " + << AutofillType::ServerFieldTypeToString(expected_field_type)); + EXPECT_EQ(expected_field_type, actual_field_type); + num_classifications += expected_field_type != UNKNOWN_TYPE; } + // There shouldn't be any classifications for other fields. + EXPECT_EQ(num_classifications, field_candidates_map_.size()); } FieldRendererId FormFieldTestBase::MakeFieldRendererId() { 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 523d186dcb0..0c6cabf3004 100644 --- a/chromium/components/autofill/core/browser/form_parsing/phone_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/phone_field.cc @@ -16,11 +16,12 @@ #include "base/strings/strcat.h" #include "base/strings/string_util.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/form_parsing/regex_patterns.h" +#include "components/autofill/core/browser/metrics/autofill_metrics.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 { @@ -57,81 +58,76 @@ std::u16string GetAreaRegex() { // The following notation is used to describe the patterns: // <cc> - country code field. // <ac> - area code field. +// TODO(crbug.com/1348137): Add a separate prefix type. // <phone> - phone or prefix. // <suffix> - suffix. -// <ext> - extension. // :N means field is limited to N characters, otherwise it is unlimited. // (pattern <field>)? means pattern is optional and matched separately. // static const std::vector<PhoneField::PhoneGrammar>& PhoneField::GetPhoneGrammars() { static const base::NoDestructor<std::vector<PhoneGrammar>> grammars({ - // Country code: <cc> Area Code: <ac> Phone: <phone> (- <suffix> - // (Ext: <ext>)?)? + // Country code: <cc> Area Code: <ac> Phone: <phone> {{REGEX_COUNTRY, FIELD_COUNTRY_CODE}, {REGEX_AREA, FIELD_AREA_CODE}, {REGEX_PHONE, FIELD_PHONE}}, - // \( <ac> \) <phone>:3 <suffix>:4 (Ext: <ext>)? + // \( <ac> \) <phone>:3 <suffix>:4 {{REGEX_AREA_NOTEXT, FIELD_AREA_CODE, 3}, {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3}, {REGEX_PHONE, FIELD_SUFFIX, 4}}, - // Phone: <cc> <ac>:3 - <phone>:3 - <suffix>:4 (Ext: <ext>)? + // Phone: <cc> <ac>:3 - <phone>:3 - <suffix>:4 {{REGEX_PHONE, FIELD_COUNTRY_CODE}, {REGEX_PHONE, FIELD_AREA_CODE, 3}, {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3}, {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 4}}, - // Phone: <cc>:3 <ac>:3 <phone>:3 <suffix>:4 (Ext: <ext>)? + // Phone: <cc>:3 <ac>:3 <phone>:3 <suffix>:4 {{REGEX_PHONE, FIELD_COUNTRY_CODE, 3}, {REGEX_PHONE, FIELD_AREA_CODE, 3}, {REGEX_PHONE, FIELD_PHONE, 3}, {REGEX_PHONE, FIELD_SUFFIX, 4}}, - // Area Code: <ac> Phone: <phone> (- <suffix> (Ext: <ext>)?)? + // Area Code: <ac> Phone: <phone> {{REGEX_AREA, FIELD_AREA_CODE}, {REGEX_PHONE, FIELD_PHONE}}, - // Phone: <ac> <phone>:3 <suffix>:4 (Ext: <ext>)? + // Phone: <ac> <phone>:3 <suffix>:4 {{REGEX_PHONE, FIELD_AREA_CODE}, {REGEX_PHONE, FIELD_PHONE, 3}, {REGEX_PHONE, FIELD_SUFFIX, 4}}, - // Phone: <cc> \( <ac> \) <phone> (- <suffix> (Ext: <ext>)?)? + // Phone: <cc> \( <ac> \) <phone> {{REGEX_PHONE, FIELD_COUNTRY_CODE}, {REGEX_AREA_NOTEXT, FIELD_AREA_CODE}, {REGEX_PREFIX_SEPARATOR, FIELD_PHONE}}, - // Phone: \( <ac> \) <phone> (- <suffix> (Ext: <ext>)?)? - {{REGEX_PHONE, FIELD_COUNTRY_CODE}, - {REGEX_AREA_NOTEXT, FIELD_AREA_CODE}, - {REGEX_PREFIX_SEPARATOR, FIELD_PHONE}}, - // Phone: <cc> - <ac> - <phone> - <suffix> (Ext: <ext>)? + // Phone: <cc> - <ac> - <phone> - <suffix> {{REGEX_PHONE, FIELD_COUNTRY_CODE}, {REGEX_PREFIX_SEPARATOR, FIELD_AREA_CODE}, {REGEX_PREFIX_SEPARATOR, FIELD_PHONE}, {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX}}, - // Area code: <ac>:3 Prefix: <prefix>:3 Suffix: <suffix>:4 (Ext: <ext>)? + // Area code: <ac>:3 Prefix: <prefix>:3 Suffix: <suffix>:4 {{REGEX_AREA, FIELD_AREA_CODE, 3}, {REGEX_PREFIX, FIELD_PHONE, 3}, {REGEX_SUFFIX, FIELD_SUFFIX, 4}}, - // Phone: <ac> Prefix: <phone> Suffix: <suffix> (Ext: <ext>)? + // Phone: <ac> Prefix: <phone> Suffix: <suffix> {{REGEX_PHONE, FIELD_AREA_CODE}, {REGEX_PREFIX, FIELD_PHONE}, {REGEX_SUFFIX, FIELD_SUFFIX}}, - // Phone: <ac> - <phone>:3 - <suffix>:4 (Ext: <ext>)? + // Phone: <ac> - <phone>:3 - <suffix>:4 {{REGEX_PHONE, FIELD_AREA_CODE}, {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3}, {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 4}}, - // Phone: <cc> - <ac> - <phone> (Ext: <ext>)? + // Phone: <cc> - <ac> - <phone> {{REGEX_PHONE, FIELD_COUNTRY_CODE}, {REGEX_PREFIX_SEPARATOR, FIELD_AREA_CODE}, {REGEX_SUFFIX_SEPARATOR, FIELD_PHONE}}, - // Phone: <ac> - <phone> (Ext: <ext>)? + // Phone: <ac> - <phone> {{REGEX_AREA, FIELD_AREA_CODE}, {REGEX_PHONE, FIELD_PHONE}}, - // Phone: <cc>:3 - <phone> (Ext: <ext>)? + // Phone: <cc>:3 - <phone> {{REGEX_PHONE, FIELD_COUNTRY_CODE, 3}, {REGEX_PHONE, FIELD_PHONE}}, - // Phone: <cc> <ac> <phone> (Ext: <ext>)? + // Phone: <cc> <ac> <phone> // Indistinguishable from <area> <prefix> <suffix> {{REGEX_PHONE, FIELD_COUNTRY_CODE}, {EMPTY_LABEL, FIELD_AREA_CODE}, {EMPTY_LABEL, FIELD_PHONE}}, - // Phone: <cc> <phone> (Ext: <ext>)? + // Phone: <cc> <phone> // Indistinguishable from <area> <phone> {{REGEX_PHONE, FIELD_COUNTRY_CODE}, {EMPTY_LABEL, FIELD_PHONE}}, - // Phone: <phone> (Ext: <ext>)? + // Phone: <phone> {{REGEX_PHONE, FIELD_PHONE}}, }); return *grammars; @@ -169,7 +165,7 @@ bool PhoneField::LikelyAugmentedPhoneCountryCode( int total_positive_options = 0; for (const auto& option : field->options) { - if (MatchesPattern(option.content, kAugmentedPhoneCountryCodeRe)) + if (MatchesRegexWithCache(option.content, kAugmentedPhoneCountryCodeRe)) total_positive_options++; } @@ -258,6 +254,7 @@ std::unique_ptr<FormField> PhoneField::Parse(AutofillScanner* scanner, // Find the first matching grammar. bool found_matching_grammar = false; + int grammar_id = 0; for (const PhoneGrammar& grammar : GetPhoneGrammars()) { std::fill(parsed_fields.begin(), parsed_fields.end(), nullptr); if (ParseGrammar(grammar, parsed_fields, scanner, page_language, @@ -266,6 +263,7 @@ std::unique_ptr<FormField> PhoneField::Parse(AutofillScanner* scanner, break; } scanner->RewindTo(start_cursor); + grammar_id++; } if (!found_matching_grammar) return nullptr; @@ -273,20 +271,26 @@ std::unique_ptr<FormField> PhoneField::Parse(AutofillScanner* scanner, DCHECK(parsed_fields[FIELD_PHONE] != nullptr); // Look for a suffix field using two different regex. + // TODO(crbug.com/1348137): Revise or remove. + bool suffix_matched = false; if (!parsed_fields[FIELD_SUFFIX]) { - ParsePhoneField(scanner, kPhoneSuffixRe, &parsed_fields[FIELD_SUFFIX], - {log_manager, "kPhoneSuffixRe"}, - /*is_country_code_field=*/false, "PHONE_SUFFIX", - page_language, pattern_source) || + suffix_matched = + ParsePhoneField(scanner, kPhoneSuffixRe, &parsed_fields[FIELD_SUFFIX], + {log_manager, "kPhoneSuffixRe"}, + /*is_country_code_field=*/false, "PHONE_SUFFIX", + page_language, pattern_source) || ParsePhoneField( scanner, kPhoneSuffixSeparatorRe, &parsed_fields[FIELD_SUFFIX], {log_manager, "kPhoneSuffixSeparatorRe"}, /*is_country_code_field=*/false, "PHONE_SUFFIX_SEPARATOR", page_language, pattern_source); } + AutofillMetrics::LogPhoneNumberGrammarMatched(grammar_id, suffix_matched, + GetPhoneGrammars().size()); + // Now look for an extension. - // The extension is not actually used, so this just eats the field so other - // parsers do not mistaken it for something else. + // The extension is unused, but it is parsed to prevent other parsers from + // misclassifying it as something else. ParsePhoneField(scanner, kPhoneExtensionRe, &parsed_fields[FIELD_EXTENSION], {log_manager, "kPhoneExtensionRe"}, /*is_country_code_field=*/false, "PHONE_EXTENSION", @@ -296,7 +300,7 @@ std::unique_ptr<FormField> PhoneField::Parse(AutofillScanner* scanner, } void PhoneField::AddClassifications( - FieldCandidatesMap* field_candidates) const { + FieldCandidatesMap& field_candidates) const { DCHECK(parsed_phone_fields_[FIELD_PHONE]); // Phone was correctly parsed. bool has_country_code = parsed_phone_fields_[FIELD_COUNTRY_CODE] != nullptr; @@ -319,24 +323,27 @@ void PhoneField::AddClassifications( AddClassification(parsed_phone_fields_[FIELD_AREA_CODE], area_code_type, kBasePhoneParserScore, field_candidates); } else if (has_country_code) { - // Only if we can find country code without city code, it means the phone - // number include city code. field_number_type = base::FeatureList::IsEnabled( features::kAutofillEnableSupportForPhoneNumberTrunkTypes) ? PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX : PHONE_HOME_CITY_AND_NUMBER; } - // We tag the prefix as PHONE_HOME_NUMBER, then when filling the form - // we fill only the prefix depending on the size of the input field. - AddClassification(parsed_phone_fields_[FIELD_PHONE], field_number_type, - kBasePhoneParserScore, field_candidates); - // We tag the suffix as PHONE_HOME_NUMBER, then when filling the form - // we fill only the suffix depending on the size of the input field. + // PHONE_HOME_NUMBER = PHONE_HOME_NUMBER_PREFIX + PHONE_HOME_NUMBER_SUFFIX + // is technically dialable (seven-digit dialing), and thus not contained in + // the area code branch. if (parsed_phone_fields_[FIELD_SUFFIX]) { - AddClassification(parsed_phone_fields_[FIELD_SUFFIX], PHONE_HOME_NUMBER, - kBasePhoneParserScore, field_candidates); + // TODO(crbug.com/1348137): Ideally we want to DCHECK that + // `parsed_phone_fields_[FIELD_AREA_CODE] || !has_country_code` here. + // With the current grammars this can be violated, even though it + // seemingly never happens in practice according to our metrics. + field_number_type = PHONE_HOME_NUMBER_PREFIX; + AddClassification(parsed_phone_fields_[FIELD_SUFFIX], + PHONE_HOME_NUMBER_SUFFIX, kBasePhoneParserScore, + field_candidates); } + AddClassification(parsed_phone_fields_[FIELD_PHONE], field_number_type, + kBasePhoneParserScore, field_candidates); } else { AddClassification(parsed_phone_fields_[FIELD_PHONE], PHONE_HOME_WHOLE_NUMBER, kBasePhoneParserScore, 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 68f9a02024a..7b38d05d01d 100644 --- a/chromium/components/autofill/core/browser/form_parsing/phone_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/phone_field.h @@ -40,13 +40,13 @@ class PhoneField : public FormField { #if defined(UNIT_TEST) // Assign types to the fields for the testing purposes. void AddClassificationsForTesting( - FieldCandidatesMap* field_candidates_for_testing) const { + FieldCandidatesMap& field_candidates_for_testing) const { AddClassifications(field_candidates_for_testing); } #endif protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: // This is for easy description of the possible parsing paths of the phone 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 05edafbe122..bd17d9f5ea3 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 @@ -12,6 +12,7 @@ #include "base/containers/contains.h" #include "base/memory/ptr_util.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_field.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" @@ -147,7 +148,7 @@ void PhoneFieldTest::RunParsingTest(const std::vector<TestFieldData>& fields, // Verify expecations. if (expect_success) { - field_->AddClassificationsForTesting(&field_candidates_map_); + field_->AddClassificationsForTesting(field_candidates_map_); for (size_t i = 0; i < fields.size(); i++) { CheckField(global_ids[i], fields[i].expected_type); } @@ -201,8 +202,10 @@ TEST_P(PhoneFieldTest, ThreePartPhoneNumber) { for (const char* field_type : kFieldTypes) { RunParsingTest( {{field_type, u"Phone:", u"dayphone1", PHONE_HOME_CITY_CODE}, - {field_type, u"-", u"dayphone2", PHONE_HOME_NUMBER, /*max_length=*/3}, - {field_type, u"-", u"dayphone3", PHONE_HOME_NUMBER, /*max_length=*/4}, + {field_type, u"-", u"dayphone2", PHONE_HOME_NUMBER_PREFIX, + /*max_length=*/3}, + {field_type, u"-", u"dayphone3", PHONE_HOME_NUMBER_SUFFIX, + /*max_length=*/4}, {field_type, u"ext.:", u"dayphone4", PHONE_HOME_EXTENSION}}); } } @@ -213,8 +216,8 @@ TEST_P(PhoneFieldTest, ThreePartPhoneNumber) { TEST_P(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix) { for (const char* field_type : kFieldTypes) { RunParsingTest({{field_type, u"Phone:", u"area", PHONE_HOME_CITY_CODE}, - {field_type, u"", u"prefix", PHONE_HOME_NUMBER}, - {field_type, u"", u"suffix", PHONE_HOME_NUMBER, + {field_type, u"", u"prefix", PHONE_HOME_NUMBER_PREFIX}, + {field_type, u"", u"suffix", PHONE_HOME_NUMBER_SUFFIX, /*max_length=*/4}}); } } @@ -223,8 +226,9 @@ TEST_P(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix2) { for (const char* field_type : kFieldTypes) { RunParsingTest( {{field_type, u"(", u"phone1", PHONE_HOME_CITY_CODE, /*max_length=*/3}, - {field_type, u")", u"phone2", PHONE_HOME_NUMBER, /*max_length=*/3}, - {field_type, u"", u"phone3", PHONE_HOME_NUMBER, + {field_type, u")", u"phone2", PHONE_HOME_NUMBER_PREFIX, + /*max_length=*/3}, + {field_type, u"", u"phone3", PHONE_HOME_NUMBER_SUFFIX, /*max_length=*/4}}); } } @@ -258,6 +262,18 @@ TEST_P(PhoneFieldTest, EmptyLabels) { {"text", u"", u"", PHONE_HOME_NUMBER}}); } +// Tests that when a phone field is parsed, a metric indicating the used grammar +// is emitted. +TEST_P(PhoneFieldTest, GrammarMetrics) { + // PHONE_HOME_WHOLE_NUMBER corresponds to the last grammar. We thus expect + // that 2*16 + 1 = 33 is logged. + base::HistogramTester histogram_tester; + RunParsingTest({{"text", u"Phone", u"phone", PHONE_HOME_WHOLE_NUMBER}}); + EXPECT_THAT(histogram_tester.GetAllSamples( + "Autofill.FieldPrediction.PhoneNumberGrammarUsage"), + BucketsAre(base::Bucket(33, 1))); +} + TEST_P(PhoneFieldTest, TrunkPrefixTypes) { base::test::ScopedFeatureList trunk_types_enabled; trunk_types_enabled.InitAndEnableFeature( 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 a91e7a34e14..7fcf9df69a1 100644 --- a/chromium/components/autofill/core/browser/form_parsing/price_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/price_field.cc @@ -5,9 +5,9 @@ #include "components/autofill/core/browser/form_parsing/price_field.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/browser/form_parsing/regex_patterns.h" +#include "components/autofill/core/common/autofill_regex_constants.h" namespace autofill { @@ -35,7 +35,7 @@ std::unique_ptr<FormField> PriceField::Parse(AutofillScanner* scanner, PriceField::PriceField(const AutofillField* field) : field_(field) {} void PriceField::AddClassifications( - FieldCandidatesMap* field_candidates) const { + FieldCandidatesMap& field_candidates) const { AddClassification(field_, PRICE, kBasePriceParserScore, field_candidates); } 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 e550b750d1d..d81a319c123 100644 --- a/chromium/components/autofill/core/browser/form_parsing/price_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/price_field.h @@ -33,7 +33,7 @@ class PriceField : public FormField { PriceField& operator=(const PriceField&) = delete; protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: FRIEND_TEST_ALL_PREFIXES(PriceFieldTest, ParsePrice); 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 56380de0c4e..72747db2198 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 @@ -33,7 +33,7 @@ INSTANTIATE_TEST_SUITE_P( ::testing::ValuesIn(PatternProviderFeatureState::All())); TEST_P(PriceFieldTest, ParsePrice) { - AddTextFormFieldData("name your price", "userPrice", PRICE); + AddTextFormFieldData("userPrice", "name your price", PRICE); ClassifyAndVerify(ParseResult::PARSED); } diff --git a/chromium/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc index 875821b3f8c..d51d5fcb153 100644 --- a/chromium/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc +++ b/chromium/components/autofill/core/browser/form_parsing/regex_patterns_unittest.cc @@ -16,10 +16,10 @@ #include "base/logging.h" #include "base/ranges/ranges.h" #include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/browser/form_parsing/buildflags.h" #include "components/autofill/core/browser/form_parsing/regex_patterns_inl.h" #include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_regexes.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -59,10 +59,12 @@ MatchPatternRefTestApi test_api(MatchPatternRef p) { return MatchPatternRefTestApi(p); } -auto Matches(base::StringPiece16 pattern) { - return ::testing::Truly([pattern](base::StringPiece actual) { - return MatchesPattern(base::UTF8ToUTF16(actual), pattern); - }); +auto Matches(base::StringPiece16 regex) { + icu::RegexPattern regex_pattern = *CompileRegex(regex); + return ::testing::Truly( + [regex_pattern = std::move(regex_pattern)](base::StringPiece actual) { + return MatchesRegex(base::UTF8ToUTF16(actual), regex_pattern); + }); } auto Matches(MatchingPattern pattern) { diff --git a/chromium/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json b/chromium/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json index a4217b5a21d..1e5fb2764c7 100644 --- a/chromium/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json +++ b/chromium/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json @@ -22,8 +22,8 @@ "positive_pattern": "street", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "de" : [ @@ -32,8 +32,8 @@ "positive_pattern": "stra(ss|ß)e", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "es" : [ @@ -42,8 +42,8 @@ "positive_pattern": "calle", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "ru" : [ @@ -52,8 +52,8 @@ "positive_pattern": "улица|название.?улицы", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "pt" : [ @@ -62,8 +62,8 @@ "positive_pattern": "rua|avenida|((?<!do |de )endereço)", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } ] }, @@ -74,8 +74,8 @@ "positive_pattern": "apartment", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "es": [ @@ -84,8 +84,8 @@ "positive_pattern": "interior|número.*apartamento", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "de": [ @@ -94,8 +94,8 @@ "positive_pattern": "wohnung", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ru": [ @@ -104,8 +104,8 @@ "positive_pattern": "квартир", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "it": [ @@ -114,8 +114,8 @@ "positive_pattern": "numero.*appartamento", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "fr": [ @@ -124,8 +124,8 @@ "positive_pattern": "numéro.*appartement", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -136,8 +136,8 @@ "positive_pattern": "(house.?|street.?|^)(number|no\\.?$)", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "de": [ @@ -146,8 +146,8 @@ "positive_pattern": "(haus|^)(nummer|nr)", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "pt": [ @@ -156,8 +156,8 @@ "positive_pattern": "^\\*?.?número(.?\\*?$| da residência)", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "es": [ @@ -166,8 +166,8 @@ "positive_pattern": "n(u|ú)mero.*apartamento|exterior", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ru": [ @@ -176,8 +176,8 @@ "positive_pattern": "дом|номер.?дома", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -188,8 +188,8 @@ "positive_pattern": "attention|attn", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -200,8 +200,8 @@ "positive_pattern": "province|region|other", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -210,8 +210,8 @@ "positive_pattern": "provincia", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "pt": [ @@ -220,8 +220,8 @@ "positive_pattern": "bairro|suburb", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -232,8 +232,8 @@ "positive_pattern": "address.*nickname|address.*label", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "tr": [ @@ -242,8 +242,8 @@ "positive_pattern": "adres ([İi]sim|başlığı|adı)", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "pt": [ @@ -252,8 +252,8 @@ "positive_pattern": "identificação do endereço", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "id": [ @@ -262,8 +262,8 @@ "positive_pattern": "(label|judul|nama) alamat", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -274,8 +274,8 @@ "positive_pattern": "company|business|organization|organisation", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "de": [ @@ -284,8 +284,8 @@ "positive_pattern": "(?<!con)firma|firmenname", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -294,8 +294,8 @@ "positive_pattern": "empresa", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fr": [ @@ -304,8 +304,8 @@ "positive_pattern": "societe|société", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "it": [ @@ -314,8 +314,8 @@ "positive_pattern": "ragione.?sociale", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ja": [ @@ -324,8 +324,8 @@ "positive_pattern": "会社", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ru": [ @@ -334,8 +334,8 @@ "positive_pattern": "название.?компании", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "zh-CN": [ @@ -344,8 +344,8 @@ "positive_pattern": "单位|公司", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fa": [ @@ -354,8 +354,8 @@ "positive_pattern": "شرکت", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ko": [ @@ -364,8 +364,8 @@ "positive_pattern": "회사|직장", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "id": [ @@ -374,8 +374,8 @@ "positive_pattern": "(nama.?)?perusahaan", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -386,16 +386,16 @@ "positive_pattern": "^address$|address[_-]?line(one)?|address1|addr1|street|(?:shipping|billing)address$|house.?name", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] }, { "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)|street.*(house|building|apartment|floor)|(house|building|apartment|floor).*street", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "de": [ @@ -404,8 +404,8 @@ "positive_pattern": "strasse|straße", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "es": [ @@ -414,8 +414,8 @@ "positive_pattern": "direccion|dirección", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "fr": [ @@ -424,16 +424,16 @@ "positive_pattern": "adresse", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] }, { "pattern_identifier": "fr_address_line_1_label_preserving", "positive_pattern": "adresse", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "it": [ @@ -442,16 +442,16 @@ "positive_pattern": "indirizzo", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] }, { "pattern_identifier": "it_address_line_1_label_preserving", "positive_pattern": "indirizzo", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "ja": [ @@ -460,16 +460,16 @@ "positive_pattern": "^住所$|住所1", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] }, { "pattern_identifier": "ja_address_line_1_label_preserving", "positive_pattern": "住所", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "pt": [ @@ -478,8 +478,8 @@ "positive_pattern": "morada|((?<!do |de )endereço)", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "ru": [ @@ -488,16 +488,16 @@ "positive_pattern": "Адрес", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] }, { "pattern_identifier": "ru_address_line_1_label_preserving", "positive_pattern": "улиц.*(дом|корпус|квартир|этаж)|(дом|корпус|квартир|этаж).*улиц", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "zh-CN": [ @@ -506,8 +506,8 @@ "positive_pattern": "地址", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "tr": [ @@ -516,16 +516,16 @@ "positive_pattern": "(\\b|_)adres(?! tarifi)(\\b|_)", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] }, { "pattern_identifier": "tr_address_line_1_label_preserving", "positive_pattern": "(\\b|_)adres(?! tarifi)(\\b|_)|(sokak|cadde).*(apartman|bina|daire|mahalle)|(apartman|bina|daire|mahalle).*(sokak|cadde)", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "ko": [ @@ -534,8 +534,8 @@ "positive_pattern": "^주소.?$|주소.?1", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } , { @@ -543,8 +543,8 @@ "positive_pattern": "주소", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT", "SEARCH"] } ], "id": [ @@ -553,8 +553,8 @@ "positive_pattern": "^alamat", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SEARCH"] } , { @@ -562,8 +562,8 @@ "positive_pattern": "^alamat", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0, 7] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT", "SEARCH"] } ] }, @@ -574,16 +574,16 @@ "positive_pattern": "address[_-]?line(2|two)|address2|addr2|street|suite|unit", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] }, { "pattern_identifier": "en_address_line_2_label_preserving", "positive_pattern": "address|line", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT"] } ], "de": [ @@ -592,8 +592,8 @@ "positive_pattern": "adresszusatz|ergänzende.?angaben", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -602,8 +602,8 @@ "positive_pattern": "direccion2|colonia|adicional", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fr": [ @@ -612,16 +612,16 @@ "positive_pattern": "addresssuppl|complementnom|appartement", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] }, { "pattern_identifier": "fr_address_line_2_label_preserving", "positive_pattern": "adresse", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT"] } ], "it": [ @@ -630,16 +630,16 @@ "positive_pattern": "indirizzo2", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] }, { "pattern_identifier": "it_address_line_2_label_preserving", "positive_pattern": "indirizzo", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT"] } ], "ja": [ @@ -648,8 +648,8 @@ "positive_pattern": "住所2", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "pt": [ @@ -658,8 +658,8 @@ "positive_pattern": "complemento|addrcomplement", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ru": [ @@ -668,8 +668,8 @@ "positive_pattern": "Улица", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "zh-CN": [ @@ -678,16 +678,16 @@ "positive_pattern": "地址2", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] }, { "pattern_identifier": "zh_address_line_2_label_preserving", "positive_pattern": "地址", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT"] } ], "ko": [ @@ -696,16 +696,16 @@ "positive_pattern": "주소.?2", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] }, { "pattern_identifier": "ko_address_line_2_label_preserving", "positive_pattern": "주소", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL"], + "match_field_input_types": ["TEXT"] } ] }, @@ -716,8 +716,8 @@ "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": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -726,8 +726,8 @@ "positive_pattern": "municipio", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fr": [ @@ -736,8 +736,8 @@ "positive_pattern": "batiment|residence", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "it": [ @@ -746,8 +746,8 @@ "positive_pattern": "indirizzo[3-9]", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -758,8 +758,8 @@ "positive_pattern": "lookup", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -770,8 +770,8 @@ "positive_pattern": "country|countries", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "es": [ @@ -780,8 +780,8 @@ "positive_pattern": "país|pais", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "de": [ @@ -790,8 +790,8 @@ "positive_pattern": "(\\b|_)land(\\b|_)(?!.*(mark.*))", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ja": [ @@ -800,8 +800,8 @@ "positive_pattern": "(?<!(入|出))国", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "zh-CN": [ @@ -810,8 +810,8 @@ "positive_pattern": "国家", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ko": [ @@ -820,8 +820,8 @@ "positive_pattern": "국가|나라", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "tr": [ @@ -830,8 +830,8 @@ "positive_pattern": "(\\b|_)(ülke|ulce|ulke)(\\b|_)", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "fa": [ @@ -840,8 +840,8 @@ "positive_pattern": "کشور", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "id": [ @@ -850,8 +850,8 @@ "positive_pattern": "negara", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ] }, @@ -862,8 +862,8 @@ "positive_pattern": "location", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["SELECT", "SEARCH"] } ] }, @@ -875,8 +875,8 @@ "positive_score": 1.1, "negative_pattern": "\\.zip\\b", "negative_patterns_explainer": ".zip refers to a file extension. However, there are field billingAddress.zip", - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] }, { "pattern_identifier": "en_zip_code_preserving", @@ -884,8 +884,8 @@ "positive_score": 1.1, "negative_pattern": null, "negative_patterns_explainer": "", - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "de": [ @@ -894,8 +894,8 @@ "positive_pattern": "postleitzahl", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "es": [ @@ -904,8 +904,8 @@ "positive_pattern": "\\bcp\\b", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "fr": [ @@ -914,8 +914,8 @@ "positive_pattern": "\\bcdp\\b", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "it": [ @@ -924,8 +924,8 @@ "positive_pattern": "\\bcap\\b", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ja": [ @@ -934,8 +934,8 @@ "positive_pattern": "郵便番号", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "pt": [ @@ -944,8 +944,8 @@ "positive_pattern": "codigo|codpos|\\bcep\\b", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ru": [ @@ -954,8 +954,8 @@ "positive_pattern": "Почтовый.?Индекс", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "hi": [ @@ -964,8 +964,8 @@ "positive_pattern": "पिन.?कोड", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ml": [ @@ -974,8 +974,8 @@ "positive_pattern": "പിന്കോഡ്", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "zh-CN": [ @@ -984,8 +984,8 @@ "positive_pattern": "邮政编码|邮编", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "zh-TW": [ @@ -994,8 +994,8 @@ "positive_pattern": "郵遞區號", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "tr": [ @@ -1004,8 +1004,8 @@ "positive_pattern": "(\\b|_)posta kodu(\\b|_)", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ko": [ @@ -1014,8 +1014,8 @@ "positive_pattern": "우편.?번호", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "id": [ @@ -1024,8 +1024,8 @@ "positive_pattern": "kode.?pos", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -1037,8 +1037,8 @@ "positive_score": 1.1, "negative_pattern": "\\.zip", "negative_patterns_explainer": ".zip refers to a file extension", - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "pt": [ @@ -1047,8 +1047,8 @@ "positive_pattern": "codpos2", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -1059,8 +1059,8 @@ "positive_pattern": "neighbo(u)?rhood", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "pt": [ @@ -1069,8 +1069,8 @@ "positive_pattern": "bairro", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "tr": [ @@ -1079,8 +1079,8 @@ "positive_pattern": "mahalle|köy", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "id": [ @@ -1089,8 +1089,8 @@ "positive_pattern": "kecamatan", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ] }, @@ -1101,8 +1101,8 @@ "positive_pattern": "city|town|suburb", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "de": [ @@ -1111,8 +1111,8 @@ "positive_pattern": "\\bort\\b|stadt", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "es": [ @@ -1121,8 +1121,8 @@ "positive_pattern": "ciudad|provincia|localidad|poblacion", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "fr": [ @@ -1131,8 +1131,8 @@ "positive_pattern": "ville|commune", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "it": [ @@ -1141,8 +1141,8 @@ "positive_pattern": "localita", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ja": [ @@ -1151,8 +1151,8 @@ "positive_pattern": "市区町村", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "pt": [ @@ -1161,8 +1161,8 @@ "positive_pattern": "cidade|município", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ru": [ @@ -1171,8 +1171,8 @@ "positive_pattern": "Город|Насел(е|ё)нный.?пункт", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "zh-TW": [ @@ -1181,8 +1181,8 @@ "positive_pattern": "市|分區", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "fa": [ @@ -1191,8 +1191,8 @@ "positive_pattern": "شهر", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "hi": [ @@ -1201,8 +1201,8 @@ "positive_pattern": "शहर|ग्राम|गाँव", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ml": [ @@ -1211,8 +1211,8 @@ "positive_pattern": "നഗരം|ഗ്രാമം", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "tr": [ @@ -1221,8 +1221,8 @@ "positive_pattern": "((\\b|_|\\*)([İii̇]l[cç]e(miz|niz)?)(\\b|_|\\*))", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ko": [ @@ -1231,8 +1231,8 @@ "positive_pattern": "^시[^도·・]|시[·・]?군[·・]?구", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "id": [ @@ -1241,8 +1241,8 @@ "positive_pattern": "kota|kabupaten", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ] }, @@ -1253,8 +1253,8 @@ "positive_pattern": "(?<!(united|hist|history).?)state|county|region|province|county|principality", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ja": [ @@ -1263,8 +1263,8 @@ "positive_pattern": "都道府県", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "pt": [ @@ -1273,8 +1273,8 @@ "positive_pattern": "estado|provincia", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ru": [ @@ -1283,8 +1283,8 @@ "positive_pattern": "область", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "zh-TW": [ @@ -1293,8 +1293,8 @@ "positive_pattern": "省|地區", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ml": [ @@ -1303,8 +1303,8 @@ "positive_pattern": "സംസ്ഥാനം", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "fa": [ @@ -1313,8 +1313,8 @@ "positive_pattern": "استان", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "hi": [ @@ -1323,8 +1323,8 @@ "positive_pattern": "राज्य", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "tr": [ @@ -1333,8 +1333,8 @@ "positive_pattern": "((\\b|_|\\*)(eyalet|[şs]ehir|[İii̇]l(imiz)?|kent)(\\b|_|\\*))", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ko": [ @@ -1343,8 +1343,8 @@ "positive_pattern": "^시[·・]?도", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "id": [ @@ -1353,8 +1353,8 @@ "positive_pattern": "provinci", "positive_score": 1.1, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ] }, @@ -1365,8 +1365,8 @@ "positive_pattern": "^q$|search|query|qry", "positive_score": 0.8, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 4, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"] } ], "de": [ @@ -1375,8 +1375,8 @@ "positive_pattern": "suche.*", "positive_score": 0.8, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 4, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"] } ], "zh-CN": [ @@ -1385,8 +1385,8 @@ "positive_pattern": "搜索", "positive_score": 0.8, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 4, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"] } ], "ja": [ @@ -1395,8 +1395,8 @@ "positive_pattern": "探す|検索", "positive_score": 0.8, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 4, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"] } ], "fr": [ @@ -1405,8 +1405,8 @@ "positive_pattern": "recherch.*", "positive_score": 0.8, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 4, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"] } ], "pt": [ @@ -1415,8 +1415,8 @@ "positive_pattern": "busca", "positive_score": 0.8, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 4, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"] } ], "fa": [ @@ -1425,8 +1425,8 @@ "positive_pattern": "جستجو", "positive_score": 0.8, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 4, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"] } ], "ru": [ @@ -1435,8 +1435,8 @@ "positive_pattern": "искать|найти|поиск", "positive_score": 0.8, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 4, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"] } ] }, @@ -1447,8 +1447,8 @@ "positive_pattern": "\\bprice\\b|\\brate\\b|\\bcost\\b", "positive_score": 0.95, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 4, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "TEXT_AREA", "NUMBER", "SEARCH"] } ], "ar": [ @@ -1457,8 +1457,8 @@ "positive_pattern": "قیمة|سعر", "positive_score": 0.95, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 4, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "TEXT_AREA", "NUMBER", "SEARCH"] } ], "fa": [ @@ -1467,8 +1467,8 @@ "positive_pattern": "قیمت", "positive_score": 0.95, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 4, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "TEXT_AREA", "NUMBER", "SEARCH"] } ], "fr": [ @@ -1477,8 +1477,8 @@ "positive_pattern": "\\bprix\\b|\\bcoût\\b|\\bcout\\b|\\btarif\\b", "positive_score": 0.95, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 4, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "TEXT_AREA", "NUMBER", "SEARCH"] } ] }, @@ -1489,8 +1489,8 @@ "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": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "de": [ @@ -1499,8 +1499,8 @@ "positive_pattern": "karteninhaber", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -1509,8 +1509,8 @@ "positive_pattern": "nombre.*tarjeta", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fr": [ @@ -1519,8 +1519,8 @@ "positive_pattern": "nom.*carte", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "it": [ @@ -1529,8 +1529,8 @@ "positive_pattern": "nome.*cart", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ja": [ @@ -1539,8 +1539,8 @@ "positive_pattern": "名前", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ru": [ @@ -1549,8 +1549,8 @@ "positive_pattern": "Имя.*карты", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "zh-CN": [ @@ -1559,8 +1559,8 @@ "positive_pattern": "信用卡开户名|开户名|持卡人姓名|持卡人姓名", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "id": [ @@ -1569,8 +1569,8 @@ "positive_pattern": "nama.*kartu", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -1581,8 +1581,8 @@ "positive_pattern": "name", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -1593,8 +1593,8 @@ "positive_pattern": "(add)?(?:card|cc|acct).?(?:number|#|no|num|field|pan)", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ], "de": [ @@ -1603,8 +1603,8 @@ "positive_pattern": "(?<!telefon|haus|person|fødsels|kunden)nummer", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ], "ja": [ @@ -1613,8 +1613,8 @@ "positive_pattern": "カード番号", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ], "ru": [ @@ -1623,8 +1623,8 @@ "positive_pattern": "Номер.*карты", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ], "zh-CN": [ @@ -1633,8 +1633,8 @@ "positive_pattern": "信用卡号|信用卡号码", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ], "zh-TW": [ @@ -1643,8 +1643,8 @@ "positive_pattern": "信用卡卡號", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ], "ko": [ @@ -1653,8 +1653,8 @@ "positive_pattern": "카드", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ], "es": [ @@ -1663,8 +1663,8 @@ "positive_pattern": "(numero|número|numéro)(?!.*(document|fono|phone|réservation))", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ], "pt": [ @@ -1673,8 +1673,8 @@ "positive_pattern": "(numero|número|numéro)(?!.*(document|fono|phone|réservation))", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ], "fr": [ @@ -1683,8 +1683,8 @@ "positive_pattern": "(numero|número|numéro)(?!.*(document|fono|phone|réservation))", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ], "id": [ @@ -1693,8 +1693,8 @@ "positive_pattern": "no.*kartu", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ] }, @@ -1705,8 +1705,8 @@ "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": [0, 1], - "match_field_input_types": [0, 2, 5, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "PASSWORD", "NUMBER"] } ] }, @@ -1717,8 +1717,8 @@ "positive_pattern": "expir|exp.*mo|exp.*date|ccmonth|cardmonth|addmonth", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "de": [ @@ -1727,8 +1727,8 @@ "positive_pattern": "gueltig|gültig|monat", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "es": [ @@ -1737,8 +1737,8 @@ "positive_pattern": "fecha", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "fr": [ @@ -1747,8 +1747,8 @@ "positive_pattern": "date.*exp", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "it": [ @@ -1757,8 +1757,8 @@ "positive_pattern": "scadenza", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "ja": [ @@ -1767,8 +1767,8 @@ "positive_pattern": "有効期限", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "pt": [ @@ -1777,8 +1777,8 @@ "positive_pattern": "validade", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "ru": [ @@ -1787,8 +1787,8 @@ "positive_pattern": "Срок действия карты", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "zh-CN": [ @@ -1797,8 +1797,8 @@ "positive_pattern": "月", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "id": [ @@ -1807,8 +1807,8 @@ "positive_pattern": "masa berlaku|berlaku hingga", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ] }, @@ -1819,8 +1819,8 @@ "positive_pattern": "exp|^/|(add)?year", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "de": [ @@ -1829,8 +1829,8 @@ "positive_pattern": "ablaufdatum|gueltig|gültig|jahr", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "es": [ @@ -1839,8 +1839,8 @@ "positive_pattern": "fecha", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "it": [ @@ -1849,8 +1849,8 @@ "positive_pattern": "scadenza", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "ja": [ @@ -1859,8 +1859,8 @@ "positive_pattern": "有効期限", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "pt": [ @@ -1869,8 +1869,8 @@ "positive_pattern": "validade", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "ru": [ @@ -1879,8 +1879,8 @@ "positive_pattern": "Срок действия карты", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "zh-CN": [ @@ -1889,8 +1889,8 @@ "positive_pattern": "年|有效期", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "id": [ @@ -1899,8 +1899,8 @@ "positive_pattern": "masa berlaku|berlaku hingga", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ] }, @@ -1911,8 +1911,8 @@ "positive_pattern": "(?:exp.*date[^y\\n\\r]*|mm\\s*[-/]?\\s*)yy(?:[^y]|$)", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ] }, @@ -1923,8 +1923,8 @@ "positive_pattern": "(?:exp.*date[^y\\n\\r]*|mm\\s*[-/]?\\s*)yyyy(?:[^y]|$)", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ] }, @@ -1935,8 +1935,8 @@ "positive_pattern": "expir|exp.*date|^expfield$", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "de": [ @@ -1945,8 +1945,8 @@ "positive_pattern": "gueltig|gültig", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "es": [ @@ -1955,8 +1955,8 @@ "positive_pattern": "fecha", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "fr": [ @@ -1965,8 +1965,8 @@ "positive_pattern": "date.*exp", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "it": [ @@ -1975,8 +1975,8 @@ "positive_pattern": "scadenza", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "ja": [ @@ -1985,8 +1985,8 @@ "positive_pattern": "有効期限", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "pt": [ @@ -1995,8 +1995,8 @@ "positive_pattern": "validade", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ], "ru": [ @@ -2005,8 +2005,8 @@ "positive_pattern": "Срок действия карты", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ] }, @@ -2017,8 +2017,8 @@ "positive_pattern": "^mm$", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ] }, @@ -2029,8 +2029,8 @@ "positive_pattern": "^(yy|yyyy)$", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER", "SEARCH"] } ] }, @@ -2041,8 +2041,8 @@ "positive_pattern": "gift.?(card|cert)", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER", "SEARCH"] } ] }, @@ -2053,8 +2053,8 @@ "positive_pattern": "(?:visa|mastercard|discover|amex|american express).*gift.?card", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER", "SEARCH"] } ] }, @@ -2065,8 +2065,8 @@ "positive_pattern": "debit.*card", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER", "SEARCH"] } ] }, @@ -2077,8 +2077,8 @@ "positive_pattern": "day", "positive_score": 1.0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT"] } ] }, @@ -2089,8 +2089,8 @@ "positive_pattern": "e.?mail", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "fr": [ @@ -2099,8 +2099,8 @@ "positive_pattern": "courriel", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "es": [ @@ -2109,8 +2109,8 @@ "positive_pattern": "correo.*electr(o|ó)nico", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "ja": [ @@ -2119,8 +2119,8 @@ "positive_pattern": "メールアドレス", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "ru": [ @@ -2129,8 +2129,8 @@ "positive_pattern": "Электронн(ая|ой).?Почт(а|ы)", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "zh-CN": [ @@ -2139,8 +2139,8 @@ "positive_pattern": "邮件|邮箱|電子郵件", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "zh-TW": [ @@ -2149,8 +2149,8 @@ "positive_pattern": "電郵地址|電子信箱", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "ml": [ @@ -2159,8 +2159,8 @@ "positive_pattern": "ഇ-മെയില്|ഇലക്ട്രോണിക്.?മെയിൽ", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "fa": [ @@ -2169,8 +2169,8 @@ "positive_pattern": "ایمیل|پست.*الکترونیک", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "hi": [ @@ -2179,8 +2179,8 @@ "positive_pattern": "ईमेल|इलॅक्ट्रॉनिक.?मेल", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "tr": [ @@ -2189,8 +2189,8 @@ "positive_pattern": "(\\b|_)eposta(\\b|_)", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ], "ko": [ @@ -2199,8 +2199,8 @@ "positive_pattern": "(?:이메일|전자.?우편|[Ee]-?mail)(.?주소)?", "positive_score": 1.4, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 1] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "EMAIL"] } ] }, @@ -2211,8 +2211,8 @@ "positive_pattern": "user.?name|user.?id|nickname|maiden name|title|prefix|suffix|mail", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "de": [ @@ -2221,8 +2221,8 @@ "positive_pattern": "vollständiger.?name", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "zh-CN": [ @@ -2231,8 +2231,8 @@ "positive_pattern": "用户名", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ], "ko": [ @@ -2241,8 +2241,8 @@ "positive_pattern": "(?:사용자.?)?아이디|사용자.?ID", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 3, 7] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "SELECT", "SEARCH"] } ] }, @@ -2253,8 +2253,8 @@ "positive_pattern": "^name|full.?name|your.?name|customer.?name|bill.?name|ship.?name|name.*first.*last|firstandlastname|contact.?(name|person)", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -2263,8 +2263,8 @@ "positive_pattern": "nombre.*y.*apellidos", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fr": [ @@ -2273,8 +2273,8 @@ "positive_pattern": "^nom(?![a-zA-Z])", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ja": [ @@ -2283,8 +2283,8 @@ "positive_pattern": "お名前|氏名", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "pt": [ @@ -2293,8 +2293,8 @@ "positive_pattern": "^nome", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fa": [ @@ -2303,8 +2303,8 @@ "positive_pattern": "نام.*نام.*خانوادگی", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "zh-CN": [ @@ -2313,8 +2313,8 @@ "positive_pattern": "姓\\s*名", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ru": [ @@ -2323,8 +2323,8 @@ "positive_pattern": "контактное.?лицо", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "tr": [ @@ -2333,8 +2333,8 @@ "positive_pattern": "(\\b|_|\\*)ad[ı]? soyad[ı]?(\\b|_|\\*)", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ko": [ @@ -2343,8 +2343,8 @@ "positive_pattern": "성명", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "id": [ @@ -2353,8 +2353,8 @@ "positive_pattern": "nama.?(lengkap|penerima|kamu)", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -2365,8 +2365,8 @@ "positive_pattern": "^name", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fr": [ @@ -2375,8 +2375,8 @@ "positive_pattern": "^nom", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "pt": [ @@ -2385,8 +2385,8 @@ "positive_pattern": "^nome", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -2397,8 +2397,8 @@ "positive_pattern": "first.*name|initials|fname|first$|given.*name", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "de": [ @@ -2407,8 +2407,8 @@ "positive_pattern": "vorname", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -2417,8 +2417,8 @@ "positive_pattern": "nombre", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fr": [ @@ -2427,8 +2427,8 @@ "positive_pattern": "forename|prénom|prenom", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ja": [ @@ -2437,8 +2437,8 @@ "positive_pattern": "名", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "pt": [ @@ -2447,8 +2447,8 @@ "positive_pattern": "nome", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ru": [ @@ -2457,8 +2457,8 @@ "positive_pattern": "Имя", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fa": [ @@ -2467,8 +2467,8 @@ "positive_pattern": "نام", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ko": [ @@ -2477,8 +2477,8 @@ "positive_pattern": "이름", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ml": [ @@ -2487,8 +2487,8 @@ "positive_pattern": "പേര്", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "tr": [ @@ -2497,8 +2497,8 @@ "positive_pattern": "(\\b|_|\\*)(isim|ad|ad(i|ı|iniz|ınız)?)(\\b|_|\\*)", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "hi": [ @@ -2507,8 +2507,8 @@ "positive_pattern": "नाम", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "id": [ @@ -2517,8 +2517,8 @@ "positive_pattern": "nama depan", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -2529,8 +2529,8 @@ "positive_pattern": "middle.*initial|m\\.i\\.|mi$|\\bmi\\b", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -2541,8 +2541,8 @@ "positive_pattern": "middle.*name|mname|middle$", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -2553,8 +2553,8 @@ "positive_pattern": "last.*name|lname|surname(?!\\d)|last$|secondname|family.*name", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "de": [ @@ -2563,8 +2563,8 @@ "positive_pattern": "nachname", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -2573,8 +2573,8 @@ "positive_pattern": "apellidos?", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fr": [ @@ -2583,8 +2583,8 @@ "positive_pattern": "famille|^nom(?![a-zA-Z])", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "it": [ @@ -2593,8 +2593,8 @@ "positive_pattern": "cognome", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ja": [ @@ -2603,8 +2603,8 @@ "positive_pattern": "姓", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "pt": [ @@ -2613,8 +2613,8 @@ "positive_pattern": "apelidos|surename|sobrenome", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ru": [ @@ -2623,8 +2623,8 @@ "positive_pattern": "Фамилия", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fa": [ @@ -2633,8 +2633,8 @@ "positive_pattern": "نام.*خانوادگی", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "hi": [ @@ -2643,8 +2643,8 @@ "positive_pattern": "उपनाम", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ml": [ @@ -2653,8 +2653,8 @@ "positive_pattern": "മറുപേര്", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "tr": [ @@ -2663,8 +2663,8 @@ "positive_pattern": "(\\b|_|\\*)(soyisim|soyad(i|ı|iniz|ınız)?)(\\b|_|\\*)", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ko": [ @@ -2673,8 +2673,8 @@ "positive_pattern": "\\b성(?:[^명]|\\b)", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "id": [ @@ -2683,8 +2683,8 @@ "positive_pattern": "nama belakang", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -2695,8 +2695,8 @@ "positive_pattern": "(primer.*apellido)|(apellido1)|(apellido.*paterno)|surname_?1|first(\\s|_)?surname", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -2707,8 +2707,8 @@ "positive_pattern": "(segund.*apellido)|(apellido2)|(apellido.*materno)|surname_?2|second(\\s|_)?surname", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -2719,8 +2719,8 @@ "positive_pattern": "^title:?$|(salutation(?! and given name))", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "de": [ @@ -2729,8 +2729,8 @@ "positive_pattern": "anrede|titel", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -2739,8 +2739,8 @@ "positive_pattern": "tratamiento|encabezamiento", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "it": [ @@ -2749,8 +2749,8 @@ "positive_pattern": "titolo", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fr": [ @@ -2759,8 +2759,8 @@ "positive_pattern": "titre", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ru": [ @@ -2769,8 +2769,8 @@ "positive_pattern": "обращение|звание", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "el": [ @@ -2779,8 +2779,8 @@ "positive_pattern": "προσφώνηση", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "tr": [ @@ -2789,8 +2789,8 @@ "positive_pattern": "hitap", "positive_score": 0.9, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -2801,8 +2801,8 @@ "positive_pattern": "phone|mobile|contact.?number", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "de": [ @@ -2811,8 +2811,8 @@ "positive_pattern": "telefonnummer", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "es": [ @@ -2821,8 +2821,8 @@ "positive_pattern": "telefono|teléfono", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "fr": [ @@ -2831,8 +2831,8 @@ "positive_pattern": "telfixe", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ja": [ @@ -2841,8 +2841,8 @@ "positive_pattern": "電話", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "pt": [ @@ -2851,8 +2851,8 @@ "positive_pattern": "telefone|telemovel", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ru": [ @@ -2861,8 +2861,8 @@ "positive_pattern": "телефон", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "hi": [ @@ -2871,8 +2871,8 @@ "positive_pattern": "मोबाइल", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "tr": [ @@ -2881,8 +2881,8 @@ "positive_pattern": "(\\b|_|\\*)telefon(\\b|_|\\*)", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "zh-CN": [ @@ -2891,8 +2891,8 @@ "positive_pattern": "电话", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ml": [ @@ -2901,8 +2901,8 @@ "positive_pattern": "മൊബൈല്", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ko": [ @@ -2911,8 +2911,8 @@ "positive_pattern": "(?:전화|핸드폰|휴대폰|휴대전화)(?:.?번호)?", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "id": [ @@ -2921,8 +2921,8 @@ "positive_pattern": "telepon|ponsel|(nomor|no\\.?).?(hp|handphone)", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -2933,8 +2933,8 @@ "positive_pattern": "^[^0-9+]*(?:\\+|00)\\s*([1-9]\\d{0,3})\\D*$", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -2945,8 +2945,8 @@ "positive_pattern": "country.*code|ccode|_cc|phone.*code|user.*phone.*code", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 3, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "SELECT", "NUMBER"] } ] }, @@ -2957,8 +2957,8 @@ "positive_pattern": "^\\($", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -2969,8 +2969,8 @@ "positive_pattern": "area.*code|acode|area", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "ko": [ @@ -2979,8 +2979,8 @@ "positive_pattern": "지역.?번호", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -2991,8 +2991,8 @@ "positive_pattern": "^-$|^\\)$", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -3003,8 +3003,8 @@ "positive_pattern": "^-$", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -3015,8 +3015,8 @@ "positive_pattern": "prefix|exchange", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "fr": [ @@ -3025,8 +3025,8 @@ "positive_pattern": "preselection", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "pt": [ @@ -3035,8 +3035,8 @@ "positive_pattern": "ddd", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -3047,8 +3047,8 @@ "positive_pattern": "suffix", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -3059,8 +3059,8 @@ "positive_pattern": "\\bext|ext\\b|extension", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ], "pt": [ @@ -3069,8 +3069,8 @@ "positive_pattern": "ramal", "positive_score": 1.3, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0, 2, 6] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT", "TELEPHONE", "NUMBER"] } ] }, @@ -3081,8 +3081,8 @@ "positive_pattern": "document.*number|passport", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "fr": [ @@ -3091,8 +3091,8 @@ "positive_pattern": "passeport", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -3101,8 +3101,8 @@ "positive_pattern": "numero.*documento|pasaporte", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ja": [ @@ -3111,8 +3111,8 @@ "positive_pattern": "書類", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3123,8 +3123,8 @@ "positive_pattern": "point.*of.*entry|arrival", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -3133,8 +3133,8 @@ "positive_pattern": "punto.*internaci(o|ó)n|fecha.*llegada", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ja": [ @@ -3143,8 +3143,8 @@ "positive_pattern": "入国", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3155,8 +3155,8 @@ "positive_pattern": "departure", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -3165,8 +3165,8 @@ "positive_pattern": "fecha.*salida|destino", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ja": [ @@ -3175,8 +3175,8 @@ "positive_pattern": "出国", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3187,8 +3187,8 @@ "positive_pattern": "airline|flight", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "es": [ @@ -3197,8 +3197,8 @@ "positive_pattern": "aerol(i|í)nea|n(u|ú)mero.*vuelo", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ], "ja": [ @@ -3207,8 +3207,8 @@ "positive_pattern": "便名|航空会社", "positive_score": 1.2, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3219,16 +3219,16 @@ "positive_pattern": "^[\\w.+-_]+@(\\w+\\.ifsc\\.npci|aadhaar\\.npci|mobile\\.npci|rupay\\.npci)$", "positive_score": 0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] }, { "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": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3239,8 +3239,8 @@ "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": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3251,8 +3251,8 @@ "positive_pattern": "^\\d{3,4}$", "positive_score": 0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3263,8 +3263,8 @@ "positive_pattern": "^[2][0][1-9][0-9]$", "positive_score": 0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3275,8 +3275,8 @@ "positive_pattern": "/search(/|((\\w*\\.\\w+)?$))", "positive_score": 0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3287,8 +3287,8 @@ "positive_pattern": "ssn|social.?security.?(num(ber)?|#)*", "positive_score": 0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3299,8 +3299,8 @@ "positive_pattern": "one.?time|sms.?(code|token|password|pwd|pass)", "positive_score": 0, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] }, @@ -3311,8 +3311,20 @@ "positive_pattern": "(promo(tion|tional)?|gift|discount|coupon)[-_. ]*code", "positive_score": 0.85, "negative_pattern": null, - "match_field_attributes": [0, 1], - "match_field_input_types": [0] + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] + } + ] + }, + "IBAN_VALUE": { + "en": [ + { + "pattern_identifier": "en_iban_preserving", + "positive_pattern": "(\\biban(\\b|_)|international bank account number)", + "positive_score": 0.975, + "negative_pattern": null, + "match_field_attributes": ["LABEL", "NAME"], + "match_field_input_types": ["TEXT"] } ] } 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 26867e2f81d..fdce2b87e5a 100644 --- a/chromium/components/autofill/core/browser/form_parsing/search_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/search_field.cc @@ -5,9 +5,9 @@ #include "components/autofill/core/browser/form_parsing/search_field.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/browser/form_parsing/regex_patterns.h" +#include "components/autofill/core/common/autofill_regex_constants.h" namespace autofill { @@ -33,7 +33,7 @@ std::unique_ptr<FormField> SearchField::Parse(AutofillScanner* scanner, SearchField::SearchField(const AutofillField* field) : field_(field) {} void SearchField::AddClassifications( - FieldCandidatesMap* field_candidates) const { + FieldCandidatesMap& field_candidates) const { AddClassification(field_, SEARCH_TERM, kBaseSearchParserScore, field_candidates); } 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 91b7766bb69..e17e68582da 100644 --- a/chromium/components/autofill/core/browser/form_parsing/search_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/search_field.h @@ -33,7 +33,7 @@ class SearchField : public FormField { SearchField& operator=(const SearchField&) = delete; protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: FRIEND_TEST_ALL_PREFIXES(SearchFieldTest, ParseSearchTerm); diff --git a/chromium/components/autofill/core/browser/form_parsing/standalone_cvc_field.cc b/chromium/components/autofill/core/browser/form_parsing/standalone_cvc_field.cc new file mode 100644 index 00000000000..1110d613293 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/standalone_cvc_field.cc @@ -0,0 +1,80 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/form_parsing/standalone_cvc_field.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_payments_features.h" + +namespace autofill { + +// static +std::unique_ptr<FormField> StandaloneCvcField::Parse( + AutofillScanner* scanner, + const LanguageCode& page_language, + PatternSource pattern_source, + LogManager* log_manager) { + if (!base::FeatureList::IsEnabled( + features::kAutofillParseVcnCardOnFileStandaloneCvcFields)) { + return nullptr; + } + + // Ignore gift card fields as both |kGiftCardRe| and |kCardCvcRe| matches + // "gift card pin" and "gift card code" but it should only match + // |kGiftCardRe|. + if (MatchGiftCard(scanner, log_manager, page_language, pattern_source)) { + return nullptr; + } + + AutofillField* field; + base::span<const MatchPatternRef> cvc_patterns = GetMatchPatterns( + CREDIT_CARD_VERIFICATION_CODE, page_language, pattern_source); + + // CVC fields can occur in many different field types so we check for each + const auto kMatchNumTelAndPwd = + kDefaultMatchParamsWith<MatchFieldType::kNumber, + MatchFieldType::kTelephone, + MatchFieldType::kPassword>; + if (ParseFieldSpecifics(scanner, kCardCvcRe, kMatchNumTelAndPwd, cvc_patterns, + &field, {log_manager, "kCardCvcRe(standalone)"})) { + return std::make_unique<StandaloneCvcField>(field); + } + + return nullptr; +} + +StandaloneCvcField::~StandaloneCvcField() = default; + +// static +bool StandaloneCvcField::MatchGiftCard(AutofillScanner* scanner, + LogManager* log_manager, + const LanguageCode& page_language, + PatternSource pattern_source) { + if (scanner->IsEnd()) + return false; + + const auto kMatchFieldType = kDefaultMatchParamsWith< + MatchFieldType::kNumber, MatchFieldType::kTelephone, + MatchFieldType::kSearch, MatchFieldType::kPassword>; + base::span<const MatchPatternRef> gift_card_patterns = + GetMatchPatterns("GIFT_CARD", page_language, pattern_source); + + return ParseFieldSpecifics(scanner, kGiftCardRe, kMatchFieldType, + gift_card_patterns, nullptr, + {log_manager, "kGiftCardRe"}); +} + +StandaloneCvcField::StandaloneCvcField(const AutofillField* field) + : field_(field) {} + +void StandaloneCvcField::AddClassifications( + FieldCandidatesMap& field_candidates) const { + AddClassification(field_, CREDIT_CARD_STANDALONE_VERIFICATION_CODE, + kBaseCreditCardParserScore, field_candidates); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/standalone_cvc_field.h b/chromium/components/autofill/core/browser/form_parsing/standalone_cvc_field.h new file mode 100644 index 00000000000..d4b272f0dfd --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/standalone_cvc_field.h @@ -0,0 +1,50 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_STANDALONE_CVC_FIELD_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_STANDALONE_CVC_FIELD_H_ + +#include <memory> + +#include "base/memory/raw_ptr.h" +#include "components/autofill/core/browser/form_parsing/form_field.h" +#include "components/autofill/core/common/language_code.h" + +namespace autofill { + +class AutofillField; +class AutofillScanner; +class LogManager; + +// A form field that accepts a standalone cvc. +class StandaloneCvcField : public FormField { + public: + static std::unique_ptr<FormField> Parse(AutofillScanner* scanner, + const LanguageCode& page_language, + PatternSource pattern_source, + LogManager* log_manager); + + explicit StandaloneCvcField(const AutofillField* field); + + ~StandaloneCvcField() override; + + StandaloneCvcField(const StandaloneCvcField&) = delete; + StandaloneCvcField& operator=(const StandaloneCvcField&) = delete; + + protected: + void AddClassifications(FieldCandidatesMap& field_candidates) const override; + + private: + raw_ptr<const AutofillField> field_; + + // static + static bool MatchGiftCard(AutofillScanner* scanner, + LogManager* log_manager, + const LanguageCode& page_language, + PatternSource pattern_source); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_PARSING_STANDALONE_CVC_FIELD_H_ diff --git a/chromium/components/autofill/core/browser/form_parsing/standalone_cvc_field_unittest.cc b/chromium/components/autofill/core/browser/form_parsing/standalone_cvc_field_unittest.cc new file mode 100644 index 00000000000..604ccc6a070 --- /dev/null +++ b/chromium/components/autofill/core/browser/form_parsing/standalone_cvc_field_unittest.cc @@ -0,0 +1,78 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/form_parsing/standalone_cvc_field.h" + +#include "base/test/scoped_feature_list.h" +#include "components/autofill/core/browser/form_parsing/parsing_test_utils.h" +#include "components/autofill/core/common/autofill_payments_features.h" + +namespace autofill { + +class StandaloneCvcFieldTest + : public FormFieldTestBase, + public testing::TestWithParam<PatternProviderFeatureState> { + public: + StandaloneCvcFieldTest() : FormFieldTestBase(GetParam()) {} + StandaloneCvcFieldTest(const StandaloneCvcFieldTest&) = delete; + StandaloneCvcFieldTest& operator=(const StandaloneCvcFieldTest&) = delete; + + protected: + std::unique_ptr<FormField> Parse( + AutofillScanner* scanner, + const LanguageCode& page_language = LanguageCode("en")) override { + return StandaloneCvcField::Parse(scanner, page_language, + GetActivePatternSource(), + /*log_manager=*/nullptr); + } + + base::test::ScopedFeatureList scoped_feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P( + StandaloneCvcFieldTest, + StandaloneCvcFieldTest, + ::testing::ValuesIn(PatternProviderFeatureState::All())); + +// Match standalone cvc. +TEST_P(StandaloneCvcFieldTest, ParseStandaloneCvc) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillParseVcnCardOnFileStandaloneCvcFields); + + AddTextFormFieldData("cvc", "CVC:", CREDIT_CARD_STANDALONE_VERIFICATION_CODE); + + ClassifyAndVerify(ParseResult::PARSED); +} + +// Do not parse non cvc standalone fields. +TEST_P(StandaloneCvcFieldTest, ParseNonStandaloneCvc) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillParseVcnCardOnFileStandaloneCvcFields); + + AddTextFormFieldData("other-field", "Other Field:", UNKNOWN_TYPE); + + ClassifyAndVerify(ParseResult::NOT_PARSED); +} + +// Do not parse when standalone cvc flag is disabled. +TEST_P(StandaloneCvcFieldTest, ParseStandaloneCvcFlagOff) { + scoped_feature_list_.InitAndDisableFeature( + features::kAutofillParseVcnCardOnFileStandaloneCvcFields); + + AddTextFormFieldData("cvc", "CVC:", CREDIT_CARD_STANDALONE_VERIFICATION_CODE); + + ClassifyAndVerify(ParseResult::NOT_PARSED); +} + +// Do not parse gift card as standalone cvc fields. +TEST_P(StandaloneCvcFieldTest, NotParseGiftCardAsStandaloneCvc) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillParseVcnCardOnFileStandaloneCvcFields); + + AddTextFormFieldData("gift-card", "Gift Card Pin:", UNKNOWN_TYPE); + + ClassifyAndVerify(ParseResult::NOT_PARSED); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/transpile_regex_patterns.py b/chromium/components/autofill/core/browser/form_parsing/transpile_regex_patterns.py index ebcd89a6000..6a795b01aea 100755 --- a/chromium/components/autofill/core/browser/form_parsing/transpile_regex_patterns.py +++ b/chromium/components/autofill/core/browser/form_parsing/transpile_regex_patterns.py @@ -8,6 +8,7 @@ import argparse from collections import defaultdict import io import json +import re import sys # Generates a set of C++ constexpr constants to facilitate lookup of a set of @@ -36,11 +37,6 @@ import sys # lookup pattern name -> language code -> span of MatchPatternRefs, but it's # significantly simpler. def generate_cpp_constants(id_to_name_to_lang_to_patterns): - # The enum constant of the MatchAttribute::kName. - kName = 1 - yield f'static_assert(MatchAttribute::kName == To<MatchAttribute,{kName}>());' - yield '' - # Stores a `key` in `dictionary` and assigns it a natural number. # # For example, after memoize("foo", d) and memoize("bar", d), @@ -64,17 +60,30 @@ def generate_cpp_constants(id_to_name_to_lang_to_patterns): def json_to_cpp_u16string_literal(json_string_literal): return 'u'+ json.dumps(json_string_literal or '') - # Maps a list of natural numbers to a DenseSet<MatchAttribute> expression. + # Maps a list of strings to a DenseSet containing these values. + # The strings represents constants in the format FOO_BAR. + # They're translated to the C++ constant kFooBar. + def json_to_cpp_dense_set(json_enum_values, cpp_enum_type): + # Converts FOO_BAR into kFooBar. + def json_to_cpp_constant_symbol(json): + assert json.isupper() + return f'{cpp_enum_type}::k' + re.sub( + r'(^|_)([a-z])', lambda matched: matched.group(2).upper(), + json.lower()) + cpp_enum_values = [json_to_cpp_constant_symbol(c) for c in json_enum_values] + return (f'DenseSet<{cpp_enum_type}>{{' + ','.join(cpp_enum_values) + f'}}') + + # Maps a list of strings to a DenseSet<MatchAttribute> expression. + # The strings must be the names of MatchAttribute constants, e.g., NAME. + # They're mapped to C++ constants, e.g., kName. def json_to_cpp_match_field_attributes(enum_values): - return f'DenseSet<MatchAttribute>{{' \ - f"{','.join(f'To<MatchAttribute,{i}>()' for i in enum_values)}" \ - f'}}' + return json_to_cpp_dense_set(enum_values, 'MatchAttribute') - # Maps a list of natural numbers to a DenseSet<MatchFieldType> expression. + # Maps a list of strings to a DenseSet<MatchFieldType> expression. + # The strings must be the names of MatchFieldType constants, e.g., TEXT_AREA. + # They're mapped to C++ constants, e.g., kTextArea. def json_to_cpp_match_field_input_types(enum_values): - return f'DenseSet<MatchFieldType>{{' \ - f"{','.join(f'To<MatchFieldType,{i}>()' for i in enum_values)}" \ - f'}}' + return json_to_cpp_dense_set(enum_values, 'MatchFieldType') # Maps a JSON object representing a pattern to a C++ MatchingPattern # expression. @@ -136,7 +145,7 @@ def generate_cpp_constants(id_to_name_to_lang_to_patterns): if 'en' not in lang_to_patterns: continue def make_supplementary_pattern(p): - assert kName in p['match_field_attributes'] + assert "NAME" in p['match_field_attributes'] p = p.copy() p['supplementary'] = True return p @@ -145,7 +154,7 @@ def generate_cpp_constants(id_to_name_to_lang_to_patterns): patterns.extend( make_supplementary_pattern(p) for p in lang_to_patterns['en'] - if kName in p['match_field_attributes']) + if "NAME" in p['match_field_attributes']) # Populate the two maps: # - a map from C++ MatchingPattern expressions to their index. @@ -266,14 +275,6 @@ constexpr MatchPatternRef MakeMatchPatternRef( return MatchPatternRef(is_supplementary, index); } -// Converts an integer to the associated enum class constant. -template<typename Enum, int i> -constexpr Enum To() { - static_assert(0 <= i); - static_assert(static_cast<Enum>(i) <= Enum::kMaxValue); - return static_cast<Enum>(i); -} - // A pair of const char* used as keys in the `kPatternMap`. struct NameAndLanguage { using StringPiecePair = std::pair<base::StringPiece, base::StringPiece>; 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 c6a696b0296..0d1618ea482 100644 --- a/chromium/components/autofill/core/browser/form_parsing/travel_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/travel_field.cc @@ -7,8 +7,8 @@ #include <memory> #include <utility> -#include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/form_parsing/regex_patterns.h" +#include "components/autofill/core/common/autofill_regex_constants.h" namespace autofill { @@ -49,7 +49,7 @@ std::unique_ptr<FormField> TravelField::Parse(AutofillScanner* scanner, } void TravelField::AddClassifications( - FieldCandidatesMap* field_candidates) const { + FieldCandidatesMap& field_candidates) const { // Simply tag all the fields as unknown types. Travel is currently used as // filter. AddClassification(passport_, UNKNOWN_TYPE, kBaseTravelParserScore, 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 17784dcc45e..07d45601a45 100644 --- a/chromium/components/autofill/core/browser/form_parsing/travel_field.h +++ b/chromium/components/autofill/core/browser/form_parsing/travel_field.h @@ -25,7 +25,7 @@ class TravelField : public FormField { LogManager* log_manager); protected: - void AddClassifications(FieldCandidatesMap* field_candidates) const override; + void AddClassifications(FieldCandidatesMap& field_candidates) const override; private: // All of the following fields are optional. diff --git a/chromium/components/autofill/core/browser/form_processing/label_processing_util.cc b/chromium/components/autofill/core/browser/form_processing/label_processing_util.cc index 4a4f1c45a45..66bd8187ff0 100644 --- a/chromium/components/autofill/core/browser/form_processing/label_processing_util.cc +++ b/chromium/components/autofill/core/browser/form_processing/label_processing_util.cc @@ -29,7 +29,7 @@ absl::optional<std::vector<std::u16string>> GetParseableLabels( // the subsequent fields. size_t label_index = 0; while (label_index < labels.size()) { - const auto& label = labels.at(label_index); + const base::StringPiece16& label = labels[label_index]; // If the label is empty or has a size that exceeds // |kMaxLengthOfShareableLabel| it can not be shared with subsequent fields. if (label.empty() || label.size() > kMaxLengthOfShareableLabel) { @@ -37,16 +37,16 @@ absl::optional<std::vector<std::u16string>> GetParseableLabels( continue; } - // Otherwise search if the subsequent fields are empty. + // Otherwise search if the subsequent fields are empty or share the same + // label. Checking for the same label is needed, because label inference + // often derives the same label for consecutive fields. size_t scan_index = label_index + 1; - while (scan_index < labels.size()) { - if (!labels.at(scan_index).empty()) { - break; - } + while (scan_index < labels.size() && + (labels[scan_index].empty() || labels[scan_index] == label)) { ++scan_index; } - // After the loop, the |scan_index| points to the first subsequent field - // that does not have an empty label or is the first out-of-bound index. + // After the loop, the `scan_index` points to the first subsequent field + // which the label cannot be shared with or to the first out-of-bound index. // Calculate the number of fields that may share a label. size_t fields_to_share_label = scan_index - label_index; @@ -87,7 +87,7 @@ absl::optional<std::vector<std::u16string>> GetParseableLabels( shared_labels_found = true; // Otherwise assign the label components to the fields. for (size_t i = 0; i < label_components.size(); ++i) { - shared_labels[shared_label_starting_index + i] = label_components.at(i); + shared_labels[shared_label_starting_index + i] = label_components[i]; } } diff --git a/chromium/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc b/chromium/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc index 584ac357e2b..0b9f2a44088 100644 --- a/chromium/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc +++ b/chromium/components/autofill/core/browser/form_processing/label_processing_util_unittest.cc @@ -10,108 +10,53 @@ #include "components/autofill/core/common/autofill_features.h" #include "testing/gtest/include/gtest/gtest.h" -using base::ASCIIToUTF16; - -namespace { - -std::vector<base::StringPiece16> StringsToStringPieces( - const std::vector<std::u16string>& strings) { - std::vector<base::StringPiece16> string_pieces; - for (const auto& s : strings) { - string_pieces.emplace_back(base::StringPiece16(s)); - } - return string_pieces; -} - -} // namespace - namespace autofill { TEST(LabelProcessingUtil, GetParseableNameStringPieces) { - std::vector<std::u16string> labels; - labels.push_back(u"City"); - labels.push_back(u"Street & House Number"); - labels.push_back(u""); - labels.push_back(u"Zip"); - - auto expectation = absl::make_optional(std::vector<std::u16string>()); - expectation->push_back(u"City"); - expectation->push_back(u"Street"); - expectation->push_back(u"House Number"); - expectation->push_back(u"Zip"); - - EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); + std::vector<base::StringPiece16> labels{u"City", u"Street & House Number", + u"", u"Zip"}; + auto expectation = absl::make_optional( + std::vector<std::u16string>{u"City", u"Street", u"House Number", u"Zip"}); + EXPECT_EQ(GetParseableLabels(labels), expectation); + + // The label is also split when consecutive fields share the same label. + labels[2] = labels[1]; + EXPECT_EQ(GetParseableLabels(labels), expectation); } TEST(LabelProcessingUtil, GetParseableNameStringPieces_ThreeComponents) { - std::vector<std::u16string> labels; - labels.push_back(u"City"); - labels.push_back(u"Street & House Number & Floor"); - labels.push_back(u""); - labels.push_back(u""); - labels.push_back(u"Zip"); - - auto expectation = absl::make_optional(std::vector<std::u16string>()); - expectation->push_back(u"City"); - expectation->push_back(u"Street"); - expectation->push_back(u"House Number"); - expectation->push_back(u"Floor"); - expectation->push_back(u"Zip"); - - EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); + EXPECT_EQ(GetParseableLabels( + {u"City", u"Street & House Number & Floor", u"", u"", u"Zip"}), + absl::make_optional(std::vector<std::u16string>{ + u"City", u"Street", u"House Number", u"Floor", u"Zip"})); } TEST(LabelProcessingUtil, GetParseableNameStringPieces_TooManyComponents) { - std::vector<std::u16string> labels; - labels.push_back(u"City"); - labels.push_back(u"Street & House Number & Floor & Stairs"); - labels.push_back(u""); - labels.push_back(u""); - labels.push_back(u""); - labels.push_back(u"Zip"); - - absl::optional<std::vector<std::u16string>> expectation = absl::nullopt; - ; - - EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); + EXPECT_EQ( + GetParseableLabels({u"City", u"Street & House Number & Floor & Stairs", + u"", u"", u"", u"Zip"}), + absl::nullopt); } TEST(LabelProcessingUtil, GetParseableNameStringPieces_UnmachtingComponents) { - std::vector<std::u16string> labels; - labels.push_back(u"City"); - labels.push_back(u"Street & House Number & Floor"); - labels.push_back(u""); - labels.push_back(u"Zip"); - - absl::optional<std::vector<std::u16string>> expectation = absl::nullopt; - - EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); + EXPECT_EQ(GetParseableLabels( + {u"City", u"Street & House Number & Floor", u"", u"Zip"}), + absl::nullopt); } TEST(LabelProcessingUtil, GetParseableNameStringPieces_SplitableLabelAtEnd) { - std::vector<std::u16string> labels; - labels.push_back(u"City"); - labels.push_back(u""); - labels.push_back(u"Zip"); - labels.push_back(u"Street & House Number & Floor"); - - absl::optional<std::vector<std::u16string>> expectation = absl::nullopt; - - EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); + EXPECT_EQ(GetParseableLabels( + {u"City", u"", u"Zip", u"Street & House Number & Floor"}), + absl::nullopt); } TEST(LabelProcessingUtil, GetParseableNameStringPieces_TooLongLabel) { - std::vector<std::u16string> labels; - labels.push_back(u"City"); - labels.push_back( - u"Street & House Number with a lot of additional text that exceeds 40 " - u"characters by far"); - labels.push_back(u""); - labels.push_back(u"Zip"); - - absl::optional<std::vector<std::u16string>> expectation = absl::nullopt; - - EXPECT_EQ(GetParseableLabels(StringsToStringPieces(labels)), expectation); + EXPECT_EQ(GetParseableLabels({u"City", + u"Street & House Number with a lot of " + u"additional text that exceeds 40 " + u"characters by far", + u"", u"Zip"}), + absl::nullopt); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc b/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc index f4699090542..ce3786be67a 100644 --- a/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc +++ b/chromium/components/autofill/core/browser/form_processing/name_processing_util.cc @@ -6,9 +6,9 @@ #include "base/feature_list.h" #include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_regexes.h" namespace autofill { @@ -84,9 +84,7 @@ size_t FindLongestCommonPrefixLengthInStringsWithMinimalLength( // Returns true if |parseable_name| is a valid parseable_name. Current criterion // is the |kParseableNameValidationRe| regex. bool IsValidParseableName(const base::StringPiece16 parseable_name) { - static const std::u16string kParseableNameValidationPattern = - kParseableNameValidationRe; - return MatchesPattern(parseable_name, kParseableNameValidationPattern); + return MatchesRegex<kParseableNameValidationRe>(parseable_name); } // Tries to strip |offset_left| and |offset_right| from entriees in diff --git a/chromium/components/autofill/core/browser/form_processing/name_processing_util.h b/chromium/components/autofill/core/browser/form_processing/name_processing_util.h index 6c644d5e411..2bbb77e6531 100644 --- a/chromium/components/autofill/core/browser/form_processing/name_processing_util.h +++ b/chromium/components/autofill/core/browser/form_processing/name_processing_util.h @@ -9,7 +9,6 @@ #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/browser/autofill_regexes.h" #include "components/autofill/core/browser/form_structure.h" #include "third_party/abseil-cpp/absl/types/optional.h" diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc index d7d3c5a898a..5491f1bd32f 100644 --- a/chromium/components/autofill/core/browser/form_structure.cc +++ b/chromium/components/autofill/core/browser/form_structure.cc @@ -34,8 +34,6 @@ #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/autofill_type.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_parsing/buildflags.h" @@ -48,11 +46,14 @@ #include "components/autofill/core/browser/randomized_encoder.h" #include "components/autofill/core/browser/rationalization_util.h" #include "components/autofill/core/browser/validation.h" +#include "components/autofill/core/common/autocomplete_parsing_util.h" #include "components/autofill/core/common/autofill_constants.h" #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_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/dense_set.h" @@ -73,12 +74,6 @@ using mojom::SubmissionIndicatorEvent; namespace { -constexpr char kBillingMode[] = "billing"; -constexpr char kShippingMode[] = "shipping"; - -// Default section name for the fields. -constexpr char kDefaultSection[] = "-default"; - // Returns true if the scheme given by |url| is one for which autofill is // allowed to activate. By default this only returns true for HTTP and HTTPS. bool HasAllowedScheme(const GURL& url) { @@ -121,168 +116,6 @@ std::string EncodeFieldTypes(const ServerFieldTypeSet& available_field_types) { return data_presence; } -// Returns |true| iff the |token| is a type hint for a contact field, as -// specified in the implementation section of http://is.gd/whatwg_autocomplete -// Note that "fax" and "pager" are intentionally ignored, as Chrome does not -// support filling either type of information. -bool IsContactTypeHint(const std::string& token) { - return token == "home" || token == "work" || token == "mobile"; -} - -// Returns |true| iff the |token| is a type hint appropriate for a field of the -// given |field_type|, as specified in the implementation section of -// http://is.gd/whatwg_autocomplete -bool ContactTypeHintMatchesFieldType(const std::string& token, - HtmlFieldType field_type) { - // The "home" and "work" type hints are only appropriate for email and phone - // number field types. - if (token == "home" || token == "work") { - return field_type == HTML_TYPE_EMAIL || - (field_type >= HTML_TYPE_TEL && - field_type <= HTML_TYPE_TEL_LOCAL_SUFFIX); - } - - // The "mobile" type hint is only appropriate for phone number field types. - // Note that "fax" and "pager" are intentionally ignored, as Chrome does not - // support filling either type of information. - if (token == "mobile") { - return field_type >= HTML_TYPE_TEL && - field_type <= HTML_TYPE_TEL_LOCAL_SUFFIX; - } - - return false; -} - -// Rationalizes the HTML `type` of `field`, based on the fields properties. At -// the moment only `max_length` is considered. For example, a max_length of 4 -// might indicate a 4 digit year. -// In case no rationalization rule applies, the original type is returned. -HtmlFieldType RationalizeAutocompleteType(HtmlFieldType type, - const AutofillField& field) { - // (original-type, max-length) -> new-type - static constexpr auto rules = - base::MakeFixedFlatMap<std::pair<HtmlFieldType, uint64_t>, HtmlFieldType>( - { - {{HTML_TYPE_ADDITIONAL_NAME, 1}, - HTML_TYPE_ADDITIONAL_NAME_INITIAL}, - {{HTML_TYPE_CREDIT_CARD_EXP, 5}, - HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, - {{HTML_TYPE_CREDIT_CARD_EXP, 7}, - HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, - {{HTML_TYPE_CREDIT_CARD_EXP_YEAR, 2}, - HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR}, - {{HTML_TYPE_CREDIT_CARD_EXP_YEAR, 4}, - HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR}, - }); - - auto* it = rules.find(std::make_pair(type, field.max_length)); - return it == rules.end() ? type : it->second; -} - -// Chrome Autofill supports a subset of the field types listed at -// http://is.gd/whatwg_autocomplete. Returns the corresponding HtmlFieldType, if -// `value` matches any of them. -absl::optional<HtmlFieldType> ParseStandardizedAutocompleteAttribute( - base::StringPiece value) { - static constexpr auto standardized_attributes = - base::MakeFixedFlatMap<base::StringPiece, HtmlFieldType>({ - {"additional-name", HTML_TYPE_ADDITIONAL_NAME}, - {"address-level1", HTML_TYPE_ADDRESS_LEVEL1}, - {"address-level2", HTML_TYPE_ADDRESS_LEVEL2}, - {"address-level3", HTML_TYPE_ADDRESS_LEVEL3}, - {"address-line1", HTML_TYPE_ADDRESS_LINE1}, - {"address-line2", HTML_TYPE_ADDRESS_LINE2}, - {"address-line3", HTML_TYPE_ADDRESS_LINE3}, - {"cc-csc", HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE}, - {"cc-exp", HTML_TYPE_CREDIT_CARD_EXP}, - {"cc-exp-month", HTML_TYPE_CREDIT_CARD_EXP_MONTH}, - {"cc-exp-year", HTML_TYPE_CREDIT_CARD_EXP_YEAR}, - {"cc-family-name", HTML_TYPE_CREDIT_CARD_NAME_LAST}, - {"cc-given-name", HTML_TYPE_CREDIT_CARD_NAME_FIRST}, - {"cc-name", HTML_TYPE_CREDIT_CARD_NAME_FULL}, - {"cc-number", HTML_TYPE_CREDIT_CARD_NUMBER}, - {"cc-type", HTML_TYPE_CREDIT_CARD_TYPE}, - {"country", HTML_TYPE_COUNTRY_CODE}, - {"country-name", HTML_TYPE_COUNTRY_NAME}, - {"email", HTML_TYPE_EMAIL}, - {"family-name", HTML_TYPE_FAMILY_NAME}, - {"given-name", HTML_TYPE_GIVEN_NAME}, - {"honorific-prefix", HTML_TYPE_HONORIFIC_PREFIX}, - {"name", HTML_TYPE_NAME}, - {"one-time-code", HTML_TYPE_ONE_TIME_CODE}, - {"organization", HTML_TYPE_ORGANIZATION}, - {"postal-code", HTML_TYPE_POSTAL_CODE}, - {"street-address", HTML_TYPE_STREET_ADDRESS}, - {"tel-area-code", HTML_TYPE_TEL_AREA_CODE}, - {"tel-country-code", HTML_TYPE_TEL_COUNTRY_CODE}, - {"tel-extension", HTML_TYPE_TEL_EXTENSION}, - {"tel", HTML_TYPE_TEL}, - {"tel-local", HTML_TYPE_TEL_LOCAL}, - {"tel-local-prefix", HTML_TYPE_TEL_LOCAL_PREFIX}, - {"tel-local-suffix", HTML_TYPE_TEL_LOCAL_SUFFIX}, - {"tel-national", HTML_TYPE_TEL_NATIONAL}, - {"transaction-amount", HTML_TYPE_TRANSACTION_AMOUNT}, - {"transaction-currency", HTML_TYPE_TRANSACTION_CURRENCY}, - }); - - auto* it = standardized_attributes.find(value); - return it != standardized_attributes.end() - ? absl::optional<HtmlFieldType>(it->second) - : absl::nullopt; -} - -// Tries mapping a non-standardized html autocomplete `value` to an -// HtmlFieldType. -absl::optional<HtmlFieldType> ParseAutocompleteAttributeExtensions( - base::StringPiece value) { - static constexpr auto extensions = - base::MakeFixedFlatMap<base::StringPiece, HtmlFieldType>({ - {"address", HTML_TYPE_STREET_ADDRESS}, - {"company", HTML_TYPE_ORGANIZATION}, - {"coupon-code", HTML_TYPE_MERCHANT_PROMO_CODE}, - {"first-name", HTML_TYPE_GIVEN_NAME}, - {"gift-code", HTML_TYPE_MERCHANT_PROMO_CODE}, - {"locality", HTML_TYPE_ADDRESS_LEVEL2}, - {"promo-code", HTML_TYPE_MERCHANT_PROMO_CODE}, - {"promotional-code", HTML_TYPE_MERCHANT_PROMO_CODE}, - {"promotion-code", HTML_TYPE_MERCHANT_PROMO_CODE}, - {"region", HTML_TYPE_ADDRESS_LEVEL1}, - {"tel-ext", HTML_TYPE_TEL_EXTENSION}, - {"upi", HTML_TYPE_UPI_VPA}, - {"upi-vpa", HTML_TYPE_UPI_VPA}, - {"username", HTML_TYPE_EMAIL}, - }); - - auto* it = extensions.find(value); - return it != extensions.end() ? absl::optional<HtmlFieldType>(it->second) - : absl::nullopt; -} - -// Returns the Chrome Autofill-supported field type corresponding to a given -// autocomplete `value`, if there is one, in the context of the given -// `field`. -HtmlFieldType FieldTypeFromAutocompleteAttributeValue( - std::string value, - const AutofillField& field) { - if (value.empty()) - return HTML_TYPE_UNSPECIFIED; - - // We are lenient and accept '_' instead of '-' as a separator. E.g. - // "given_name" is treated like "given-name". - base::ReplaceChars(value, "_", "-", &value); - // We accept e.g. "phone-country" instead of "tel-country". - if (base::StartsWith(value, "phone")) - base::ReplaceFirstSubstringAfterOffset(&value, 0, "phone", "tel"); - - absl::optional<HtmlFieldType> type = - ParseStandardizedAutocompleteAttribute(value); - if (!type.has_value()) - type = ParseAutocompleteAttributeExtensions(value); - - return type.has_value() ? RationalizeAutocompleteType(type.value(), field) - : HTML_TYPE_UNRECOGNIZED; -} - std::ostream& operator<<(std::ostream& out, const AutofillQueryResponse& response) { for (const auto& form : response.form_suggestions()) { @@ -447,6 +280,12 @@ void PopulateRandomizedFieldMetadata( field.placeholder, /*include_checksum=*/false, metadata->mutable_placeholder()); } + if (!field.autocomplete_attribute.empty()) { + EncodeRandomizedValue( + encoder, form_signature, field_signature, + RandomizedEncoder::FIELD_AUTOCOMPLETE, field.autocomplete_attribute, + /*include_checksum=*/false, metadata->mutable_autocomplete()); + } } // Defines necessary types for the rationalization logic, meaning that fields of @@ -456,7 +295,7 @@ void PopulateRandomizedFieldMetadata( ServerFieldTypeSet GetNecessaryTypesFor(ServerFieldType type) { switch (type) { case PHONE_HOME_COUNTRY_CODE: - return {PHONE_HOME_NUMBER, + return {PHONE_HOME_NUMBER, PHONE_HOME_NUMBER_PREFIX, base::FeatureList::IsEnabled( features::kAutofillEnableSupportForPhoneNumberTrunkTypes) ? PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX @@ -466,39 +305,6 @@ ServerFieldTypeSet GetNecessaryTypesFor(ServerFieldType type) { } } -LogBufferSubmitter LogRationalization(LogManager* log_manager) { - if (!log_manager) - return LogManager::DevNull(); - LogBufferSubmitter submitter = log_manager->Log(); - submitter << LoggingScope::kRationalization << LogMessage::kRationalization; - return submitter; -} - -// Creates a unique name for the section that starts with |field|. -// -// The section name is a string of the form "%s_%u_%u", where the first string -// is the field's name and the two integers are the field's frame ID and its -// renderer ID. -// -// For the frame ID, we do not use LocalFrameTokens but instead map them to -// consecutive integers using |frame_token_ids|, which uniquely identify a frame -// within a given FormStructure. Since we do not intend to compare sections from -// different FormStructures, this is sufficient. -// -// We intentionally do not include the LocalFrameToken in the section string -// because frame tokens should not be sent to a renderer. -// -// TODO(crbug.com/1257141): Remove special handling of FrameTokens. -std::u16string GetSectionName( - const AutofillField& field, - base::flat_map<LocalFrameToken, size_t>& frame_token_ids) { - size_t id = frame_token_ids.emplace(field.host_frame, frame_token_ids.size()) - .first->second; - return base::StrCat( - {field.name, u"_", base::NumberToString16(id), u"_", - base::NumberToString16(field.unique_renderer_id.value())}); -} - } // namespace class FormStructure::SectionedFieldsIndexes { @@ -780,10 +586,8 @@ void FormStructure::ProcessQueryResponse( using FieldSuggestion = AutofillQueryResponse::FormSuggestion::FieldSuggestion; AutofillMetrics::LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_PARSED); - if (log_manager) { - log_manager->Log() << LoggingScope::kParsing - << LogMessage::kProcessingServerData; - } + LOG_AF(log_manager) << LoggingScope::kParsing + << LogMessage::kProcessingServerData; bool heuristics_detected_fillable_field = false; bool query_response_overrode_heuristics = false; @@ -909,7 +713,7 @@ std::vector<FormDataPredictions> FormStructure::GetFieldTypePredictions( annotated_field.overall_type = field->Type().ToString(); annotated_field.parseable_name = base::UTF16ToUTF8(field->parseable_name()); - annotated_field.section = field->section; + annotated_field.section = field->section.ToString(); form.fields.push_back(annotated_field); } @@ -988,10 +792,8 @@ void FormStructure::UpdateAutofillCount() { bool FormStructure::ShouldBeParsed(LogManager* log_manager) const { // Exclude URLs not on the web via HTTP(S). if (!HasAllowedScheme(source_url_)) { - if (log_manager) { - log_manager->Log() << LoggingScope::kAbortParsing - << LogMessage::kAbortParsingNotAllowedScheme << *this; - } + LOG_AF(log_manager) << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingNotAllowedScheme << *this; return false; } @@ -1002,35 +804,28 @@ bool FormStructure::ShouldBeParsed(LogManager* log_manager) const { (!all_fields_are_passwords() || active_field_count() < kRequiredFieldsForFormsWithOnlyPasswordFields) && !has_author_specified_types_) { - if (log_manager) { - log_manager->Log() << LoggingScope::kAbortParsing - << LogMessage::kAbortParsingNotEnoughFields - << active_field_count() << *this; - } + LOG_AF(log_manager) << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingNotEnoughFields + << active_field_count() << *this; return false; } // Rule out search forms. - if (MatchesPattern(base::UTF8ToUTF16(target_url_.path_piece()), - kUrlSearchActionRe)) { - if (log_manager) { - log_manager->Log() << LoggingScope::kAbortParsing - << LogMessage::kAbortParsingUrlMatchesSearchRegex - << *this; - } + if (MatchesRegex<kUrlSearchActionRe>( + base::UTF8ToUTF16(target_url_.path_piece()))) { + LOG_AF(log_manager) << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingUrlMatchesSearchRegex + << *this; return false; } - bool has_text_field = false; - for (const auto& it : *this) { - has_text_field |= it->form_control_type != "select-one"; + bool has_text_field = base::ranges::any_of(*this, [](const auto& field) { + return field->form_control_type != "select-one"; + }); + if (!has_text_field) { + LOG_AF(log_manager) << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingFormHasNoTextfield << *this; } - - if (!has_text_field && log_manager) { - log_manager->Log() << LoggingScope::kAbortParsing - << LogMessage::kAbortParsingFormHasNoTextfield << *this; - } - return has_text_field; } @@ -1039,10 +834,8 @@ bool FormStructure::ShouldRunHeuristics() const { HasAllowedScheme(source_url_); } -bool FormStructure::ShouldRunPromoCodeHeuristics() const { - return base::FeatureList::IsEnabled( - features::kAutofillParseMerchantPromoCodeFields) && - active_field_count() > 0 && HasAllowedScheme(source_url_); +bool FormStructure::ShouldRunHeuristicsForSingleFieldForms() const { + return active_field_count() > 0 && HasAllowedScheme(source_url_); } bool FormStructure::ShouldBeQueried() const { @@ -1460,106 +1253,33 @@ void FormStructure::ParseFieldTypesFromAutocompleteAttributes() { has_author_specified_sections_ = false; has_author_specified_upi_vpa_hint_ = false; for (const std::unique_ptr<AutofillField>& field : fields_) { - // To prevent potential section name collisions, add a default suffix for - // other fields. Without this, 'autocomplete' attribute values - // "section--shipping street-address" and "shipping street-address" would be - // parsed identically, given the section handling code below. We do this - // before any validation so that fields with invalid attributes still end up - // in the default section. These default section names will be overridden - // by subsequent heuristic parsing steps if there are no author-specified - // section names. - field->section = kDefaultSection; - - std::vector<std::string> tokens = - LowercaseAndTokenizeAttributeString(field->autocomplete_attribute); - - // The autocomplete attribute is overloaded: it can specify either a field - // type hint or whether autocomplete should be enabled at all. Ignore the - // latter type of attribute value. - if (tokens.empty() || - (tokens.size() == 1 && - (tokens[0] == "on" || tokens[0] == "off" || tokens[0] == "false"))) { + field->section = Section(); + auto parsing_result = ParseAutocompleteAttribute(*field); + if (!parsing_result) continue; - } - // Any other value, even it is invalid, is considered to be a type hint. - // This allows a website's author to specify an attribute like - // autocomplete="other" on a field to disable all Autofill heuristics for - // the form. + // A parsable autocomplete value was specified. Even an invalid field_type + // is considered a type hint. This allows a website's author to specify an + // attribute like autocomplete="other" on a field to disable all Autofill + // heuristics for the form. has_author_specified_types_ = true; - - // Per the spec, the tokens are parsed in reverse order. The expected - // pattern is: - // [section-*] [shipping|billing] [type_hint] field_type - - // (1) The final token must be the field type. If it is not one of the known - // types, abort. - std::string field_type_token = tokens.back(); - tokens.pop_back(); - HtmlFieldType field_type = - FieldTypeFromAutocompleteAttributeValue(field_type_token, *field); - if (field_type == HTML_TYPE_UPI_VPA) { - has_author_specified_upi_vpa_hint_ = true; - // TODO(crbug.com/702223): Flesh out support for UPI-VPA. - field_type = HTML_TYPE_UNRECOGNIZED; - } - if (field_type == HTML_TYPE_UNSPECIFIED) + if (parsing_result->field_type == HTML_TYPE_UNSPECIFIED) continue; - // (2) The preceding token, if any, may be a type hint. - if (!tokens.empty() && IsContactTypeHint(tokens.back())) { - // If it is, it must match the field type; otherwise, abort. - // Note that an invalid token invalidates the entire attribute value, even - // if the other tokens are valid. - if (!ContactTypeHintMatchesFieldType(tokens.back(), field_type)) - continue; - - // Chrome Autofill ignores these type hints. - tokens.pop_back(); - } - - DCHECK_EQ(kDefaultSection, field->section); - std::string section = field->section; - HtmlFieldMode mode = HTML_MODE_NONE; - - // (3) The preceding token, if any, may be a fixed string that is either - // "shipping" or "billing". Chrome Autofill treats these as implicit - // section name suffixes. - if (!tokens.empty()) { - if (tokens.back() == kShippingMode) - mode = HTML_MODE_SHIPPING; - else if (tokens.back() == kBillingMode) - mode = HTML_MODE_BILLING; - - if (mode != HTML_MODE_NONE) { - section = "-" + tokens.back(); - tokens.pop_back(); - } - } - - // (4) The preceding token, if any, may be a named section. - const base::StringPiece kSectionPrefix = "section-"; - if (!tokens.empty() && base::StartsWith(tokens.back(), kSectionPrefix, - base::CompareCase::SENSITIVE)) { - // Prepend this section name to the suffix set in the preceding block. - section = tokens.back().substr(kSectionPrefix.size()) + section; - tokens.pop_back(); + // TODO(crbug.com/702223): Flesh out support for UPI-VPA. + if (parsing_result->field_type == HTML_TYPE_UPI_VPA) { + has_author_specified_upi_vpa_hint_ = true; + parsing_result->field_type = HTML_TYPE_UNRECOGNIZED; } - // (5) No other tokens are allowed. If there are any remaining, abort. - if (!tokens.empty()) - continue; - - if (section != kDefaultSection) { + // Compute a section name based on the specified hints and apply the result. + if (field->section.SetPrefixFromAutocomplete( + {.section = parsing_result->section, + .mode = parsing_result->mode})) { has_author_specified_sections_ = true; - field->section = section; } - - // No errors encountered while parsing! - // Update the |field|'s type based on what was parsed from the attribute. - field->SetHtmlType(field_type, mode); + field->SetHtmlType(parsing_result->field_type, parsing_result->mode); } - was_parsed_for_autocomplete_attributes_ = true; } @@ -1567,13 +1287,12 @@ void FormStructure::ParseFieldTypesWithPatterns(PatternSource pattern_source, LogManager* log_manager) { FieldCandidatesMap field_type_map; if (ShouldRunHeuristics()) { - field_type_map = - FormField::ParseFormFields(fields_, current_page_language_, - is_form_tag_, pattern_source, log_manager); - } else if (ShouldRunPromoCodeHeuristics()) { - field_type_map = FormField::ParseFormFieldsForPromoCodes( - fields_, current_page_language_, is_form_tag_, pattern_source, - log_manager); + FormField::ParseFormFields(fields_, current_page_language_, is_form_tag_, + pattern_source, field_type_map, log_manager); + } else if (ShouldRunHeuristicsForSingleFieldForms()) { + FormField::ParseSingleFieldForms(fields_, current_page_language_, + is_form_tag_, pattern_source, + field_type_map, log_manager); } if (field_type_map.empty()) return; @@ -1680,7 +1399,6 @@ void FormStructure::RationalizeCreditCardFieldPredictions( cc_cvc_found = true; break; case ADDRESS_HOME_ZIP: - case ADDRESS_BILLING_ZIP: // Zip/Postal code often appears as part of a Credit Card form. Do // not count it as a non-cc-related field. break; @@ -1712,7 +1430,8 @@ void FormStructure::RationalizeCreditCardFieldPredictions( cc_num_found || num_cc_fields_found >= 3 || num_other_fields_found == 0; if (!keep_cc_fields && num_cc_fields_found && log_manager) { - LogRationalization(log_manager) + LOG_AF(log_manager) + << LoggingScope::kRationalization << LogMessage::kRationalization << "Credit card rationalization: Did not find credit card number, did " "not find >= 3 credit card fields (" << num_cc_fields_found << "), and had non-cc fields (" @@ -1759,7 +1478,9 @@ void FormStructure::RationalizeCreditCardFieldPredictions( // month field(s) not immediately preceding an expiry year field. if (!keep_cc_fields || !cc_date_found) { if (!cc_date_found && log_manager) { - LogRationalization(log_manager) + LOG_AF(log_manager) + << LoggingScope::kRationalization + << LogMessage::kRationalization << "Credit card rationalization: Found CC expiration month but " "not a full date."; } @@ -1768,7 +1489,9 @@ void FormStructure::RationalizeCreditCardFieldPredictions( auto it2 = it + 1; if (it2 == fields_.end()) { field->SetTypeTo(AutofillType(UNKNOWN_TYPE)); - LogRationalization(log_manager) + LOG_AF(log_manager) + << LoggingScope::kRationalization + << LogMessage::kRationalization << "Credit card rationalization: Found multiple expiration " "months and the last field was an expiration month"; field->SetTypeTo(AutofillType(UNKNOWN_TYPE)); @@ -1778,7 +1501,9 @@ void FormStructure::RationalizeCreditCardFieldPredictions( next_field_type != CREDIT_CARD_EXP_4_DIGIT_YEAR) { field->SetTypeTo(AutofillType(UNKNOWN_TYPE)); } - LogRationalization(log_manager) + LOG_AF(log_manager) + << LoggingScope::kRationalization + << LogMessage::kRationalization << "Credit card rationalization: Found multiple expiration " "months and the field following one is not an " "expiration year but " @@ -1791,7 +1516,9 @@ void FormStructure::RationalizeCreditCardFieldPredictions( if (!keep_cc_fields || !cc_date_found) { field->SetTypeTo(AutofillType(UNKNOWN_TYPE)); if (!cc_date_found && log_manager) { - LogRationalization(log_manager) + LOG_AF(log_manager) + << LoggingScope::kRationalization + << LogMessage::kRationalization << "Credit card rationalization: Found expiration year but no " "full expriration date."; } @@ -1825,7 +1552,8 @@ void FormStructure::RationalizeStreetAddressAndAddressLine( continue; } if (log_manager) { - LogRationalization(log_manager) + LOG_AF(log_manager) + << LoggingScope::kRationalization << LogMessage::kRationalization << "Street Address Rationalization: Converting sequence of (street " "address, address line 2) to (address line 1, address line 2)"; } @@ -1833,9 +1561,8 @@ void FormStructure::RationalizeStreetAddressAndAddressLine( } } -void FormStructure::RationalizePhoneNumbersInSection( - const std::string& section) { - if (phone_rationalized_[section]) +void FormStructure::RationalizePhoneNumbersInSection(const Section& section) { + if (base::Contains(phone_rationalized_, section)) return; std::vector<AutofillField*> fields; for (size_t i = 0; i < field_count(); ++i) { @@ -1844,7 +1571,7 @@ void FormStructure::RationalizePhoneNumbersInSection( fields.push_back(field(i)); } rationalization_util::RationalizePhoneNumberFields(fields); - phone_rationalized_[section] = true; + phone_rationalized_.insert(section); } void FormStructure::ApplyRationalizationsToFieldAndLog( @@ -1880,24 +1607,30 @@ void FormStructure::RationalizeAddressLineFields( int nb_address_rationalized = 0; for (auto field_index : *current_section) { - LogBufferSubmitter log_submitter = LogRationalization(log_manager); - log_submitter + LOG_AF(log_manager) + << LoggingScope::kRationalization << LogMessage::kRationalization << "RationalizeAddressLineFields ADDRESS_HOME_STREET_ADDRESS to "; switch (nb_address_rationalized) { case 0: ApplyRationalizationsToFieldAndLog(field_index, ADDRESS_HOME_LINE1, form_interactions_ukm_logger); - log_submitter << "ADDRESS_HOME_LINE1"; + LOG_AF(log_manager) + << LoggingScope::kRationalization << LogMessage::kRationalization + << "ADDRESS_HOME_LINE1"; break; case 1: ApplyRationalizationsToFieldAndLog(field_index, ADDRESS_HOME_LINE2, form_interactions_ukm_logger); - log_submitter << "ADDRESS_HOME_LINE2"; + LOG_AF(log_manager) + << LoggingScope::kRationalization << LogMessage::kRationalization + << "ADDRESS_HOME_LINE2"; break; case 2: ApplyRationalizationsToFieldAndLog(field_index, ADDRESS_HOME_LINE3, form_interactions_ukm_logger); - log_submitter << "ADDRESS_HOME_LINE3"; + LOG_AF(log_manager) + << LoggingScope::kRationalization << LogMessage::kRationalization + << "ADDRESS_HOME_LINE3"; break; default: NOTREACHED(); @@ -2005,14 +1738,13 @@ void FormStructure::RationalizeAddressStateCountry( AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger, LogManager* log_manager) { // Walk on the sections of state and country indexes simultaneously. If they - // both point to the same section, it means that that section includes both - // the country and the state type. This means that no that rationalization is - // needed. So, walk both pointers forward. Otherwise, look at the section that - // appears earlier on the form. That section doesn't have any field of the - // other type. Rationalize the fields on the earlier section if needed. Walk - // the pointer that points to the earlier section forward. Stop when both - // sections of indexes are processed. (This resembles the merge in the merge - // sort.) + // both point to the same section, it means that the section includes both the + // country and the state type. This means that no rationalization is needed. + // So, walk both pointers forward. Otherwise, look at the section that appears + // earlier on the form. That section doesn't have any field of the other type. + // Rationalize the fields on the earlier section if needed. Walk the pointer + // that points to the earlier section forward. Stop when both sections of + // indexes are processed. (This resembles the merge in the merge sort.) sections_of_state_indexes->Reset(); sections_of_country_indexes->Reset(); @@ -2073,7 +1805,8 @@ void FormStructure::RationalizeAddressStateCountry( ApplyRationalizationsToFields( upper_index, lower_index, fields_[upper_index]->heuristic_type(), fields_[lower_index]->heuristic_type(), form_interactions_ukm_logger); - LogRationalization(log_manager) + LOG_AF(log_manager) + << LoggingScope::kRationalization << LogMessage::kRationalization << "RationalizeAddressStateCountry: Heuristics are applicable"; continue; } @@ -2082,14 +1815,18 @@ void FormStructure::RationalizeAddressStateCountry( ApplyRationalizationsToFields(upper_index, lower_index, ADDRESS_HOME_COUNTRY, ADDRESS_HOME_STATE, form_interactions_ukm_logger); - LogRationalization(log_manager) << "RationalizeAddressStateCountry: " - "FieldShouldBeRationalizedToCountry"; + LOG_AF(log_manager) << LoggingScope::kRationalization + << LogMessage::kRationalization + << "RationalizeAddressStateCountry: " + "FieldShouldBeRationalizedToCountry"; } else { ApplyRationalizationsToFields(upper_index, lower_index, ADDRESS_HOME_STATE, ADDRESS_HOME_COUNTRY, form_interactions_ukm_logger); - LogRationalization(log_manager) << "RationalizeAddressStateCountry: " - "!FieldShouldBeRationalizedToCountry"; + LOG_AF(log_manager) << LoggingScope::kRationalization + << LogMessage::kRationalization + << "RationalizeAddressStateCountry: " + "!FieldShouldBeRationalizedToCountry"; } } } @@ -2129,8 +1866,6 @@ void FormStructure::RationalizeRepeatedFields( RationalizeAddressLineFields( &(sectioned_field_indexes_by_type[ADDRESS_HOME_STREET_ADDRESS]), form_interactions_ukm_logger, log_manager); - // Since the billing types are mapped to the non-billing ones, no need to - // take care of ADDRESS_BILLING_STATE and .. . RationalizeAddressStateCountry( &(sectioned_field_indexes_by_type[ADDRESS_HOME_STATE]), &(sectioned_field_indexes_by_type[ADDRESS_HOME_COUNTRY]), @@ -2140,9 +1875,8 @@ void FormStructure::RationalizeRepeatedFields( void FormStructure::RationalizeFieldTypePredictions(LogManager* log_manager) { RationalizeCreditCardFieldPredictions(log_manager); RationalizeStreetAddressAndAddressLine(log_manager); - for (const auto& field : fields_) { + for (const auto& field : fields_) field->SetTypeTo(field->Type()); - } RationalizeTypeRelationships(log_manager); } @@ -2295,9 +2029,11 @@ void FormStructure::IdentifySectionsWithNewMethod() { base::FeatureList::IsEnabled( features::kAutofillSectionUponRedundantNameInfo); + // Create a unique identifier for the section based on the field. base::flat_map<LocalFrameToken, size_t> frame_token_ids; - std::u16string current_section = - GetSectionName(*fields_.front(), frame_token_ids); + Section current_section; + current_section.SetPrefixFromFieldIdentifier(*fields_.front(), + frame_token_ids); // Keep track of the types we've seen in this section. ServerFieldTypeSet seen_types; @@ -2308,13 +2044,13 @@ void FormStructure::IdentifySectionsWithNewMethod() { bool previous_autocomplete_section_present = false; bool is_hidden_section = false; - std::u16string last_visible_section; + Section last_visible_section; for (const auto& field : fields_) { const ServerFieldType current_type = field->Type().GetStorableType(); // All credit card fields belong to the same section that's different // from address sections. if (AutofillType(current_type).group() == FieldTypeGroup::kCreditCard) { - field->section = "credit-card"; + field->section.SetPrefixToCreditCard(); continue; } @@ -2365,24 +2101,20 @@ void FormStructure::IdentifySectionsWithNewMethod() { if (current_type == previous_type) already_saw_current_type = false; - // Boolean flag that is set to true when the |field| has - // autocomplete-section attribute defined. - bool autocomplete_section_attribute_present = - (field->section != kDefaultSection); - - // Boolean flag that is set to true when the |field| has - // autocomplete-section attribute defined and is different that the - // previous field. - bool different_autocomplete_section_than_previous = - (autocomplete_section_attribute_present && - (!field_index || fields_[field_index - 1]->section != field->section)); - - // Start a new section if the |current_type| was already seen or the - // autocomplete-section attribute is defined for the |field| which is - // different than the previous field. + // Boolean flag that is set to true when the section of the `field` is + // derived from the autocomplete attribute and its section is different than + // the previous field's section. + bool different_autocomplete_section_than_previous_field_section = + field->section.is_from_autocomplete() && + (field_index == 0 || + fields_[field_index - 1]->section != field->section); + + // Start a new section if the `current_type` was already seen or the section + // is derived from the autocomplete attribute which is different than the + // previous field's section. if (current_type != UNKNOWN_TYPE && (already_saw_current_type || - different_autocomplete_section_than_previous)) { + different_autocomplete_section_than_previous_field_section)) { // Keep track of seen_types if the new section is hidden. The next // visible section might be the continuation of the previous visible // section. @@ -2391,33 +2123,34 @@ void FormStructure::IdentifySectionsWithNewMethod() { last_visible_section = current_section; } - if (!is_hidden_section && (!autocomplete_section_attribute_present || - different_autocomplete_section_than_previous)) + if (!is_hidden_section && + (!field->section.is_from_autocomplete() || + different_autocomplete_section_than_previous_field_section)) { seen_types.clear(); + } - if (autocomplete_section_attribute_present && + if (field->section.is_from_autocomplete() && !previous_autocomplete_section_present) { // If this field is the first field within the section with a defined // autocomplete section, then change the section attribute of all the - // parsed fields in the current section to |field->section|. + // parsed fields in the current section to `field->section`. int i = static_cast<int>(field_index - 1); - while (i >= 0 && - base::UTF8ToUTF16(fields_[i]->section) == current_section) { + while (i >= 0 && fields_[i]->section == current_section) { fields_[i]->section = field->section; i--; } } // The end of a section, so start a new section. - current_section = GetSectionName(*field, frame_token_ids); + current_section.SetPrefixFromFieldIdentifier(*field, frame_token_ids); // The section described in the autocomplete section attribute // overrides the value determined by the heuristic. - if (autocomplete_section_attribute_present) - current_section = base::UTF8ToUTF16(field->section); + if (field->section.is_from_autocomplete()) + current_section = field->section; previous_autocomplete_section_present = - autocomplete_section_attribute_present; + field->section.is_from_autocomplete(); } // Only consider a type "seen" if it was not ignored. Some forms have @@ -2433,17 +2166,17 @@ void FormStructure::IdentifySectionsWithNewMethod() { is_hidden_section = false; } - field->section = base::UTF16ToUTF8(current_section); + field->section = current_section; } // Ensure that credit card and address fields are in separate sections. // This simplifies the section-aware logic in autofill_manager.cc. for (const auto& field : fields_) { FieldTypeGroup field_type_group = field->Type().group(); - if (field_type_group == FieldTypeGroup::kCreditCard) - field->section = field->section + "-cc"; - else - field->section = field->section + "-default"; + field->section.set_field_type_group( + field_type_group == FieldTypeGroup::kCreditCard + ? Section::FieldTypeGroupSuffix::kCreditCard + : Section::FieldTypeGroupSuffix::kDefault); } // Since this function has changed the sections, subsequent calls to @@ -2467,22 +2200,24 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { features::kAutofillSectionUponRedundantNameInfo); if (!has_author_specified_sections) { + // Create a unique identifier based on the field for the section. base::flat_map<LocalFrameToken, size_t> frame_token_ids; - std::u16string current_section = - GetSectionName(*fields_.front(), frame_token_ids); + Section current_section; + current_section.SetPrefixFromFieldIdentifier(*fields_.front(), + frame_token_ids); // Keep track of the types we've seen in this section. ServerFieldTypeSet seen_types; ServerFieldType previous_type = UNKNOWN_TYPE; bool is_hidden_section = false; - std::u16string last_visible_section; + Section last_visible_section; for (const auto& field : fields_) { const ServerFieldType current_type = field->Type().GetStorableType(); // All credit card fields belong to the same section that's different // from address sections. if (AutofillType(current_type).group() == FieldTypeGroup::kCreditCard) { - field->section = "credit-card"; + field->section.SetPrefixToCreditCard(); continue; } @@ -2533,9 +2268,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { if (current_type == previous_type) already_saw_current_type = false; - // Start a new section if the |current_type| was already seen or the - // autocomplete-section attribute is defined for the |field| which is - // different than the previous field. + // Start a new section if the |current_type| was already seen. if (current_type != UNKNOWN_TYPE && already_saw_current_type) { // Keep track of seen_types if the new section is hidden. The next // visible section might be the continuation of the previous visible @@ -2549,7 +2282,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { seen_types.clear(); // The end of a section, so start a new section. - current_section = GetSectionName(*field, frame_token_ids); + current_section.SetPrefixFromFieldIdentifier(*field, frame_token_ids); } // Only consider a type "seen" if it was not ignored. Some forms have @@ -2565,7 +2298,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { is_hidden_section = false; } - field->section = base::UTF16ToUTF8(current_section); + field->section = current_section; } } @@ -2573,10 +2306,10 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { // This simplifies the section-aware logic in autofill_manager.cc. for (const auto& field : fields_) { FieldTypeGroup field_type_group = field->Type().group(); - if (field_type_group == FieldTypeGroup::kCreditCard) - field->section = field->section + "-cc"; - else - field->section = field->section + "-default"; + field->section.set_field_type_group( + field_type_group == FieldTypeGroup::kCreditCard + ? Section::FieldTypeGroupSuffix::kCreditCard + : Section::FieldTypeGroupSuffix::kDefault); } // Since this function has changed the sections, subsequent calls to @@ -2678,7 +2411,7 @@ void FormStructure::RationalizeTypeRelationships(LogManager* log_manager) { // We have relationship rules for this type, but no `neccessary_type` was // found. Disabling Autofill for this field. field->SetTypeTo(AutofillType(UNKNOWN_TYPE)); - LogRationalization(log_manager) + LOG_AF(log_manager) << "RationalizeTypeRelationships: Fields of type " << FieldTypeToStringPiece(field_type) << " can only exist if other fields of specific types exist."; @@ -2825,8 +2558,14 @@ LogBuffer& operator<<(LogBuffer& buffer, const FormStructure& form) { buffer << Tr{} << "Section:" << field->section; constexpr size_t kMaxLabelSize = 100; + // TODO(crbug/1165780): Remove once shared labels are launched. + const std::u16string& label = + base::FeatureList::IsEnabled( + features::kAutofillEnableSupportForParsingWithSharedLabels) + ? field->parseable_label() + : field->label; const std::u16string truncated_label = - field->label.substr(0, std::min(field->label.length(), kMaxLabelSize)); + label.substr(0, std::min(label.length(), kMaxLabelSize)); buffer << Tr{} << "Label:" << truncated_label; buffer << Tr{} << "Is empty:" << (field->IsEmpty() ? "Yes" : "No"); diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h index ca9a21f1382..1c0859df4e6 100644 --- a/chromium/components/autofill/core/browser/form_structure.h +++ b/chromium/components/autofill/core/browser/form_structure.h @@ -7,7 +7,6 @@ #include <stddef.h> -#include <map> #include <memory> #include <set> #include <string> @@ -25,6 +24,7 @@ #include "components/autofill/core/browser/metrics/form_interactions_counter.h" #include "components/autofill/core/browser/proto/api_v1.pb.h" #include "components/autofill/core/common/dense_set.h" +#include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/language_code.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" #include "components/autofill/core/common/unique_ids.h" @@ -171,9 +171,10 @@ class FormStructure { // this form. bool ShouldRunHeuristics() const; - // Returns true if heuristic autofill type detection for promo codes should be - // attempted for this form. - bool ShouldRunPromoCodeHeuristics() const; + // Returns true if autofill's heuristic field type detection should be + // attempted for this form given that |kMinRequiredFieldsForHeuristics| is not + // met. + bool ShouldRunHeuristicsForSingleFieldForms() const; // Returns true if we should query the crowd-sourcing server to determine this // form's field types. If the form includes author-specified types, this will @@ -246,7 +247,7 @@ class FormStructure { // Rationalize phone number fields in a given section, that is only fill // the fields that are considered composing a first complete phone number. - void RationalizePhoneNumbersInSection(const std::string& section); + void RationalizePhoneNumbersInSection(const Section& section); // Overrides server predictions with specific heuristic predictions: // * NAME_LAST_SECOND heuristic predictions are unconditionally used. @@ -667,7 +668,7 @@ class FormStructure { base::TimeTicks form_parsed_timestamp_; // If phone number rationalization has been performed for a given section. - std::map<std::string, bool> phone_rationalized_; + std::set<Section> phone_rationalized_; // True iff the form is a password form and the user has seen the password // value before accepting the prompt to save. Used for crowdsourcing. diff --git a/chromium/components/autofill/core/browser/test_form_structure.cc b/chromium/components/autofill/core/browser/form_structure_test_api.cc index cbf12d6c15b..a1a1b79a6b9 100644 --- a/chromium/components/autofill/core/browser/test_form_structure.cc +++ b/chromium/components/autofill/core/browser/form_structure_test_api.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/browser/test_form_structure.h" +#include "components/autofill/core/browser/form_structure_test_api.h" #include "components/autofill/core/browser/form_parsing/field_candidates.h" #include "testing/gmock/include/gmock/gmock.h" @@ -15,39 +15,19 @@ using ::testing::Contains; using ::testing::Each; using ::testing::Pair; -TestFormStructure::TestFormStructure(const FormData& form) - : FormStructure(form) {} - -TestFormStructure::~TestFormStructure() {} - -void TestFormStructure::SetFieldTypes( - const std::vector<ServerFieldType>& heuristic_types, - const std::vector<ServerFieldType>& server_types) { - std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>> - all_heuristic_types; - - base::ranges::transform( - heuristic_types, std::back_inserter(all_heuristic_types), - [](ServerFieldType type) - -> std::vector<std::pair<PatternSource, ServerFieldType>> { - return {{GetActivePatternSource(), type}}; - }); - - SetFieldTypes(all_heuristic_types, server_types); -} - -void TestFormStructure::SetFieldTypes( +// static +void FormStructureTestApi::SetFieldTypes( const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>& heuristic_types, const std::vector<ServerFieldType>& server_types) { - ASSERT_EQ(field_count(), heuristic_types.size()); - ASSERT_EQ(field_count(), server_types.size()); + ASSERT_EQ(form_structure_->field_count(), heuristic_types.size()); + ASSERT_EQ(form_structure_->field_count(), server_types.size()); ASSERT_THAT(heuristic_types, Each(Contains(Pair(GetActivePatternSource(), _)))) << "There must be a default heuristic prediction for every field."; - for (size_t i = 0; i < field_count(); ++i) { - AutofillField* form_field = field(i); + for (size_t i = 0; i < form_structure_->field_count(); ++i) { + AutofillField* form_field = form_structure_->field(i); ASSERT_TRUE(form_field); for (const auto& [source, type] : heuristic_types[i]) @@ -58,7 +38,17 @@ void TestFormStructure::SetFieldTypes( form_field->set_server_predictions({prediction}); } - UpdateAutofillCount(); + form_structure_->UpdateAutofillCount(); +} + +void FormStructureTestApi::SetFieldTypes( + const std::vector<ServerFieldType>& heuristic_types, + const std::vector<ServerFieldType>& server_types) { + std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>> + all_heuristic_types; + for (ServerFieldType type : heuristic_types) + all_heuristic_types.push_back({{GetActivePatternSource(), type}}); + SetFieldTypes(all_heuristic_types, server_types); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_structure_test_api.h b/chromium/components/autofill/core/browser/form_structure_test_api.h index 08894272f87..eeca25c1f6a 100644 --- a/chromium/components/autofill/core/browser/form_structure_test_api.h +++ b/chromium/components/autofill/core/browser/form_structure_test_api.h @@ -7,6 +7,7 @@ #include <string> +#include "base/containers/contains.h" #include "base/memory/raw_ptr.h" #include "base/strings/string_piece.h" #include "components/autofill/core/browser/form_structure.h" @@ -42,6 +43,22 @@ class FormStructureTestApi { DCHECK(form_structure_); } + // Set the heuristic and server types for each field. The `heuristic_types` + // and `server_types` vectors must be aligned with the indices of the fields + // in the form. For each field in `heuristic_types` there must be exactly one + // `GetActivePatternSource()` prediction and any number of alternative + // predictions. + void SetFieldTypes( + const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>& + heuristic_types, + const std::vector<ServerFieldType>& server_types); + + // Set the heuristic and server types for each field. The `heuristic_types` + // and `server_types` vectors must be aligned with the indices of the fields + // in the form. + void SetFieldTypes(const std::vector<ServerFieldType>& heuristic_types, + const std::vector<ServerFieldType>& server_types); + const std::vector<std::unique_ptr<AutofillField>>& fields() { return form_structure_->fields_; } @@ -50,9 +67,8 @@ class FormStructureTestApi { form_structure_->IdentifySections(has_author_specified_sections); } - bool phone_rationalized(const std::string& section) const { - auto it = form_structure_->phone_rationalized_.find(section); - return it != form_structure_->phone_rationalized_.end() && it->second; + bool phone_rationalized(const Section& section) const { + return base::Contains(form_structure_->phone_rationalized_, section); } void ParseFieldTypesWithPatterns(PatternSource pattern_source) { diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc index 8bfe6ca5cd3..f88a89d1490 100644 --- a/chromium/components/autofill/core/browser/form_structure_unittest.cc +++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc @@ -28,6 +28,7 @@ #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/html_field_types.h" #include "components/autofill/core/common/signatures.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" @@ -152,8 +153,8 @@ class FormStructureTestImpl : public test::FormStructureTest { return FormStructure(form).ShouldRunHeuristics(); } - bool FormShouldRunPromoCodeHeuristics(const FormData& form) { - return FormStructure(form).ShouldRunPromoCodeHeuristics(); + bool FormShouldRunHeuristicsForSingleFieldForms(const FormData& form) { + return FormStructure(form).ShouldRunHeuristicsForSingleFieldForms(); } bool FormShouldBeQueried(const FormData& form) { @@ -573,6 +574,20 @@ TEST_F(FormStructureTestImpl, ShouldBeParsed_TwoFields_HasAutocomplete) { EXPECT_TRUE(form_structure->ShouldBeParsed()); } +// Tests that unmappable autocomplete values containing "address" are treated +// as HTML_TYPE_UNSPECIFIED instead of HTML_TYPE_UNRECOGNIZED. +TEST_F(FormStructureTestImpl, IgnoreUnmappableAutocompleteValues) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + features::kAutofillIgnoreUnmappableAutocompleteValues); + + CheckFormStructureTestData( + {{{.description_for_logging = "IgnoreUnmappableAutocompleteValues", + .fields = {{.autocomplete_attribute = "address-info"}}}, + {.determine_heuristic_type = true}, + {.expected_html_type = {HTML_TYPE_UNSPECIFIED}}}}); +} + // Tests that ShouldBeParsed returns true for a form containing less than three // fields if at least one has an autocomplete attribute. TEST_F(FormStructureTestImpl, DetermineHeuristicTypes_AutocompleteFalse) { @@ -1019,10 +1034,7 @@ TEST_F(FormStructureTestImpl, HeuristicsAutocompleteAttributePhoneTypes) { .field_count = 3, .autofill_count = 3}, {.expected_html_type = {HTML_TYPE_TEL_LOCAL, HTML_TYPE_TEL_LOCAL_PREFIX, - HTML_TYPE_TEL_LOCAL_SUFFIX}, - .expected_phone_part = {AutofillField::IGNORED, - AutofillField::PHONE_PREFIX, - AutofillField::PHONE_SUFFIX}}}}); + HTML_TYPE_TEL_LOCAL_SUFFIX}}}}); } // The heuristics and server predictions should run if there are more than two @@ -1171,7 +1183,8 @@ TEST_F(FormStructureTestImpl, } } -// Tests that promo code heuristics are run for forms with fewer than 3 fields. +// Tests that heuristics for single field parseable types are run for forms with +// fewer than 3 fields. TEST_F(FormStructureTestImpl, PromoCodeHeuristics_SmallForm) { base::test::ScopedFeatureList scoped_feature; scoped_feature.InitAndEnableFeature( @@ -1187,7 +1200,7 @@ TEST_F(FormStructureTestImpl, PromoCodeHeuristics_SmallForm) { field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - EXPECT_TRUE(FormShouldRunPromoCodeHeuristics(form)); + EXPECT_TRUE(FormShouldRunHeuristicsForSingleFieldForms(form)); // Default configuration. { @@ -1310,7 +1323,7 @@ TEST_F(FormStructureTestImpl, HeuristicsAutocompleteAttributeWithSections) { // All of the fields in this form should be parsed as belonging to different // sections. - std::set<std::string> section_names; + std::set<Section> section_names; for (size_t i = 0; i < 9; ++i) { section_names.insert(form_structure.field(i)->section); } @@ -1364,7 +1377,7 @@ TEST_F(FormStructureTestImpl, // All of the fields in this form should be parsed as belonging to the same // section. - std::set<std::string> section_names; + std::set<Section> section_names; for (size_t i = 0; i < 6; ++i) { section_names.insert(form_structure.field(i)->section); } @@ -1398,7 +1411,7 @@ TEST_F(FormStructureTestImpl, // All of the fields in this form should be parsed as belonging to the same // section. - std::set<std::string> section_names; + std::set<Section> section_names; for (size_t i = 0; i < 2; ++i) { section_names.insert(form_structure.field(i)->section); } @@ -2151,13 +2164,11 @@ TEST_F(FormStructureTestImpl, ThreePartPhoneNumber) { ASSERT_EQ(4U, form_structure->field_count()); ASSERT_EQ(4U, form_structure->autofill_count()); - // Area code. EXPECT_EQ(PHONE_HOME_CITY_CODE, form_structure->field(0)->heuristic_type()); - // Phone number suffix. - EXPECT_EQ(PHONE_HOME_NUMBER, form_structure->field(1)->heuristic_type()); - // Phone number suffix. - EXPECT_EQ(PHONE_HOME_NUMBER, form_structure->field(2)->heuristic_type()); - // Phone extension. + EXPECT_EQ(PHONE_HOME_NUMBER_PREFIX, + form_structure->field(1)->heuristic_type()); + EXPECT_EQ(PHONE_HOME_NUMBER_SUFFIX, + form_structure->field(2)->heuristic_type()); EXPECT_EQ(PHONE_HOME_EXTENSION, form_structure->field(3)->heuristic_type()); } @@ -2696,8 +2707,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { form_structure->set_password_attributes_vote( std::make_pair(PasswordAttribute::kHasLowercaseLetter, true)); form_structure->set_password_length_vote(10u); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -2714,8 +2725,6 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { available_field_types.insert(ADDRESS_HOME_LINE1); available_field_types.insert(ADDRESS_HOME_LINE2); available_field_types.insert(ADDRESS_HOME_COUNTRY); - available_field_types.insert(ADDRESS_BILLING_LINE1); - available_field_types.insert(ADDRESS_BILLING_LINE2); available_field_types.insert(EMAIL_ADDRESS); available_field_types.insert(PHONE_HOME_WHOLE_NUMBER); @@ -2725,7 +2734,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(false); - upload.set_data_present("144200030e"); + upload.set_data_present("1442000308"); upload.set_passwords_revealed(false); upload.set_password_has_lowercase_letter(true); upload.set_password_length(10u); @@ -2770,18 +2779,16 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, - {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_BILLING_LINE1, - ADDRESS_BILLING_LINE2}, - {AutofillProfile::VALID, AutofillProfile::VALID, - AutofillProfile::INVALID, AutofillProfile::INVALID}); + {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2}, + {AutofillProfile::VALID, AutofillProfile::VALID}); } form_structure = std::make_unique<FormStructure>(form); form_structure->set_password_attributes_vote( std::make_pair(PasswordAttribute::kHasLowercaseLetter, true)); form_structure->set_password_length_vote(10u); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -2798,9 +2805,9 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMatchingValidities) { // Create an additional 2 fields (total of 7). Put the appropriate autofill // type on the different address fields. test::FillUploadField(upload.add_field(), 509334676U, "address", "text", - nullptr, {30U, 31U, 37U, 38U}, {2, 2, 3, 3}); + nullptr, {30U, 31U}, {2, 2}); test::FillUploadField(upload.add_field(), 509334676U, "address", "text", - nullptr, {30U, 31U, 37U, 38U}, {2, 2, 3, 3}); + nullptr, {30U, 31U}, {2, 2}); EXPECT_THAT(form_structure->EncodeUploadRequest(available_field_types, false, std::string(), true, true), @@ -2879,8 +2886,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { form_structure->set_password_attributes_vote( std::make_pair(PasswordAttribute::kHasLowercaseLetter, true)); form_structure->set_password_length_vote(10u); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -2897,8 +2904,6 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { available_field_types.insert(ADDRESS_HOME_LINE1); available_field_types.insert(ADDRESS_HOME_LINE2); available_field_types.insert(ADDRESS_HOME_COUNTRY); - available_field_types.insert(ADDRESS_BILLING_LINE1); - available_field_types.insert(ADDRESS_BILLING_LINE2); available_field_types.insert(EMAIL_ADDRESS); available_field_types.insert(PHONE_HOME_WHOLE_NUMBER); @@ -2908,7 +2913,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithNonMatchingValidities) { upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(false); - upload.set_data_present("144200030e"); + upload.set_data_present("1442000308"); upload.set_passwords_revealed(false); upload.set_password_has_lowercase_letter(true); upload.set_password_length(10u); @@ -3005,8 +3010,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { form_structure->set_password_attributes_vote( std::make_pair(PasswordAttribute::kHasLowercaseLetter, true)); form_structure->set_password_length_vote(10u); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -3023,8 +3028,6 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { available_field_types.insert(ADDRESS_HOME_LINE1); available_field_types.insert(ADDRESS_HOME_LINE2); available_field_types.insert(ADDRESS_HOME_COUNTRY); - available_field_types.insert(ADDRESS_BILLING_LINE1); - available_field_types.insert(ADDRESS_BILLING_LINE2); available_field_types.insert(EMAIL_ADDRESS); available_field_types.insert(PHONE_HOME_WHOLE_NUMBER); @@ -3034,7 +3037,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithMultipleValidities) { upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(false); - upload.set_data_present("144200030e"); + upload.set_data_present("1442000308"); upload.set_passwords_revealed(false); upload.set_password_has_lowercase_letter(true); upload.set_password_length(10u); @@ -3128,8 +3131,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { form_structure->set_password_length_vote(10u); form_structure->set_submission_event( SubmissionIndicatorEvent::HTML_FORM_SUBMISSION); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -3146,8 +3149,6 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { available_field_types.insert(ADDRESS_HOME_LINE1); available_field_types.insert(ADDRESS_HOME_LINE2); available_field_types.insert(ADDRESS_HOME_COUNTRY); - available_field_types.insert(ADDRESS_BILLING_LINE1); - available_field_types.insert(ADDRESS_BILLING_LINE2); available_field_types.insert(EMAIL_ADDRESS); available_field_types.insert(PHONE_HOME_WHOLE_NUMBER); @@ -3158,7 +3159,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { upload.set_client_version(GetProductNameAndVersionForUserAgent()); upload.set_form_signature(form_structure->form_signature().value()); upload.set_autofill_used(false); - upload.set_data_present("144200030e"); + upload.set_data_present("1442000308"); upload.set_passwords_revealed(false); upload.set_password_has_lowercase_letter(true); upload.set_password_length(10u); @@ -3194,8 +3195,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, - {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_BILLING_LINE1, - ADDRESS_BILLING_LINE2}); + {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2}); } form_structure = std::make_unique<FormStructure>(form); @@ -3228,9 +3228,9 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { } // Put the appropriate autofill type on the different address fields. test::FillUploadField(upload.mutable_field(5), 509334676U, "address", "text", - nullptr, {31U, 37U, 38U}); + nullptr, 31U); test::FillUploadField(upload.mutable_field(6), 509334676U, "address", "text", - nullptr, {31U, 37U, 38U}); + nullptr, 31U); EXPECT_THAT(form_structure->EncodeUploadRequest(available_field_types, false, std::string(), true, true), @@ -3246,8 +3246,7 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest) { form.fields.push_back(field); test::InitializePossibleTypesAndValidities( possible_field_types, possible_field_types_validities, - {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_BILLING_LINE1, - ADDRESS_BILLING_LINE2}); + {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2}); } form_structure = std::make_unique<FormStructure>(form); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -3318,8 +3317,8 @@ TEST_F(FormStructureTestImpl, {ACCOUNT_CREATION_PASSWORD}); form_structure = std::make_unique<FormStructure>(form); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -3431,8 +3430,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithAutocomplete) { possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); form_structure = std::make_unique<FormStructure>(form); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -3525,8 +3524,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestWithPropertiesMask) { possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); form_structure = std::make_unique<FormStructure>(form); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -3610,8 +3609,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_ObservedSubmissionFalse) { possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); form_structure = std::make_unique<FormStructure>(form); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -3684,8 +3683,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithLabels) { possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); form_structure = std::make_unique<FormStructure>(form); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -3757,8 +3756,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithCssClassesAndIds) { possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -3839,8 +3838,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithFormName) { possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); form_structure = std::make_unique<FormStructure>(form); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); form_structure->set_submission_source(SubmissionSource::FRAME_DETACHED); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -3921,8 +3920,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequestPartialMetadata) { possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); form_structure = std::make_unique<FormStructure>(form); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -4012,8 +4011,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_DisabledMetadata) { possible_field_types, possible_field_types_validities, {EMAIL_ADDRESS}); form_structure = std::make_unique<FormStructure>(form); - for (auto& field : *form_structure) - field->host_form_signature = form_structure->form_signature(); + for (auto& fs_field : *form_structure) + fs_field->host_form_signature = form_structure->form_signature(); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); ASSERT_EQ(form_structure->field_count(), @@ -4227,8 +4226,8 @@ TEST_F(FormStructureTestImpl, CheckDataPresence) { FormStructure form_structure(form); form_structure.set_submission_source(SubmissionSource::FORM_SUBMISSION); - for (auto& field : form_structure) - field->host_form_signature = form_structure.form_signature(); + for (auto& fs_field : form_structure) + fs_field->host_form_signature = form_structure.form_signature(); std::vector<ServerFieldTypeSet> possible_field_types; std::vector<ServerFieldTypeValidityStatesMap> possible_field_types_validities; @@ -4608,8 +4607,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_PasswordsRevealed) { FormStructure form_structure(form); form_structure.set_passwords_were_revealed(true); - for (auto& field : form_structure) - field->host_form_signature = form_structure.form_signature(); + for (auto& fs_field : form_structure) + fs_field->host_form_signature = form_structure.form_signature(); std::vector<AutofillUploadContents> uploads = form_structure.EncodeUploadRequest( @@ -4635,8 +4634,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_IsFormTag) { form.is_form_tag = is_form_tag; FormStructure form_structure(form); - for (auto& field : form_structure) - field->host_form_signature = form_structure.form_signature(); + for (auto& fs_field : form_structure) + fs_field->host_form_signature = form_structure.form_signature(); form_structure.set_passwords_were_revealed(true); std::vector<AutofillUploadContents> uploads = form_structure.EncodeUploadRequest( @@ -4652,19 +4651,21 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_IsFormTag) { TEST_F(FormStructureTestImpl, EncodeUploadRequest_RichMetadata) { struct FieldMetadata { const char *id, *name, *label, *placeholder, *aria_label, *aria_description, - *css_classes; + *css_classes, *autocomplete; }; static const FieldMetadata kFieldMetadata[] = { {"fname_id", "fname_name", "First Name:", "Please enter your first name", - "Type your first name", "You can type your first name here", "blah"}, + "Type your first name", "You can type your first name here", "blah", + "given-name"}, {"lname_id", "lname_name", "Last Name:", "Please enter your last name", - "Type your lat name", "You can type your last name here", "blah"}, + "Type your lat name", "You can type your last name here", "blah", + "family-name"}, {"email_id", "email_name", "Email:", "Please enter your email address", "Type your email address", "You can type your email address here", - "blah"}, - {"id_only", "", "", "", "", "", ""}, - {"", "name_only", "", "", "", "", ""}, + "blah", "email"}, + {"id_only", "", "", "", "", "", "", ""}, + {"", "name_only", "", "", "", "", "", ""}, }; FormData form; @@ -4683,6 +4684,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.autocomplete_attribute = f.autocomplete; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); } @@ -4812,6 +4814,15 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_RichMetadata) { RandomizedEncoder::FIELD_PLACEHOLDER, field.placeholder)); } + if (field.autocomplete_attribute.empty()) { + EXPECT_FALSE(metadata.has_autocomplete()); + } else { + EXPECT_EQ(metadata.autocomplete().encoded_bits(), + encoder.EncodeForTesting( + form_signature, field_signature, + RandomizedEncoder::FIELD_AUTOCOMPLETE, + base::UTF8ToUTF16(field.autocomplete_attribute))); + } } } @@ -5129,8 +5140,8 @@ TEST_F(FormStructureTestImpl, EncodeQueryRequest_MissingNames) { form.fields.push_back(field); FormStructure form_structure(form); - for (auto& field : form_structure) - field->host_form_signature = form_structure.form_signature(); + for (auto& fs_field : form_structure) + fs_field->host_form_signature = form_structure.form_signature(); std::vector<FormStructure*> forms; forms.push_back(&form_structure); @@ -5166,8 +5177,8 @@ TEST_F(FormStructureTestImpl, EncodeUploadRequest_WithSingleUsernameVoteType) { FormStructure form_structure(form); form_structure.field(0)->set_single_username_vote_type( AutofillUploadContents::Field::STRONG); - for (auto& field : form_structure) - field->host_form_signature = form_structure.form_signature(); + for (auto& fs_field : form_structure) + fs_field->host_form_signature = form_structure.form_signature(); std::vector<AutofillUploadContents> uploads = form_structure.EncodeUploadRequest( @@ -5823,7 +5834,7 @@ TEST_F(FormStructureTestImpl, ParseApiQueryResponse) { auto* field_prediction0 = field0->add_predictions(); field_prediction0->set_type(NAME_FULL); auto* field_prediction1 = field0->add_predictions(); - field_prediction1->set_type(PHONE_FAX_COUNTRY_CODE); + field_prediction1->set_type(PHONE_HOME_COUNTRY_CODE); AddFieldSuggestionToForm(form_suggestion, form.fields[1], ADDRESS_HOME_LINE1); // Make form 2 suggestions. form_suggestion = api_response.add_form_suggestions(); @@ -5846,7 +5857,8 @@ TEST_F(FormStructureTestImpl, ParseApiQueryResponse) { EXPECT_EQ(NAME_FULL, forms[0]->field(0)->server_type()); ASSERT_EQ(2U, forms[0]->field(0)->server_predictions().size()); EXPECT_EQ(NAME_FULL, forms[0]->field(0)->server_predictions()[0].type()); - EXPECT_EQ(NO_SERVER_DATA, forms[0]->field(0)->server_predictions()[1].type()); + EXPECT_EQ(PHONE_HOME_COUNTRY_CODE, + forms[0]->field(0)->server_predictions()[1].type()); EXPECT_EQ(ADDRESS_HOME_LINE1, forms[0]->field(1)->server_type()); ASSERT_EQ(1U, forms[0]->field(1)->server_predictions().size()); EXPECT_EQ(ADDRESS_HOME_LINE1, @@ -6250,11 +6262,10 @@ TEST_F(FormStructureTestImpl, RationalizePhoneNumber_RunsOncePerSection) { test::GetEncodedSignatures(forms), nullptr, nullptr); - EXPECT_FALSE( - test_api(&form_structure).phone_rationalized("fullName_0_11-default")); - form_structure.RationalizePhoneNumbersInSection("fullName_0_11-default"); - EXPECT_TRUE( - test_api(&form_structure).phone_rationalized("fullName_0_11-default")); + Section s = forms[0]->field(0)->section; + EXPECT_FALSE(test_api(&form_structure).phone_rationalized(s)); + form_structure.RationalizePhoneNumbersInSection(s); + EXPECT_TRUE(test_api(&form_structure).phone_rationalized(s)); ASSERT_EQ(1U, forms.size()); ASSERT_EQ(4U, forms[0]->field_count()); EXPECT_EQ(NAME_FULL, forms[0]->field(0)->server_type()); @@ -6578,39 +6589,41 @@ TEST_F(FormStructureTestImpl, field.form_control_type = "text"; field.max_length = 10000; + // Billing. + field.section.SetPrefixFromAutocomplete( + {.section = "Billing", .mode = HtmlFieldMode::HTML_MODE_NONE}); + field.label = u"Full Name"; field.name = u"fullName"; - field.section = "Billing"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Billing"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"City"; field.name = u"city"; - field.section = "Billing"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); + // Shipping. + field.section.SetPrefixFromAutocomplete( + {.section = "Shipping", .mode = HtmlFieldMode::HTML_MODE_NONE}); + field.label = u"Full Name"; field.name = u"fullName"; - field.section = "Shipping"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Shipping"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"City"; field.name = u"city"; - field.section = "Shipping"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); @@ -6664,96 +6677,87 @@ TEST_F( field.form_control_type = "text"; field.max_length = 10000; - // Shipping + // Shipping. + field.section.SetPrefixFromAutocomplete( + {.section = "Shipping", .mode = HtmlFieldMode::HTML_MODE_NONE}); field.label = u"Full Name"; field.name = u"fullName"; - field.section = "Shipping"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Shipping"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Shipping"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"City"; field.name = u"city"; - field.section = "Shipping"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - // Billing + // Billing. + field.section.SetPrefixFromAutocomplete( + {.section = "Billing", .mode = HtmlFieldMode::HTML_MODE_NONE}); field.label = u"Full Name"; field.name = u"fullName"; - field.section = "Billing"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Billing"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Billing"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Billing"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"City"; field.name = u"city"; - field.section = "Billing"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - // Work address (not realistic) + // Work address (not realistic). + field.section.SetPrefixFromAutocomplete( + {.section = "Work", .mode = HtmlFieldMode::HTML_MODE_NONE}); field.label = u"Full Name"; field.name = u"fullName"; - field.section = "Work"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Work"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Work"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Work"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"Address"; field.name = u"address"; - field.section = "Work"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); field.label = u"City"; field.name = u"city"; - field.section = "Work"; field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); @@ -7126,7 +7130,9 @@ TEST_F(FormStructureTestImpl, field.form_control_type = "text"; field.max_length = 10000; - field.section = "shipping"; + // Shipping. + field.section.SetPrefixFromAutocomplete( + {.section = "shipping", .mode = HtmlFieldMode::HTML_MODE_NONE}); field.label = u"Full Name"; field.name = u"fullName"; @@ -7148,7 +7154,9 @@ TEST_F(FormStructureTestImpl, field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - field.section = "billing"; + // Billing. + field.section.SetPrefixFromAutocomplete( + {.section = "billing", .mode = HtmlFieldMode::HTML_MODE_NONE}); field.label = u"Country"; field.name = u"country2"; @@ -7197,7 +7205,9 @@ TEST_F(FormStructureTestImpl, field.unique_renderer_id = MakeFieldRendererId(); form.fields.push_back(field); - field.section = "billing-2"; + // Billing-2. + field.section.SetPrefixFromAutocomplete( + {.section = "billing-2", .mode = HtmlFieldMode::HTML_MODE_NONE}); field.label = u"Country"; field.name = u"country"; @@ -7232,13 +7242,13 @@ TEST_F(FormStructureTestImpl, AddFieldSuggestionToForm(form_suggestion, form.fields[8], ADDRESS_HOME_STATE); AddFieldSuggestionToForm(form_suggestion, form.fields[9], NAME_FULL); AddFieldSuggestionToForm(form_suggestion, form.fields[10], - ADDRESS_BILLING_STATE); + ADDRESS_HOME_STATE); // third section AddFieldSuggestionToForm(form_suggestion, form.fields[11], - ADDRESS_BILLING_STATE); + ADDRESS_HOME_STATE); AddFieldSuggestionToForm(form_suggestion, form.fields[12], NAME_FULL); AddFieldSuggestionToForm(form_suggestion, form.fields[13], - ADDRESS_BILLING_STATE); + ADDRESS_HOME_STATE); std::string response_string = SerializeAndEncode(response); @@ -7385,15 +7395,15 @@ TEST_F(FormStructureTestImpl, ADDRESS_HOME_COUNTRY); AddFieldSuggestionToForm(form_suggestion, form.fields[7], ADDRESS_HOME_CITY); AddFieldSuggestionToForm(form_suggestion, form.fields[8], - ADDRESS_BILLING_COUNTRY); + ADDRESS_HOME_COUNTRY); // third section AddFieldSuggestionToForm(form_suggestion, form.fields[9], ADDRESS_HOME_CITY); AddFieldSuggestionToForm(form_suggestion, form.fields[10], - ADDRESS_BILLING_COUNTRY); + ADDRESS_HOME_COUNTRY); AddFieldSuggestionToForm(form_suggestion, form.fields[11], ADDRESS_HOME_COUNTRY); AddFieldSuggestionToForm(form_suggestion, form.fields[12], - ADDRESS_BILLING_COUNTRY); + ADDRESS_HOME_COUNTRY); AddFieldSuggestionToForm(form_suggestion, form.fields[13], ADDRESS_HOME_COUNTRY); @@ -7434,7 +7444,8 @@ TEST_F(FormStructureTestImpl, field.form_control_type = "text"; field.max_length = 10000; - field.section = "billing"; + field.section.SetPrefixFromAutocomplete( + {.section = "billing", .mode = HtmlFieldMode::HTML_MODE_NONE}); field.label = u"Country"; field.name = u"country"; @@ -7473,8 +7484,7 @@ TEST_F(FormStructureTestImpl, AddFieldSuggestionToForm(form_suggestion, form.fields[1], ADDRESS_HOME_STATE); AddFieldSuggestionToForm(form_suggestion, form.fields[2], ADDRESS_HOME_STATE); AddFieldSuggestionToForm(form_suggestion, form.fields[3], NAME_FULL); - AddFieldSuggestionToForm(form_suggestion, form.fields[4], - ADDRESS_BILLING_STATE); + AddFieldSuggestionToForm(form_suggestion, form.fields[4], ADDRESS_HOME_STATE); std::string response_string = SerializeAndEncode(response); @@ -7504,7 +7514,8 @@ TEST_F(FormStructureTestImpl, field.form_control_type = "text"; field.max_length = 10000; - field.section = "billing"; + field.section.SetPrefixFromAutocomplete( + {.section = "billing", .mode = HtmlFieldMode::HTML_MODE_NONE}); field.label = u"Country"; field.name = u"country"; @@ -8165,13 +8176,18 @@ TEST_F(FormStructureTestImpl, NoAutocompleteSectionNames) { // Assert the correct number of fields. ASSERT_EQ(6U, form_structure.field_count()); - - EXPECT_EQ("fullName_0_11-default", form_structure.field(0)->section); - EXPECT_EQ("fullName_0_11-default", form_structure.field(1)->section); - EXPECT_EQ("fullName_0_11-default", form_structure.field(2)->section); - EXPECT_EQ("fullName_0_14-default", form_structure.field(3)->section); - EXPECT_EQ("fullName_0_14-default", form_structure.field(4)->section); - EXPECT_EQ("fullName_0_14-default", form_structure.field(5)->section); + EXPECT_EQ("fullName_0_11-default", + form_structure.field(0)->section.ToString()); + EXPECT_EQ("fullName_0_11-default", + form_structure.field(1)->section.ToString()); + EXPECT_EQ("fullName_0_11-default", + form_structure.field(2)->section.ToString()); + EXPECT_EQ("fullName_0_14-default", + form_structure.field(3)->section.ToString()); + EXPECT_EQ("fullName_0_14-default", + form_structure.field(4)->section.ToString()); + EXPECT_EQ("fullName_0_14-default", + form_structure.field(5)->section.ToString()); } // Tests that the immediate recurrence of the |PHONE_HOME_NUMBER| type does not @@ -8229,8 +8245,8 @@ TEST_F(FormStructureTestImpl, NoSplitByRecurringPhoneFieldType) { form_structure.set_overall_field_type_for_testing(1, PHONE_HOME_NUMBER); form_structure.set_overall_field_type_for_testing(2, PHONE_HOME_NUMBER); form_structure.set_overall_field_type_for_testing(3, NAME_FULL); - form_structure.set_overall_field_type_for_testing(4, PHONE_BILLING_NUMBER); - form_structure.set_overall_field_type_for_testing(5, PHONE_BILLING_NUMBER); + form_structure.set_overall_field_type_for_testing(4, PHONE_HOME_NUMBER); + form_structure.set_overall_field_type_for_testing(5, PHONE_HOME_NUMBER); form_structure.set_overall_field_type_for_testing(6, ADDRESS_HOME_COUNTRY); std::vector<FormStructure*> forms; @@ -8241,13 +8257,20 @@ TEST_F(FormStructureTestImpl, NoSplitByRecurringPhoneFieldType) { // Assert the correct number of fields. ASSERT_EQ(7U, form_structure.field_count()); - EXPECT_EQ("blue-billing-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("blue-billing-default", form_structure.field(3)->section); - EXPECT_EQ("blue-billing-default", form_structure.field(4)->section); - EXPECT_EQ("blue-billing-default", form_structure.field(5)->section); - EXPECT_EQ("blue-billing-default", form_structure.field(6)->section); + EXPECT_EQ("blue-billing-default", + form_structure.field(0)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(1)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(2)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(3)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(4)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(5)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(6)->section.ToString()); } // Tests if a new logical form is started with the second appearance of a field @@ -8300,10 +8323,14 @@ TEST_F(FormStructureTestImpl, SplitByRecurringFieldType) { // Assert the correct number of fields. ASSERT_EQ(4U, form_structure.field_count()); - 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_0_14-default", form_structure.field(3)->section); + EXPECT_EQ("blue-shipping-default", + form_structure.field(0)->section.ToString()); + EXPECT_EQ("blue-shipping-default", + form_structure.field(1)->section.ToString()); + EXPECT_EQ("blue-shipping-default", + form_structure.field(2)->section.ToString()); + EXPECT_EQ("country_0_14-default", + form_structure.field(3)->section.ToString()); } // Tests if a new logical form is started with the second appearance of a field @@ -8359,10 +8386,14 @@ TEST_F(FormStructureTestImpl, // Assert the correct number of fields. ASSERT_EQ(4U, form_structure.field_count()); - 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_0_14-default", form_structure.field(3)->section); + EXPECT_EQ("blue-shipping-default", + form_structure.field(0)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(1)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(2)->section.ToString()); + EXPECT_EQ("country_0_14-default", + form_structure.field(3)->section.ToString()); } // Tests if a new logical form is started with the second appearance of a field @@ -8416,10 +8447,14 @@ TEST_F(FormStructureTestImpl, SplitByNewAutocompleteSectionName) { // Assert the correct number of fields. ASSERT_EQ(4U, form_structure.field_count()); - EXPECT_EQ("blue-shipping-default", form_structure.field(0)->section); - EXPECT_EQ("blue-shipping-default", form_structure.field(1)->section); - EXPECT_EQ("blue-billing-default", form_structure.field(2)->section); - EXPECT_EQ("blue-billing-default", form_structure.field(3)->section); + EXPECT_EQ("blue-shipping-default", + form_structure.field(0)->section.ToString()); + EXPECT_EQ("blue-shipping-default", + form_structure.field(1)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(2)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(3)->section.ToString()); } // Tests if a new logical form is started with the second appearance of a field @@ -8474,10 +8509,14 @@ TEST_F( // Assert the correct number of fields. ASSERT_EQ(4U, form_structure.field_count()); - EXPECT_EQ("blue-shipping-default", form_structure.field(0)->section); - EXPECT_EQ("blue-shipping-default", form_structure.field(1)->section); - EXPECT_EQ("blue-billing-default", form_structure.field(2)->section); - EXPECT_EQ("blue-billing-default", form_structure.field(3)->section); + EXPECT_EQ("blue-shipping-default", + form_structure.field(0)->section.ToString()); + EXPECT_EQ("blue-shipping-default", + form_structure.field(1)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(2)->section.ToString()); + EXPECT_EQ("blue-billing-default", + form_structure.field(3)->section.ToString()); } // Tests if all the fields in the form belong to the same section when the @@ -8516,8 +8555,10 @@ TEST_F(FormStructureTestImpl, FromEmptyAutocompleteSectionToDefinedOne) { // Assert the correct number of fields. ASSERT_EQ(2U, form_structure.field_count()); - 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(0)->section.ToString()); + EXPECT_EQ("blue-shipping-default", + form_structure.field(1)->section.ToString()); } // Tests if all the fields in the form belong to the same section when one of @@ -8565,50 +8606,9 @@ TEST_F(FormStructureTestImpl, // Assert the correct number of fields. ASSERT_EQ(3U, form_structure.field_count()); - EXPECT_EQ("-shipping-default", form_structure.field(0)->section); - EXPECT_EQ("-shipping-default", form_structure.field(1)->section); - EXPECT_EQ("-shipping-default", form_structure.field(2)->section); -} - -// Tests if the autocomplete section name other than 'shipping' and 'billing' -// are ignored. -TEST_F(FormStructureTestImpl, IgnoreAribtraryAutocompleteSectionName) { - base::test::ScopedFeatureList enabled; - enabled.InitAndEnableFeature(features::kAutofillUseNewSectioningMethod); - - FormData form; - form.url = GURL("http://foo.com"); - FormFieldData field; - field.form_control_type = "text"; - field.max_length = 10000; - - field.label = u"Full Name"; - field.name = u"fullName"; - field.autocomplete_attribute = "section-red ship name"; - field.unique_renderer_id = MakeFieldRendererId(); - form.fields.push_back(field); - - field.label = u"Country"; - field.name = u"country"; - field.autocomplete_attribute = "section-blue shipping country"; - field.unique_renderer_id = MakeFieldRendererId(); - form.fields.push_back(field); - - FormStructure form_structure(form); - - form_structure.set_overall_field_type_for_testing(0, NAME_FULL); - form_structure.set_overall_field_type_for_testing(1, ADDRESS_HOME_COUNTRY); - - std::vector<FormStructure*> forms; - forms.push_back(&form_structure); - - form_structure.identify_sections_for_testing(); - - // Assert the correct number of fields. - ASSERT_EQ(2U, form_structure.field_count()); - - EXPECT_EQ("blue-shipping-default", form_structure.field(0)->section); - EXPECT_EQ("blue-shipping-default", form_structure.field(1)->section); + EXPECT_EQ("-shipping-default", form_structure.field(0)->section.ToString()); + EXPECT_EQ("-shipping-default", form_structure.field(1)->section.ToString()); + EXPECT_EQ("-shipping-default", form_structure.field(2)->section.ToString()); } TEST_F(FormStructureTestImpl, FindFieldsEligibleForManualFilling) { diff --git a/chromium/components/autofill/core/browser/geo/address_i18n.cc b/chromium/components/autofill/core/browser/geo/address_i18n.cc index fb38157dcfb..1eeef91ed77 100644 --- a/chromium/components/autofill/core/browser/geo/address_i18n.cc +++ b/chromium/components/autofill/core/browser/geo/address_i18n.cc @@ -94,39 +94,30 @@ ServerFieldType TypeForField(AddressField address_field) { bool FieldForType(ServerFieldType server_type, AddressField* field) { switch (server_type) { - case ADDRESS_BILLING_COUNTRY: case ADDRESS_HOME_COUNTRY: if (field) *field = ::i18n::addressinput::COUNTRY; return true; - case ADDRESS_BILLING_STATE: case ADDRESS_HOME_STATE: if (field) *field = ::i18n::addressinput::ADMIN_AREA; return true; - case ADDRESS_BILLING_CITY: case ADDRESS_HOME_CITY: if (field) *field = ::i18n::addressinput::LOCALITY; return true; - case ADDRESS_BILLING_DEPENDENT_LOCALITY: case ADDRESS_HOME_DEPENDENT_LOCALITY: if (field) *field = ::i18n::addressinput::DEPENDENT_LOCALITY; return true; - case ADDRESS_BILLING_SORTING_CODE: case ADDRESS_HOME_SORTING_CODE: if (field) *field = ::i18n::addressinput::SORTING_CODE; return true; - case ADDRESS_BILLING_ZIP: case ADDRESS_HOME_ZIP: if (field) *field = ::i18n::addressinput::POSTAL_CODE; return true; - case ADDRESS_BILLING_STREET_ADDRESS: - case ADDRESS_BILLING_LINE1: - case ADDRESS_BILLING_LINE2: case ADDRESS_HOME_STREET_ADDRESS: case ADDRESS_HOME_LINE1: case ADDRESS_HOME_LINE2: @@ -137,7 +128,6 @@ bool FieldForType(ServerFieldType server_type, AddressField* field) { if (field) *field = ::i18n::addressinput::ORGANIZATION; return true; - case NAME_BILLING_FULL: case NAME_FULL: if (field) *field = ::i18n::addressinput::RECIPIENT; diff --git a/chromium/components/autofill/core/browser/geo/address_i18n_unittest.cc b/chromium/components/autofill/core/browser/geo/address_i18n_unittest.cc index bd9f81602a7..7268c8bdcc4 100644 --- a/chromium/components/autofill/core/browser/geo/address_i18n_unittest.cc +++ b/chromium/components/autofill/core/browser/geo/address_i18n_unittest.cc @@ -90,10 +90,6 @@ INSTANTIATE_TEST_SUITE_P(AddressI18nTest, FieldTypeUnidirectionalConversionsTest, testing::Values( FieldTypeUnidirectionalConversionsTestCase{ - ADDRESS_BILLING_LINE1, STREET_ADDRESS}, - FieldTypeUnidirectionalConversionsTestCase{ - ADDRESS_BILLING_LINE2, STREET_ADDRESS}, - FieldTypeUnidirectionalConversionsTestCase{ ADDRESS_HOME_LINE1, STREET_ADDRESS}, FieldTypeUnidirectionalConversionsTestCase{ ADDRESS_HOME_LINE2, STREET_ADDRESS})); diff --git a/chromium/components/autofill/core/browser/geo/autofill_country.cc b/chromium/components/autofill/core/browser/geo/autofill_country.cc index 5f0cdae2611..ad65c3c325a 100644 --- a/chromium/components/autofill/core/browser/geo/autofill_country.cc +++ b/chromium/components/autofill/core/browser/geo/autofill_country.cc @@ -32,7 +32,7 @@ const size_t kLocaleCapacity = } // namespace AutofillCountry::AutofillCountry(const std::string& country_code, - const std::string& locale) { + const absl::optional<std::string>& locale) { CountryDataMap* country_data_map = CountryDataMap::GetInstance(); // If the country code is an alias (e.g. "GB" for "UK") expand the country @@ -46,7 +46,8 @@ AutofillCountry::AutofillCountry(const std::string& country_code, country_data_map->GetRequiredFieldsForAddressImport(country_code_); // Translate the country name by the supplied local. - name_ = l10n_util::GetDisplayNameForCountry(country_code_, locale); + if (locale) + name_ = l10n_util::GetDisplayNameForCountry(country_code_, *locale); } AutofillCountry::~AutofillCountry() {} diff --git a/chromium/components/autofill/core/browser/geo/autofill_country.h b/chromium/components/autofill/core/browser/geo/autofill_country.h index 3a46edff14c..5983099f164 100644 --- a/chromium/components/autofill/core/browser/geo/autofill_country.h +++ b/chromium/components/autofill/core/browser/geo/autofill_country.h @@ -10,6 +10,7 @@ #include "base/containers/span.h" #include "base/strings/string_piece.h" #include "components/autofill/core/browser/geo/country_data.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_field.h" namespace autofill { @@ -24,8 +25,9 @@ class AutofillCountry { // `country_code`. // `locale` is used translate the `name()` appropriately and can be ignored // if the name is not queried. - explicit AutofillCountry(const std::string& country_code, - const std::string& locale = "en"); + explicit AutofillCountry( + const std::string& country_code, + const absl::optional<std::string>& locale = absl::nullopt); AutofillCountry(const AutofillCountry&) = delete; AutofillCountry& operator=(const AutofillCountry&) = delete; @@ -63,7 +65,12 @@ class AutofillCountry { // mapping from the locale is available. static const std::string CountryCodeForLocale(const std::string& locale); + // The `country_code` provided to the constructor, with aliases like "GB" + // replaced by their canonical version ("UK", in this case). const std::string& country_code() const { return country_code_; } + + // Returns the name of the country translated into the `locale` provided to + // the constructor. If no `locale` was provided, an empty string is returned. const std::u16string& name() const { return name_; } // City is expected in a complete address for this country. 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 b9ce7000597..8583dc8e527 100644 --- a/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc +++ b/chromium/components/autofill/core/browser/geo/autofill_country_unittest.cc @@ -48,6 +48,11 @@ TEST(AutofillCountryTest, AutofillCountry) { // Unrecognizable country codes remain that way. AutofillCountry unknown("Unknown", "en_US"); EXPECT_EQ("Unknown", unknown.country_code()); + + // If no locale is provided, no `name()` is returned. + AutofillCountry empty_locale("AT"); + EXPECT_EQ("AT", empty_locale.country_code()); + EXPECT_TRUE(empty_locale.name().empty()); } // Test locale to country code mapping. diff --git a/chromium/components/autofill/core/browser/geo/phone_number_i18n.cc b/chromium/components/autofill/core/browser/geo/phone_number_i18n.cc index 8368f0d69ff..9fe04651044 100644 --- a/chromium/components/autofill/core/browser/geo/phone_number_i18n.cc +++ b/chromium/components/autofill/core/browser/geo/phone_number_i18n.cc @@ -386,9 +386,12 @@ PhoneObject::PhoneObject(const std::u16string& number, i18n_number_->has_country_code()) { country_code_ = base::NumberToString16(i18n_number_->country_code()); } + // Autofill doesn't support filling extensions, so we should not store them. + i18n_number_->clear_extension(); } else { // Parsing failed. Store passed phone "as is" into |whole_number_|. whole_number_ = number; + // We have no way of removing any extensions. } } diff --git a/chromium/components/autofill/core/browser/geo/phone_number_i18n_unittest.cc b/chromium/components/autofill/core/browser/geo/phone_number_i18n_unittest.cc index 88de443bf02..4a68eee2e0d 100644 --- a/chromium/components/autofill/core/browser/geo/phone_number_i18n_unittest.cc +++ b/chromium/components/autofill/core/browser/geo/phone_number_i18n_unittest.cc @@ -66,7 +66,7 @@ class ParseNumberTest : public testing::TestWithParam<ParseNumberTestCase> {}; TEST_P(ParseNumberTest, ParsePhoneNumber) { auto test_case = GetParam(); - SCOPED_TRACE(test_case.input.c_str()); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); std::u16string country_code, city_code, number; std::string deduced_region; @@ -99,7 +99,7 @@ INSTANTIATE_TEST_SUITE_P( // unknown("ZZ") deduced region. ParseNumberTestCase{true, u"7134567", "US", u"7134567", u"", u"", "ZZ"}, // Valid Canadian toll-free number. - ParseNumberTestCase{true, u"3101234", "CA", u"3101234", u"", u"", "ZZ"}, + ParseNumberTestCase{true, u"3101234", "CA", u"1234", u"310", u"", "CA"}, // Test for string with greater than 7 digits but less than 10 digits. // Should fail parsing in US. ParseNumberTestCase{false, u"123456789", "US"}, diff --git a/chromium/components/autofill/core/browser/iban_manager.cc b/chromium/components/autofill/core/browser/iban_manager.cc new file mode 100644 index 00000000000..ef691ec2d8b --- /dev/null +++ b/chromium/components/autofill/core/browser/iban_manager.cc @@ -0,0 +1,81 @@ +// Copyright 2022 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/iban_manager.h" + +#include "base/containers/contains.h" +#include "components/autofill/core/browser/autofill_suggestion_generator.h" +#include "components/autofill/core/browser/browser_autofill_manager.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/browser/suggestions_context.h" +#include "components/autofill/core/common/autofill_clock.h" + +namespace autofill { + +IBANManager::IBANManager(PersonalDataManager* personal_data_manager, + bool is_off_the_record) + : personal_data_manager_(personal_data_manager), + is_off_the_record_(is_off_the_record) {} + +IBANManager::~IBANManager() = default; + +bool IBANManager::OnGetSingleFieldSuggestions( + int query_id, + bool is_autocomplete_enabled, + bool autoselect_first_suggestion, + const FormFieldData& field, + base::WeakPtr<SuggestionsHandler> handler, + const SuggestionsContext& context) { + if (!is_off_the_record_ && personal_data_manager_) { + std::vector<IBAN*> ibans = personal_data_manager_->GetIBANs(); + if (!ibans.empty()) { + // Rank the IBANs by ranking score (see AutoFillDataModel for details). + base::Time comparison_time = AutofillClock::Now(); + base::ranges::sort( + ibans, [comparison_time](const IBAN* iban0, const IBAN* iban1) { + return iban0->HasGreaterRankingThan(iban1, comparison_time); + }); + SendIBANSuggestions(ibans, + QueryHandler(query_id, autoselect_first_suggestion, + field.value, handler)); + return true; + } + } + return false; +} + +base::WeakPtr<IBANManager> IBANManager::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +void IBANManager::SendIBANSuggestions(const std::vector<IBAN*>& ibans, + const QueryHandler& query_handler) { + if (!query_handler.handler_) { + // Either the handler has been destroyed, or it is invalid. + return; + } + + // If the input box content equals any of the available IBANs, then + // assume the IBAN has been filled, and don't show any suggestions. + // Note: this |prefix_| is actually the value of form and we are comparing + // the value with the full IBAN value. However, once we land + // MASKED_SERVER_IBANs and Chrome doesn't know the whole value, we'll have + // check 'prefix'(E.g., the first ~5 characters). + if (base::Contains(ibans, query_handler.prefix_, &IBAN::value)) { + // Return empty suggestions to query handler. This will result in no + // suggestions being displayed. + query_handler.handler_->OnSuggestionsReturned( + query_handler.client_query_id_, + query_handler.autoselect_first_suggestion_, {}); + return; + } + + // Return suggestions to query handler. + query_handler.handler_->OnSuggestionsReturned( + query_handler.client_query_id_, + query_handler.autoselect_first_suggestion_, + AutofillSuggestionGenerator::GetSuggestionsForIBANs(ibans)); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/iban_manager.h b/chromium/components/autofill/core/browser/iban_manager.h new file mode 100644 index 00000000000..dbbc61b1a76 --- /dev/null +++ b/chromium/components/autofill/core/browser/iban_manager.h @@ -0,0 +1,81 @@ +// Copyright 2022 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_IBAN_MANAGER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_IBAN_MANAGER_H_ + +#include "base/gtest_prod_util.h" +#include "components/autofill/core/browser/autofill_subject.h" +#include "components/autofill/core/browser/data_model/iban.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/browser/single_field_form_filler.h" +#include "components/keyed_service/core/keyed_service.h" +#include "components/webdata/common/web_data_service_consumer.h" + +namespace autofill { + +class PersonalDataManager; +struct SuggestionsContext; + +// Per-profile IBAN Manager. This class handles IBAN-related functionality +// such as retrieving IBAN data, managing IBAN suggestions, filling IBAN fields, +// and handling form submission data when there is an IBAN field present. +class IBANManager : public SingleFieldFormFiller, + public KeyedService, + public AutofillSubject { + public: + // Initializes the instance with the given parameters. |personal_data_manager| + // is a profile-scope data manager used to retrieve IBAN data from the + // local autofill table. |is_off_the_record| indicates whether the user is + // currently operating in an off-the-record context (i.e. incognito). + explicit IBANManager(PersonalDataManager* personal_data_manager, + bool is_off_the_record); + + IBANManager(const IBANManager&) = delete; + IBANManager& operator=(const IBANManager&) = delete; + + ~IBANManager() override; + + // SingleFieldFormFiller overrides: + [[nodiscard]] bool OnGetSingleFieldSuggestions( + int query_id, + bool is_autocomplete_enabled, + bool autoselect_first_suggestion, + const FormFieldData& field, + base::WeakPtr<SuggestionsHandler> handler, + const SuggestionsContext& context) override; + void OnWillSubmitFormWithFields(const std::vector<FormFieldData>& fields, + bool is_autocomplete_enabled) override {} + void CancelPendingQueries(const SuggestionsHandler* handler) override {} + void OnRemoveCurrentSingleFieldSuggestion(const std::u16string& field_name, + const std::u16string& value, + int frontend_id) override {} + void OnSingleFieldSuggestionSelected(const std::u16string& value, + int frontend_id) override {} + + base::WeakPtr<IBANManager> GetWeakPtr(); + +#if defined(UNIT_TEST) + // Assign types to the fields for the testing purposes. + void SetOffTheRecordForTesting(bool is_off_the_record) { + is_off_the_record_ = is_off_the_record; + } +#endif + + private: + // Sends suggestions for |ibans| to the |query_handler|'s handler for display + // in the associated Autofill popup. + void SendIBANSuggestions(const std::vector<IBAN*>& ibans, + const QueryHandler& query_handler); + + PersonalDataManager* personal_data_manager_ = nullptr; + + bool is_off_the_record_ = false; + + base::WeakPtrFactory<IBANManager> weak_ptr_factory_{this}; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_IBAN_MANAGER_H_ diff --git a/chromium/components/autofill/core/browser/iban_manager_unittest.cc b/chromium/components/autofill/core/browser/iban_manager_unittest.cc new file mode 100644 index 00000000000..adeda73149a --- /dev/null +++ b/chromium/components/autofill/core/browser/iban_manager_unittest.cc @@ -0,0 +1,155 @@ +// Copyright 2022 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/iban_manager.h" + +#include "base/guid.h" +#include "components/autofill/core/browser/suggestions_context.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" + +using testing::_; +using testing::Field; +using testing::Truly; +using testing::UnorderedElementsAre; + +namespace autofill { + +namespace { + +class MockSuggestionsHandler : public IBANManager::SuggestionsHandler { + public: + MockSuggestionsHandler() = default; + MockSuggestionsHandler(const MockSuggestionsHandler&) = delete; + MockSuggestionsHandler& operator=(const MockSuggestionsHandler&) = delete; + ~MockSuggestionsHandler() override = default; + + MOCK_METHOD(void, + OnSuggestionsReturned, + (int query_id, + bool autoselect_first_suggestion, + const std::vector<Suggestion>& suggestions), + (override)); + + base::WeakPtr<MockSuggestionsHandler> GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } + + private: + base::WeakPtrFactory<MockSuggestionsHandler> weak_ptr_factory_{this}; +}; + +} // namespace + +class IBANManagerTest : public testing::Test { + protected: + IBANManagerTest() + : iban_manager_(&personal_data_manager_, /*is_off_the_record=*/false) {} + + // Sets up the TestPersonalDataManager with an IBAN. + IBAN SetUpIBAN(base::StringPiece16 value, base::StringPiece16 nickname) { + IBAN iban; + std::string guid = base::GenerateGUID(); + iban.set_guid(guid); + iban.set_value(std::u16string(value)); + iban.set_nickname(std::u16string(nickname)); + personal_data_manager_.AddIBANForTest(std::make_unique<IBAN>(iban)); + return iban; + } + + // Sets up the TestPersonalDataManager with an IBAN and corresponding + // suggestion. + Suggestion SetUpIBANAndSuggestion(base::StringPiece16 value, + base::StringPiece16 nickname) { + IBAN iban = SetUpIBAN(value, nickname); + Suggestion iban_suggestion(iban.GetIdentifierStringForAutofillDisplay()); + return iban_suggestion; + } + + MockSuggestionsHandler suggestions_handler_; + TestPersonalDataManager personal_data_manager_; + IBANManager iban_manager_; +}; + +TEST_F(IBANManagerTest, ShowsIBANSuggestions) { + int test_query_id = 2; + Suggestion iban_suggestion_0 = + SetUpIBANAndSuggestion(u"IE12 BOFI 9000 0112 3456 78", u"Nickname 0"); + Suggestion iban_suggestion_1 = + SetUpIBANAndSuggestion(u"CH56 0483 5012 3456 7800 9", u"Nickname 1"); + + SuggestionsContext context; + FormFieldData test_field; + + // Setting up mock to verify that the handler is returned a list of + // iban-based suggestions and the iban details line. + EXPECT_CALL( + suggestions_handler_, + OnSuggestionsReturned( + test_query_id, /*autoselect_first_suggestion=*/false, + UnorderedElementsAre( + Field(&Suggestion::main_text, iban_suggestion_0.main_text), + Field(&Suggestion::main_text, iban_suggestion_1.main_text)))) + .Times(1); + + // Simulate request for suggestions. + // Because all criteria are met to trigger returning to the handler, + // the handler should be triggered and this should return true. + EXPECT_TRUE(iban_manager_.OnGetSingleFieldSuggestions( + test_query_id, /*is_autocomplete_enabled=*/false, + /*autoselect_first_suggestion=*/false, test_field, + suggestions_handler_.GetWeakPtr(), + /*context=*/context)); +} + +TEST_F(IBANManagerTest, ShowsIBANSuggestions_OnlyPrefixMatch) { + int test_query_id = 2; + base::StringPiece16 value_0 = u"IE12 BOFI 9000 0112 3456 78"; + Suggestion iban_suggestion_0 = SetUpIBANAndSuggestion(value_0, u"Nickname 0"); + Suggestion iban_suggestion_1 = + SetUpIBANAndSuggestion(u"CH56 0483 5012 3456 7800 9", u"Nickname 1"); + + SuggestionsContext context; + FormFieldData test_field; + test_field.value = std::u16string(value_0); + + // Setting up mock to verify that the handler is not returned any iban-based + // suggestions as the field already contains an iban. + EXPECT_CALL(suggestions_handler_, + OnSuggestionsReturned( + _, _, + testing::Truly( + [](const std::vector<Suggestion>& returned_suggestions) { + return returned_suggestions.empty(); + }))); + + // Simulate request for suggestions. + // Because all criteria are met to trigger returning to the handler, + // the handler should be triggered and this should return true. + EXPECT_TRUE(iban_manager_.OnGetSingleFieldSuggestions( + test_query_id, /*is_autocomplete_enabled=*/false, + /*autoselect_first_suggestion=*/false, test_field, + suggestions_handler_.GetWeakPtr(), + /*context=*/context)); +} + +TEST_F(IBANManagerTest, DoesNotShowIBANsForOffTheRecord) { + IBAN iban_0 = SetUpIBAN(u"IE12 BOFI 9000 0112 3456 78", u"Nickname 0"); + iban_manager_.SetOffTheRecordForTesting(true); + SuggestionsContext context; + FormFieldData test_field; + + // Setting up mock to verify that suggestions returning is not triggered if + // the user is off the record. + EXPECT_CALL(suggestions_handler_, OnSuggestionsReturned).Times(0); + + // Simulate request for suggestions. + EXPECT_FALSE(iban_manager_.OnGetSingleFieldSuggestions( + /*query_id=*/2, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_field, + suggestions_handler_.GetWeakPtr(), /*context=*/context)); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/logging/log_buffer_submitter.cc b/chromium/components/autofill/core/browser/logging/log_buffer_submitter.cc index 7ef67ffe52b..a084333c0b5 100644 --- a/chromium/components/autofill/core/browser/logging/log_buffer_submitter.cc +++ b/chromium/components/autofill/core/browser/logging/log_buffer_submitter.cc @@ -9,28 +9,32 @@ namespace autofill { LogBufferSubmitter::LogBufferSubmitter(LogRouter* destination, bool active) - : destination_(destination) { - buffer_.set_active(destination != nullptr && active); -} - -LogBufferSubmitter::LogBufferSubmitter(LogBufferSubmitter&& that) noexcept { - operator=(std::move(that)); + : destination_(destination), + buffer_(LogBuffer::IsActive(destination != nullptr && active)), + destruct_with_logging_(buffer_.active()) {} + +LogBufferSubmitter::LogBufferSubmitter(LogBufferSubmitter&& that) noexcept + : destination_(std::move(that.destination_)), + buffer_(std::move(that.buffer_)), + destruct_with_logging_(std::move(that.destruct_with_logging_)) { + that.destruct_with_logging_ = false; } LogBufferSubmitter& LogBufferSubmitter::operator=(LogBufferSubmitter&& that) { - destination_ = that.destination_; + destination_ = std::move(that.destination_); buffer_ = std::move(that.buffer_); + destruct_with_logging_ = std::move(that.destruct_with_logging_); that.destruct_with_logging_ = false; return *this; } LogBufferSubmitter::~LogBufferSubmitter() { - if (!destruct_with_logging_) + if (!destruct_with_logging_ || !destination_) return; - base::Value message = buffer_.RetrieveResult(); - if (!destination_ || message.is_none()) + absl::optional<base::Value::Dict> message = buffer_.RetrieveResult(); + if (!message) return; - destination_->ProcessLog(std::move(message)); + destination_->ProcessLog(*message); } } // namespace autofill 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 03b293d4dab..b5f99a87041 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 @@ -20,21 +20,21 @@ namespace autofill { class MockLogReceiver : public LogReceiver { public: - MOCK_METHOD(void, LogEntry, (const base::Value&), (override)); + MOCK_METHOD(void, LogEntry, (const base::Value::Dict&), (override)); }; TEST(LogBufferSubmitter, VerifySubmissionOnDestruction) { LogBuffer buffer; buffer << 42; - base::Value expected = buffer.RetrieveResult(); + absl::optional<base::Value::Dict> expected = buffer.RetrieveResult(); MockLogReceiver receiver; LogRouter router; - std::ignore = router.RegisterReceiver(&receiver); + router.RegisterReceiver(&receiver); std::unique_ptr<LogManager> log_manager = LogManager::Create(&router, base::NullCallback()); - EXPECT_CALL(receiver, LogEntry(testing::Eq(testing::ByRef(expected)))); + EXPECT_CALL(receiver, LogEntry(testing::Eq(testing::ByRef(*expected)))); log_manager->Log() << 42; log_manager.reset(); router.UnregisterReceiver(&receiver); @@ -43,7 +43,7 @@ TEST(LogBufferSubmitter, VerifySubmissionOnDestruction) { TEST(LogBufferSubmitter, NoEmptySubmission) { MockLogReceiver receiver; LogRouter router; - std::ignore = router.RegisterReceiver(&receiver); + router.RegisterReceiver(&receiver); std::unique_ptr<LogManager> log_manager = LogManager::Create(&router, base::NullCallback()); @@ -60,7 +60,7 @@ TEST(LogBufferSubmitter, CorrectActivation) { LogRouter router; MockLogReceiver receiver; - std::ignore = router.RegisterReceiver(&receiver); + router.RegisterReceiver(&receiver); std::unique_ptr<LogManager> log_manager_2 = LogManager::Create(&router, base::NullCallback()); EXPECT_TRUE(log_manager_2->Log().buffer().active()); diff --git a/chromium/components/autofill/core/browser/logging/log_manager.cc b/chromium/components/autofill/core/browser/logging/log_manager.cc index 2cd1f7c73db..023a9391f08 100644 --- a/chromium/components/autofill/core/browser/logging/log_manager.cc +++ b/chromium/components/autofill/core/browser/logging/log_manager.cc @@ -25,7 +25,7 @@ class LogManagerImpl : public LogManager { void OnLogRouterAvailabilityChanged(bool router_can_be_used) override; void SetSuspended(bool suspended) override; void LogTextMessage(const std::string& text) const override; - void LogEntry(base::Value&& entry) const override; + void LogEntry(const base::Value::Dict& entry) const override; bool IsLoggingActive() const override; LogBufferSubmitter Log() override; @@ -83,10 +83,10 @@ void LogManagerImpl::LogTextMessage(const std::string& text) const { log_router_->ProcessLog(text); } -void LogManagerImpl::LogEntry(base::Value&& entry) const { +void LogManagerImpl::LogEntry(const base::Value::Dict& entry) const { if (!IsLoggingActive()) return; - log_router_->ProcessLog(std::move(entry)); + log_router_->ProcessLog(entry); } bool LogManagerImpl::IsLoggingActive() const { @@ -107,9 +107,4 @@ std::unique_ptr<LogManager> LogManager::Create( std::move(notification_callback)); } -// static -LogBufferSubmitter LogManager::DevNull() { - return LogBufferSubmitter(nullptr, false); -} - } // namespace autofill diff --git a/chromium/components/autofill/core/browser/logging/log_manager.h b/chromium/components/autofill/core/browser/logging/log_manager.h index b0656116517..bbdc3a908de 100644 --- a/chromium/components/autofill/core/browser/logging/log_manager.h +++ b/chromium/components/autofill/core/browser/logging/log_manager.h @@ -7,9 +7,11 @@ #include <memory> #include <string> +#include <type_traits> #include "base/callback.h" #include "components/autofill/core/browser/logging/log_buffer_submitter.h" +#include "components/autofill/core/common/logging/log_macros.h" namespace base { class Value; @@ -41,7 +43,7 @@ class LogManager { // Forward a DOM structured log entry to the LogRouter (if registered with // one). - virtual void LogEntry(base::Value&& entry) const = 0; + virtual void LogEntry(const base::Value::Dict& entry) const = 0; // Returns true if logs recorded via LogTextMessage will be displayed, and // false otherwise. @@ -56,11 +58,46 @@ class LogManager { // This is the preferred way to submitting log entries. virtual LogBufferSubmitter Log() = 0; +}; + +inline LogBuffer::IsActive IsLoggingActive(LogManager* log_manager) { + return LogBuffer::IsActive(log_manager && log_manager->IsLoggingActive()); +} - // Returns a LogBufferSubmitter that ignores all input. - static LogBufferSubmitter DevNull(); +namespace internal { + +// Traits for LOG_AF() macro for `LogManager*`. +template <typename T> +struct LoggerTraits< + T, + typename std::enable_if_t<std::is_convertible_v<decltype(std::declval<T>()), + const LogManager*>>> { + static bool active(const LogManager* log_manager) { + return log_manager && log_manager->IsLoggingActive(); + } + + static LogBufferSubmitter get_stream(LogManager* log_manager) { + return log_manager->Log(); + } }; +// Traits for LOG_AF() macro for `LogManager&`. +template <typename T> +struct LoggerTraits< + T, + typename std::enable_if_t<std::is_convertible_v<decltype(std::declval<T>()), + const LogManager&>>> { + static bool active(const LogManager& log_manager) { + return log_manager.IsLoggingActive(); + } + + static LogBufferSubmitter get_stream(LogManager& log_manager) { + return log_manager.Log(); + } +}; + +} // namespace internal + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_LOGGING_LOG_MANAGER_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 a0b0b017f94..3d13dff491e 100644 --- a/chromium/components/autofill/core/browser/logging/log_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/logging/log_manager_unittest.cc @@ -26,7 +26,7 @@ class MockLogReceiver : public autofill::LogReceiver { MockLogReceiver(const MockLogReceiver&) = delete; MockLogReceiver& operator=(const MockLogReceiver&) = delete; - MOCK_METHOD(void, LogEntry, (const base::Value&), (override)); + MOCK_METHOD(void, LogEntry, (const base::Value::Dict&), (override)); }; class MockNotifiedObject { @@ -72,10 +72,10 @@ TEST_F(LogManagerTest, LogTextMessageAttachReceiver) { EXPECT_FALSE(manager_->IsLoggingActive()); EXPECT_CALL(notified_object_, NotifyAboutLoggingActivity()); - EXPECT_EQ(std::vector<base::Value>(), router_.RegisterReceiver(&receiver_)); + router_.RegisterReceiver(&receiver_); EXPECT_TRUE(manager_->IsLoggingActive()); // After attaching the logger, text should be passed. - base::Value log_entry = LogRouter::CreateEntryForText(kTestText); + base::Value::Dict log_entry = LogRouter::CreateEntryForText(kTestText); EXPECT_CALL(receiver_, LogEntry(testing::Eq(testing::ByRef(log_entry)))); manager_->LogTextMessage(kTestText); EXPECT_CALL(notified_object_, NotifyAboutLoggingActivity()); @@ -85,7 +85,7 @@ TEST_F(LogManagerTest, LogTextMessageAttachReceiver) { TEST_F(LogManagerTest, LogTextMessageDetachReceiver) { EXPECT_CALL(notified_object_, NotifyAboutLoggingActivity()); - EXPECT_EQ(std::vector<base::Value>(), router_.RegisterReceiver(&receiver_)); + router_.RegisterReceiver(&receiver_); EXPECT_TRUE(manager_->IsLoggingActive()); EXPECT_CALL(notified_object_, NotifyAboutLoggingActivity()); router_.UnregisterReceiver(&receiver_); @@ -98,13 +98,13 @@ TEST_F(LogManagerTest, LogTextMessageDetachReceiver) { TEST_F(LogManagerTest, NullCallbackWillNotCrash) { manager_ = LogManager::Create(&router_, base::NullCallback()); - EXPECT_EQ(std::vector<base::Value>(), router_.RegisterReceiver(&receiver_)); + router_.RegisterReceiver(&receiver_); router_.UnregisterReceiver(&receiver_); } TEST_F(LogManagerTest, SetSuspended_WithActiveLogging) { EXPECT_CALL(notified_object_, NotifyAboutLoggingActivity()); - EXPECT_EQ(std::vector<base::Value>(), router_.RegisterReceiver(&receiver_)); + router_.RegisterReceiver(&receiver_); EXPECT_TRUE(manager_->IsLoggingActive()); EXPECT_CALL(notified_object_, NotifyAboutLoggingActivity()); @@ -134,7 +134,7 @@ TEST_F(LogManagerTest, InterleaveSuspendAndLoggingActivation_SuspendFirst) { manager_->SetSuspended(true); EXPECT_FALSE(manager_->IsLoggingActive()); - EXPECT_EQ(std::vector<base::Value>(), router_.RegisterReceiver(&receiver_)); + router_.RegisterReceiver(&receiver_); EXPECT_FALSE(manager_->IsLoggingActive()); EXPECT_CALL(notified_object_, NotifyAboutLoggingActivity()); @@ -150,7 +150,7 @@ TEST_F(LogManagerTest, InterleaveSuspendAndLoggingActivation_ActiveFirst) { EXPECT_FALSE(manager_->IsLoggingActive()); EXPECT_CALL(notified_object_, NotifyAboutLoggingActivity()); - EXPECT_EQ(std::vector<base::Value>(), router_.RegisterReceiver(&receiver_)); + router_.RegisterReceiver(&receiver_); EXPECT_TRUE(manager_->IsLoggingActive()); EXPECT_CALL(notified_object_, NotifyAboutLoggingActivity()); diff --git a/chromium/components/autofill/core/browser/logging/log_receiver.h b/chromium/components/autofill/core/browser/logging/log_receiver.h index e984c33eaac..16d6fa91120 100644 --- a/chromium/components/autofill/core/browser/logging/log_receiver.h +++ b/chromium/components/autofill/core/browser/logging/log_receiver.h @@ -13,14 +13,14 @@ namespace autofill { // logs about progress of actions like saving a password. class LogReceiver { public: - LogReceiver() {} + LogReceiver() = default; LogReceiver(const LogReceiver&) = delete; LogReceiver& operator=(const LogReceiver&) = delete; - virtual ~LogReceiver() {} + virtual ~LogReceiver() = default; - virtual void LogEntry(const base::Value& entry) = 0; + virtual void LogEntry(const base::Value::Dict& entry) = 0; }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/logging/log_router.cc b/chromium/components/autofill/core/browser/logging/log_router.cc index 52ebb49d8ce..1e4507900a0 100644 --- a/chromium/components/autofill/core/browser/logging/log_router.cc +++ b/chromium/components/autofill/core/browser/logging/log_router.cc @@ -18,28 +18,27 @@ LogRouter::LogRouter() = default; LogRouter::~LogRouter() = default; // static -base::Value LogRouter::CreateEntryForText(const std::string& text) { - LogBuffer buffer; +base::Value::Dict LogRouter::CreateEntryForText(const std::string& text) { + LogBuffer buffer(LogBuffer::IsActive(true)); buffer << Tag{"div"}; for (const auto& line : base::SplitStringPiece( text, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { buffer << line << Br{}; } buffer << CTag{}; - return buffer.RetrieveResult(); + return *buffer.RetrieveResult(); } void LogRouter::ProcessLog(const std::string& text) { ProcessLog(CreateEntryForText(text)); } -void LogRouter::ProcessLog(base::Value&& node) { +void LogRouter::ProcessLog(const base::Value::Dict& node) { // This may not be called when there are no receivers (i.e., the router is // inactive), because in that case the logs cannot be displayed. DCHECK(!receivers_.empty()); - accumulated_logs_.emplace_back(std::move(node)); for (LogReceiver& receiver : receivers_) - receiver.LogEntry(accumulated_logs_.back()); + receiver.LogEntry(node); } bool LogRouter::RegisterManager(LogManager* manager) { @@ -53,26 +52,19 @@ void LogRouter::UnregisterManager(LogManager* manager) { managers_.RemoveObserver(manager); } -const std::vector<base::Value>& LogRouter::RegisterReceiver( - LogReceiver* receiver) { +void LogRouter::RegisterReceiver(LogReceiver* receiver) { DCHECK(receiver); - DCHECK(accumulated_logs_.empty() || !receivers_.empty()); - if (receivers_.empty()) { for (LogManager& manager : managers_) manager.OnLogRouterAvailabilityChanged(true); } receivers_.AddObserver(receiver); - return accumulated_logs_; } void LogRouter::UnregisterReceiver(LogReceiver* receiver) { DCHECK(receivers_.HasObserver(receiver)); receivers_.RemoveObserver(receiver); if (receivers_.empty()) { - // |accumulated_logs_| can become very long; use the swap instead of clear() - // to ensure that the memory is freed. - std::vector<base::Value>().swap(accumulated_logs_); for (LogManager& manager : managers_) manager.OnLogRouterAvailabilityChanged(false); } diff --git a/chromium/components/autofill/core/browser/logging/log_router.h b/chromium/components/autofill/core/browser/logging/log_router.h index 2ef73cc25d7..b335bd35e47 100644 --- a/chromium/components/autofill/core/browser/logging/log_router.h +++ b/chromium/components/autofill/core/browser/logging/log_router.h @@ -32,11 +32,11 @@ class LogRouter : public KeyedService { ~LogRouter() override; // Returns a JSON entry that can be fed into the logger. - static base::Value CreateEntryForText(const std::string& text); + static base::Value::Dict CreateEntryForText(const std::string& text); // Passes logs to the router. Only call when there are receivers registered. void ProcessLog(const std::string& text); - void ProcessLog(base::Value&& node); + void ProcessLog(const base::Value::Dict& node); // All four (Unr|R)egister* methods below are safe to call from the // constructor of the registered object, because they do not call that object, @@ -50,11 +50,7 @@ class LogRouter : public KeyedService { void UnregisterManager(LogManager* manager); // The receivers must register to get updates with new logs in the future. - // RegisterReceiver adds |receiver| to the right observer list, and returns - // the logs accumulated so far. (It returns by value, not const ref, to - // provide a snapshot as opposed to a link to |accumulated_logs_|.) - [[nodiscard]] const std::vector<base::Value>& RegisterReceiver( - LogReceiver* receiver); + void RegisterReceiver(LogReceiver* receiver); // Remove |receiver| from the observers list. void UnregisterReceiver(LogReceiver* receiver); @@ -64,9 +60,6 @@ class LogRouter : public KeyedService { // on destruction. base::ObserverList<LogManager, true>::Unchecked managers_; base::ObserverList<LogReceiver, true>::Unchecked receivers_; - - // Logs accumulated since the first receiver was registered. - std::vector<base::Value> accumulated_logs_; }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/logging/log_router_unittest.cc b/chromium/components/autofill/core/browser/logging/log_router_unittest.cc index 987a453c599..1e5464958f7 100644 --- a/chromium/components/autofill/core/browser/logging/log_router_unittest.cc +++ b/chromium/components/autofill/core/browser/logging/log_router_unittest.cc @@ -25,7 +25,7 @@ class MockLogReceiver : public LogReceiver { MockLogReceiver(const MockLogReceiver&) = delete; MockLogReceiver& operator=(const MockLogReceiver&) = delete; - MOCK_METHOD(void, LogEntry, (const base::Value&), (override)); + MOCK_METHOD(void, LogEntry, (const base::Value::Dict&), (override)); }; class MockLogManager : public StubLogManager { @@ -47,57 +47,23 @@ class LogRouterTest : public testing::Test { testing::StrictMock<MockLogManager> manager_; }; -TEST_F(LogRouterTest, ProcessLog_NoReceiver) { - LogRouter router; - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver_)); - base::Value log_entry = LogRouter::CreateEntryForText(kTestText); - EXPECT_CALL(receiver_, LogEntry(testing::Eq(testing::ByRef(log_entry)))) - .Times(1); - router.ProcessLog(kTestText); - router.UnregisterReceiver(&receiver_); - // Without receivers, accumulated logs should not have been kept. That means - // that on the registration of the first receiver, none are returned. - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver_)); - router.UnregisterReceiver(&receiver_); -} - TEST_F(LogRouterTest, ProcessLog_OneReceiver) { LogRouter router; - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver_)); - // Check that logs generated after activation are passed. - base::Value log_entry = LogRouter::CreateEntryForText(kTestText); - EXPECT_CALL(receiver_, LogEntry(testing::Eq(testing::ByRef(log_entry)))) - .Times(1); - router.ProcessLog(kTestText); - router.UnregisterReceiver(&receiver_); -} - -TEST_F(LogRouterTest, ProcessLog_TwoReceiversAccumulatedLogsPassed) { - LogRouter router; - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver_)); - - // Log something with only the first receiver, to accumulate some logs. - base::Value log_entry = LogRouter::CreateEntryForText(kTestText); + router.RegisterReceiver(&receiver_); + base::Value::Dict log_entry = LogRouter::CreateEntryForText(kTestText); EXPECT_CALL(receiver_, LogEntry(testing::Eq(testing::ByRef(log_entry)))) .Times(1); - EXPECT_CALL(receiver2_, LogEntry(testing::Eq(testing::ByRef(log_entry)))) - .Times(0); router.ProcessLog(kTestText); - // Accumulated logs get passed on registration. - std::vector<base::Value> expected_logs; - expected_logs.emplace_back(log_entry.Clone()); - EXPECT_EQ(expected_logs, router.RegisterReceiver(&receiver2_)); router.UnregisterReceiver(&receiver_); - router.UnregisterReceiver(&receiver2_); } TEST_F(LogRouterTest, ProcessLog_TwoReceiversBothUpdated) { LogRouter router; - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver_)); - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver2_)); + router.RegisterReceiver(&receiver_); + router.RegisterReceiver(&receiver2_); // Check that both receivers get log updates. - base::Value log_entry = LogRouter::CreateEntryForText(kTestText); + base::Value::Dict log_entry = LogRouter::CreateEntryForText(kTestText); EXPECT_CALL(receiver_, LogEntry(testing::Eq(testing::ByRef(log_entry)))) .Times(1); EXPECT_CALL(receiver2_, LogEntry(testing::Eq(testing::ByRef(log_entry)))) @@ -109,13 +75,13 @@ TEST_F(LogRouterTest, ProcessLog_TwoReceiversBothUpdated) { TEST_F(LogRouterTest, ProcessLog_TwoReceiversNoUpdateAfterUnregistering) { LogRouter router; - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver_)); - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver2_)); + router.RegisterReceiver(&receiver_); + router.RegisterReceiver(&receiver2_); // Check that no logs are passed to an unregistered receiver. router.UnregisterReceiver(&receiver_); EXPECT_CALL(receiver_, LogEntry(_)).Times(0); - base::Value log_entry = LogRouter::CreateEntryForText(kTestText); + base::Value::Dict log_entry = LogRouter::CreateEntryForText(kTestText); EXPECT_CALL(receiver2_, LogEntry(testing::Eq(testing::ByRef(log_entry)))) .Times(1); router.ProcessLog(kTestText); @@ -131,7 +97,7 @@ TEST_F(LogRouterTest, RegisterManager_NoReceivers) { TEST_F(LogRouterTest, RegisterManager_OneReceiverBeforeManager) { LogRouter router; // First register a receiver. - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver_)); + router.RegisterReceiver(&receiver_); // The manager should be told the LogRouter has some receivers. EXPECT_TRUE(router.RegisterManager(&manager_)); // Now unregister the receiver. The manager should be told the LogRouter has @@ -148,7 +114,7 @@ TEST_F(LogRouterTest, RegisterManager_OneManagerBeforeReceiver) { EXPECT_FALSE(router.RegisterManager(&manager_)); // Now register the receiver. The manager should be notified. EXPECT_CALL(manager_, OnLogRouterAvailabilityChanged(true)); - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver_)); + router.RegisterReceiver(&receiver_); // Now unregister the manager. router.UnregisterManager(&manager_); // Now unregister the receiver. The manager should not hear about it. @@ -163,10 +129,10 @@ TEST_F(LogRouterTest, RegisterManager_OneManagerTwoReceivers) { EXPECT_FALSE(router.RegisterManager(&manager_)); // Now register the 1st receiver. The manager should be notified. EXPECT_CALL(manager_, OnLogRouterAvailabilityChanged(true)); - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver_)); + router.RegisterReceiver(&receiver_); // Now register the 2nd receiver. The manager should not be notified. EXPECT_CALL(manager_, OnLogRouterAvailabilityChanged(true)).Times(0); - EXPECT_EQ(std::vector<base::Value>(), router.RegisterReceiver(&receiver2_)); + router.RegisterReceiver(&receiver2_); // Now unregister the 1st receiver. The manager should not hear about it. EXPECT_CALL(manager_, OnLogRouterAvailabilityChanged(false)).Times(0); router.UnregisterReceiver(&receiver_); diff --git a/chromium/components/autofill/core/browser/logging/stub_log_manager.cc b/chromium/components/autofill/core/browser/logging/stub_log_manager.cc index 8fab7f94dd5..00a6ad19621 100644 --- a/chromium/components/autofill/core/browser/logging/stub_log_manager.cc +++ b/chromium/components/autofill/core/browser/logging/stub_log_manager.cc @@ -12,7 +12,7 @@ void StubLogManager::SetSuspended(bool suspended) {} void StubLogManager::LogTextMessage(const std::string& text) const {} -void StubLogManager::LogEntry(base::Value&& entry) const {} +void StubLogManager::LogEntry(const base::Value::Dict& entry) const {} bool StubLogManager::IsLoggingActive() const { return false; diff --git a/chromium/components/autofill/core/browser/logging/stub_log_manager.h b/chromium/components/autofill/core/browser/logging/stub_log_manager.h index 66b4290ae09..949bfe2bf32 100644 --- a/chromium/components/autofill/core/browser/logging/stub_log_manager.h +++ b/chromium/components/autofill/core/browser/logging/stub_log_manager.h @@ -26,7 +26,7 @@ class StubLogManager : public LogManager { void OnLogRouterAvailabilityChanged(bool router_can_be_used) override; void SetSuspended(bool suspended) override; void LogTextMessage(const std::string& text) const override; - void LogEntry(base::Value&& entry) const override; + void LogEntry(const base::Value::Dict& entry) const override; bool IsLoggingActive() const override; LogBufferSubmitter Log() override; }; diff --git a/chromium/components/autofill/core/browser/merchant_promo_code_manager.cc b/chromium/components/autofill/core/browser/merchant_promo_code_manager.cc index 0c78f120478..ee3af4bcdbf 100644 --- a/chromium/components/autofill/core/browser/merchant_promo_code_manager.cc +++ b/chromium/components/autofill/core/browser/merchant_promo_code_manager.cc @@ -18,29 +18,43 @@ MerchantPromoCodeManager::MerchantPromoCodeManager() = default; MerchantPromoCodeManager::~MerchantPromoCodeManager() = default; -void MerchantPromoCodeManager::OnGetSingleFieldSuggestions( +bool MerchantPromoCodeManager::OnGetSingleFieldSuggestions( int query_id, bool is_autocomplete_enabled, bool autoselect_first_suggestion, - const std::u16string& name, - const std::u16string& prefix, - const std::string& form_control_type, + const FormFieldData& field, base::WeakPtr<SuggestionsHandler> handler, const SuggestionsContext& context) { + // We don't check whether |is_autocomplete_enabled| is false because it is + // redundant. If |is_autocomplete_enabled| is false, then autofill + // wallet import must be disabled. Disabling autofill wallet import (turning + // off the "Save and fill payment methods" toggle) will disable offering + // suggestions and filling promo codes, because it will cause + // PersonalDataManager::GetActiveAutofillPromoCodeOffersForOrigin() to return + // an empty vector. + bool field_is_eligible = + context.focused_field && + context.focused_field->Type().GetStorableType() == MERCHANT_PROMO_CODE; + if (!field_is_eligible) + return false; + // If merchant promo code offers are available for the given site, and the // profile is not OTR, show the promo code offers. if (!is_off_the_record_ && personal_data_manager_) { - std::vector<const AutofillOfferData*> promo_code_offers = + const std::vector<const AutofillOfferData*> promo_code_offers = personal_data_manager_->GetActiveAutofillPromoCodeOffersForOrigin( context.form_structure->main_frame_origin().GetURL()); if (!promo_code_offers.empty()) { SendPromoCodeSuggestions( - promo_code_offers, - QueryHandler(query_id, autoselect_first_suggestion, prefix, handler)); - uma_recorder_.OnOffersSuggestionsShown(name, promo_code_offers); - return; + promo_code_offers, QueryHandler(query_id, autoselect_first_suggestion, + field.value, handler)); + // TODO(crbug.com/1190334): Fix the metrics logging to not log if we will + // not show promo code autofill suggestions. + uma_recorder_.OnOffersSuggestionsShown(field.name, promo_code_offers); + return true; } } + return false; } void MerchantPromoCodeManager::OnWillSubmitFormWithFields( @@ -73,7 +87,7 @@ base::WeakPtr<MerchantPromoCodeManager> MerchantPromoCodeManager::GetWeakPtr() { void MerchantPromoCodeManager::UMARecorder::OnOffersSuggestionsShown( const std::u16string& name, - std::vector<const AutofillOfferData*>& offers) { + const std::vector<const AutofillOfferData*>& offers) { // Log metrics related to the showing of overall offers suggestions popup. autofill_metrics::LogOffersSuggestionsPopupShown( /*first_time_being_logged=*/most_recent_suggestions_shown_field_name_ != @@ -156,6 +170,11 @@ void MerchantPromoCodeManager::SendPromoCodeSuggestions( for (const AutofillOfferData* promo_code_offer : promo_code_offers) { if (query_handler.prefix_ == base::ASCIIToUTF16(promo_code_offer->GetPromoCode())) { + // Return empty suggestions to query handler. This will result in no + // suggestions being displayed. + query_handler.handler_->OnSuggestionsReturned( + query_handler.client_query_id_, + query_handler.autoselect_first_suggestion_, {}); return; } } diff --git a/chromium/components/autofill/core/browser/merchant_promo_code_manager.h b/chromium/components/autofill/core/browser/merchant_promo_code_manager.h index d461ae9cae9..ec0c0c3e196 100644 --- a/chromium/components/autofill/core/browser/merchant_promo_code_manager.h +++ b/chromium/components/autofill/core/browser/merchant_promo_code_manager.h @@ -33,14 +33,13 @@ class MerchantPromoCodeManager : public SingleFieldFormFiller, ~MerchantPromoCodeManager() override; // SingleFieldFormFiller overrides: - void OnGetSingleFieldSuggestions(int query_id, - bool is_autocomplete_enabled, - bool autoselect_first_suggestion, - const std::u16string& name, - const std::u16string& prefix, - const std::string& form_control_type, - base::WeakPtr<SuggestionsHandler> handler, - const SuggestionsContext& context) override; + [[nodiscard]] bool OnGetSingleFieldSuggestions( + int query_id, + bool is_autocomplete_enabled, + bool autoselect_first_suggestion, + const FormFieldData& field, + base::WeakPtr<SuggestionsHandler> handler, + const SuggestionsContext& context) override; void OnWillSubmitFormWithFields(const std::vector<FormFieldData>& fields, bool is_autocomplete_enabled) override; void CancelPendingQueries(const SuggestionsHandler* handler) override; @@ -80,7 +79,7 @@ class MerchantPromoCodeManager : public SingleFieldFormFiller, void OnOffersSuggestionsShown( const std::u16string& name, - std::vector<const AutofillOfferData*>& offers); + const std::vector<const AutofillOfferData*>& offers); void OnOfferSuggestionSelected(int frontend_id); private: diff --git a/chromium/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc b/chromium/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc index b6543802b03..e2b1ca8e82b 100644 --- a/chromium/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc @@ -6,9 +6,9 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/metrics/payments/offers_metrics.h" #include "components/autofill/core/browser/suggestions_context.h" -#include "components/autofill/core/browser/test_form_structure.h" #include "components/autofill/core/browser/test_personal_data_manager.h" #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/form_data.h" @@ -17,11 +17,16 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" +using testing::_; using testing::Field; +using testing::Truly; using testing::UnorderedElementsAre; namespace autofill { +using FieldPrediction = + AutofillQueryResponse::FormSuggestion::FieldSuggestion::FieldPrediction; + namespace { class MockSuggestionsHandler @@ -55,6 +60,8 @@ class MerchantPromoCodeManagerTest : public testing::Test { merchant_promo_code_manager_ = std::make_unique<MerchantPromoCodeManager>(); merchant_promo_code_manager_->Init(personal_data_manager_.get(), /*is_off_the_record=*/false); + test::CreateTestFormField(/*label=*/"", "Some Field Name", "SomePrefix", + "Some Type", &test_field_); } // Sets up the TestPersonalDataManager with a promo code offer for the given @@ -64,6 +71,7 @@ class MerchantPromoCodeManagerTest : public testing::Test { std::string SetUpPromoCodeOffer(std::string origin, const GURL& offer_details_url) { personal_data_manager_.get()->SetAutofillWalletImportEnabled(true); + personal_data_manager_.get()->SetAutofillCreditCardEnabled(true); AutofillOfferData testPromoCodeOfferData = test::GetPromoCodeOfferData(GURL(origin)); testPromoCodeOfferData.SetOfferDetailsUrl(offer_details_url); @@ -72,26 +80,35 @@ class MerchantPromoCodeManagerTest : public testing::Test { return testPromoCodeOfferData.GetPromoCode(); } + // Adds a promo code focused field to the suggestions context. + void AddPromoCodeFocusedFieldToSuggestionsContext(SuggestionsContext* out) { + FieldPrediction merchant_promo_code_field_prediction; + merchant_promo_code_field_prediction.set_type(MERCHANT_PROMO_CODE); + autofill_field_.set_server_predictions( + {merchant_promo_code_field_prediction}); + out->focused_field = &autofill_field_; + } + std::unique_ptr<MerchantPromoCodeManager> merchant_promo_code_manager_; std::unique_ptr<TestPersonalDataManager> personal_data_manager_; + FormFieldData test_field_; + AutofillField autofill_field_; }; TEST_F(MerchantPromoCodeManagerTest, ShowsPromoCodeSuggestions) { base::HistogramTester histogram_tester; auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; bool autoselect_first_suggestion = false; bool is_autocomplete_enabled = true; - std::string form_control_type = "Some Type"; std::string last_committed_origin_url = "https://www.example.com"; FormData form_data; form_data.main_frame_origin = url::Origin::Create(GURL(last_committed_origin_url)); - TestFormStructure form_structure{form_data}; + FormStructure form_structure{form_data}; SuggestionsContext context; context.form_structure = &form_structure; + AddPromoCodeFocusedFieldToSuggestionsContext(&context); std::string promo_code = SetUpPromoCodeOffer( last_committed_origin_url, GURL("https://offer-details-url.com/")); Suggestion promo_code_suggestion = Suggestion(base::ASCIIToUTF16(promo_code)); @@ -106,6 +123,7 @@ TEST_F(MerchantPromoCodeManagerTest, ShowsPromoCodeSuggestions) { test_query_id, autoselect_first_suggestion, UnorderedElementsAre( Field(&Suggestion::main_text, promo_code_suggestion.main_text), + Field(&Suggestion::frontend_id, POPUP_ITEM_ID_SEPARATOR), Field(&Suggestion::main_text, footer_suggestion.main_text)))) .Times(3); @@ -113,27 +131,27 @@ TEST_F(MerchantPromoCodeManagerTest, ShowsPromoCodeSuggestions) { // Because all criteria are met, active promo code suggestions for the given // merchant site will be displayed instead of requesting Autocomplete // suggestions. - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( test_query_id, is_autocomplete_enabled, autoselect_first_suggestion, - test_name, test_prefix, form_control_type, - suggestions_handler->GetWeakPtr(), - /*context=*/context); + test_field_, suggestions_handler->GetWeakPtr(), + /*context=*/context)); // Trigger offers suggestions popup again to be able to test that we do not // log metrics twice for the same field. - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( test_query_id, is_autocomplete_enabled, autoselect_first_suggestion, - test_name, test_prefix, form_control_type, - suggestions_handler->GetWeakPtr(), - /*context=*/context); + test_field_, suggestions_handler->GetWeakPtr(), + /*context=*/context)); // Trigger offers suggestions popup again to be able to test that we log // metrics more than once if it is a different field. - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + FormFieldData other_field; + test::CreateTestFormField(/*label=*/"", "Some Other Name", "SomePrefix", + "Some Type", &other_field); + EXPECT_TRUE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( test_query_id, is_autocomplete_enabled, autoselect_first_suggestion, - u"other_name", test_prefix, form_control_type, - suggestions_handler->GetWeakPtr(), - /*context=*/context); + other_field, suggestions_handler->GetWeakPtr(), + /*context=*/context)); histogram_tester.ExpectBucketCount( "Autofill.Offer.SuggestionsPopupShown", @@ -154,6 +172,41 @@ TEST_F(MerchantPromoCodeManagerTest, ShowsPromoCodeSuggestions) { } TEST_F(MerchantPromoCodeManagerTest, + DoesNotShowPromoCodeOffersIfFieldIsNotAPromoCodeField) { + base::HistogramTester histogram_tester; + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + + // Setting up mock to verify that suggestions returning is not triggered if + // the field is not a promo code field. + EXPECT_CALL(*suggestions_handler, OnSuggestionsReturned).Times(0); + + // Simulate request for suggestions. + EXPECT_FALSE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + /*query_id=*/2, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), + /*context=*/SuggestionsContext())); + + // Ensure that no metrics were logged. + histogram_tester.ExpectBucketCount( + "Autofill.Offer.SuggestionsPopupShown", + autofill_metrics::OffersSuggestionsPopupEvent:: + kOffersSuggestionsPopupShownOnce, + 0); + histogram_tester.ExpectBucketCount( + "Autofill.Offer.SuggestionsPopupShown", + autofill_metrics::OffersSuggestionsPopupEvent:: + kOffersSuggestionsPopupShown, + 0); + histogram_tester.ExpectBucketCount( + "Autofill.Offer.Suggestion.GPayPromoCodeOffer", + autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 0); + histogram_tester.ExpectBucketCount( + "Autofill.Offer.Suggestion.GPayPromoCodeOffer", + autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0); +} + +TEST_F(MerchantPromoCodeManagerTest, DoesNotShowPromoCodeOffersForOffTheRecord) { base::HistogramTester histogram_tester; auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); @@ -163,9 +216,10 @@ TEST_F(MerchantPromoCodeManagerTest, FormData form_data; form_data.main_frame_origin = url::Origin::Create(GURL(last_committed_origin_url)); - TestFormStructure form_structure{form_data}; + FormStructure form_structure{form_data}; SuggestionsContext context; context.form_structure = &form_structure; + AddPromoCodeFocusedFieldToSuggestionsContext(&context); merchant_promo_code_manager_->is_off_the_record_ = true; // Setting up mock to verify that suggestions returning is not triggered if @@ -173,11 +227,11 @@ TEST_F(MerchantPromoCodeManagerTest, EXPECT_CALL(*suggestions_handler, OnSuggestionsReturned).Times(0); // Simulate request for suggestions. - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + EXPECT_FALSE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( /*query_id=*/2, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name", - /*prefix=*/u"SomePrefix", "Some Type", suggestions_handler->GetWeakPtr(), - /*context=*/context); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), + /*context=*/context)); // Ensure that no metrics were logged. histogram_tester.ExpectBucketCount( @@ -206,9 +260,10 @@ TEST_F(MerchantPromoCodeManagerTest, FormData form_data; form_data.main_frame_origin = url::Origin::Create(GURL(last_committed_origin_url)); - TestFormStructure form_structure{form_data}; + FormStructure form_structure{form_data}; SuggestionsContext context; context.form_structure = &form_structure; + AddPromoCodeFocusedFieldToSuggestionsContext(&context); merchant_promo_code_manager_->personal_data_manager_ = nullptr; // Setting up mock to verify that suggestions returning is not triggered if @@ -216,11 +271,11 @@ TEST_F(MerchantPromoCodeManagerTest, EXPECT_CALL(*suggestions_handler, OnSuggestionsReturned).Times(0); // Simulate request for suggestions. - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + EXPECT_FALSE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( /*query_id=*/2, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name", - /*prefix=*/u"SomePrefix", "Some Type", suggestions_handler->GetWeakPtr(), - /*context=*/context); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), + /*context=*/context)); // Ensure that no metrics were logged. histogram_tester.ExpectBucketCount( @@ -246,23 +301,25 @@ TEST_F(MerchantPromoCodeManagerTest, NoPromoCodeOffers) { auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); std::string last_committed_origin_url = "https://www.example.com"; personal_data_manager_.get()->SetAutofillWalletImportEnabled(true); + personal_data_manager_.get()->SetAutofillCreditCardEnabled(true); FormData form_data; form_data.main_frame_origin = url::Origin::Create(GURL(last_committed_origin_url)); - TestFormStructure form_structure{form_data}; + FormStructure form_structure{form_data}; SuggestionsContext context; context.form_structure = &form_structure; + AddPromoCodeFocusedFieldToSuggestionsContext(&context); // Setting up mock to verify that suggestions returning is not triggered if // there are no promo code offers to suggest. EXPECT_CALL(*suggestions_handler, OnSuggestionsReturned).Times(0); // Simulate request for suggestions. - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + EXPECT_FALSE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( /*query_id=*/2, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name", - /*prefix=*/u"SomePrefix", "Some Type", suggestions_handler->GetWeakPtr(), - /*context=*/context); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), + /*context=*/context)); // Ensure that no metrics were logged. histogram_tester.ExpectBucketCount( @@ -283,25 +340,174 @@ TEST_F(MerchantPromoCodeManagerTest, NoPromoCodeOffers) { autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0); } +// This test case exists to ensure that disabling autofill wallet import (by +// turning off the "Payment methods, offers, and addresses using Google Pay" +// toggle) disables offering suggestions and autofilling for promo codes. +TEST_F(MerchantPromoCodeManagerTest, AutofillWalletImportDisabled) { + base::HistogramTester histogram_tester; + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + std::string last_committed_origin_url = "https://www.example.com"; + FormData form_data; + form_data.main_frame_origin = + url::Origin::Create(GURL(last_committed_origin_url)); + FormStructure form_structure{form_data}; + SuggestionsContext context; + context.form_structure = &form_structure; + AddPromoCodeFocusedFieldToSuggestionsContext(&context); + SetUpPromoCodeOffer(last_committed_origin_url, + GURL("https://offer-details-url.com/")); + personal_data_manager_->SetAutofillWalletImportEnabled(false); + + // Autofill wallet import is disabled, so check that we do not return + // suggestions to the handler. + EXPECT_CALL(*suggestions_handler, OnSuggestionsReturned).Times(0); + + // Simulate request for suggestions. + EXPECT_FALSE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + /*query_id=*/2, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), + /*context=*/context)); + + // Ensure that no metrics were logged. + histogram_tester.ExpectBucketCount( + "Autofill.Offer.SuggestionsPopupShown", + autofill_metrics::OffersSuggestionsPopupEvent:: + kOffersSuggestionsPopupShownOnce, + 0); + histogram_tester.ExpectBucketCount( + "Autofill.Offer.SuggestionsPopupShown", + autofill_metrics::OffersSuggestionsPopupEvent:: + kOffersSuggestionsPopupShown, + 0); + histogram_tester.ExpectBucketCount( + "Autofill.Offer.Suggestion.GPayPromoCodeOffer", + autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 0); + histogram_tester.ExpectBucketCount( + "Autofill.Offer.Suggestion.GPayPromoCodeOffer", + autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0); +} + +// This test case exists to ensure that disabling autofill credit card (by +// turning off the "Save and fill payment methods" toggle) disables offering +// suggestions and autofilling for promo codes. +TEST_F(MerchantPromoCodeManagerTest, AutofillCreditCardDisabled) { + base::HistogramTester histogram_tester; + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + std::string last_committed_origin_url = "https://www.example.com"; + FormData form_data; + form_data.main_frame_origin = + url::Origin::Create(GURL(last_committed_origin_url)); + FormStructure form_structure{form_data}; + SuggestionsContext context; + context.form_structure = &form_structure; + AddPromoCodeFocusedFieldToSuggestionsContext(&context); + SetUpPromoCodeOffer(last_committed_origin_url, + GURL("https://offer-details-url.com/")); + personal_data_manager_->SetAutofillCreditCardEnabled(false); + + // Autofill credit card is disabled, so check that we do not return + // suggestions to the handler. + EXPECT_CALL(*suggestions_handler, OnSuggestionsReturned).Times(0); + + // Simulate request for suggestions. + EXPECT_FALSE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + /*query_id=*/2, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), + /*context=*/context)); + + // Ensure that no metrics were logged. + histogram_tester.ExpectBucketCount( + "Autofill.Offer.SuggestionsPopupShown", + autofill_metrics::OffersSuggestionsPopupEvent:: + kOffersSuggestionsPopupShownOnce, + 0); + histogram_tester.ExpectBucketCount( + "Autofill.Offer.SuggestionsPopupShown", + autofill_metrics::OffersSuggestionsPopupEvent:: + kOffersSuggestionsPopupShown, + 0); + histogram_tester.ExpectBucketCount( + "Autofill.Offer.Suggestion.GPayPromoCodeOffer", + autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, 0); + histogram_tester.ExpectBucketCount( + "Autofill.Offer.Suggestion.GPayPromoCodeOffer", + autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0); +} + +// This test case exists to ensure that we do not offer promo code offer +// suggestions if the field already contains a promo code. +TEST_F(MerchantPromoCodeManagerTest, PrefixMatched) { + base::HistogramTester histogram_tester; + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + std::string last_committed_origin_url = "https://www.example.com"; + FormData form_data; + form_data.main_frame_origin = + url::Origin::Create(GURL(last_committed_origin_url)); + FormStructure form_structure{form_data}; + SuggestionsContext context; + context.form_structure = &form_structure; + AddPromoCodeFocusedFieldToSuggestionsContext(&context); + test_field_.value = base::ASCIIToUTF16(SetUpPromoCodeOffer( + last_committed_origin_url, GURL("https://offer-details-url.com/"))); + + // The field contains the promo code already, so check that we do not return + // suggestions to the handler. + EXPECT_CALL(*suggestions_handler, + OnSuggestionsReturned( + _, _, + testing::Truly( + [](const std::vector<Suggestion>& returned_suggestions) { + return returned_suggestions.empty(); + }))); + + // Simulate request for suggestions. + EXPECT_TRUE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + /*query_id=*/2, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), + /*context=*/context)); + + // Log that suggestions were attempted to be shown. + // TODO(crbug.com/1190334): Fix the metrics logging to not log if we will not + // show promo code autofill suggestions. + // histogram_tester.ExpectBucketCount( + // "Autofill.Offer.SuggestionsPopupShown", + // autofill_metrics::OffersSuggestionsPopupEvent:: + // kOffersSuggestionsPopupShownOnce, + // 0); + // histogram_tester.ExpectBucketCount( + // "Autofill.Offer.SuggestionsPopupShown", + // autofill_metrics::OffersSuggestionsPopupEvent:: + // kOffersSuggestionsPopupShown, + // 0); + // histogram_tester.ExpectBucketCount( + // "Autofill.Offer.Suggestion.GPayPromoCodeOffer", + // autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShownOnce, + // 0); + // histogram_tester.ExpectBucketCount( + // "Autofill.Offer.Suggestion.GPayPromoCodeOffer", + // autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionShown, 0); +} + TEST_F(MerchantPromoCodeManagerTest, OnSingleFieldSuggestion_GPayPromoCodeOfferSuggestion) { // Set up the test. base::HistogramTester histogram_tester; auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; std::u16string test_promo_code = u"test_promo_code"; bool autoselect_first_suggestion = false; bool is_autocomplete_enabled = true; - std::string form_control_type = "Some Type"; std::string last_committed_origin_url = "https://www.example.com"; FormData form_data; form_data.main_frame_origin = url::Origin::Create(GURL(last_committed_origin_url)); - TestFormStructure form_structure{form_data}; + FormStructure form_structure{form_data}; SuggestionsContext context; context.form_structure = &form_structure; + AddPromoCodeFocusedFieldToSuggestionsContext(&context); SetUpPromoCodeOffer(last_committed_origin_url, GURL("https://offer-details-url.com/")); @@ -314,11 +520,10 @@ TEST_F(MerchantPromoCodeManagerTest, autofill_metrics::OffersSuggestionsEvent::kOfferSuggestionSelected, 0); // Simulate showing the promo code offers suggestions popup. - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( test_query_id, is_autocomplete_enabled, autoselect_first_suggestion, - test_name, test_prefix, form_control_type, - suggestions_handler->GetWeakPtr(), - /*context=*/context); + test_field_, suggestions_handler->GetWeakPtr(), + /*context=*/context)); // Simulate selecting a promo code offer suggestion. merchant_promo_code_manager_->OnSingleFieldSuggestionSelected( @@ -334,11 +539,10 @@ TEST_F(MerchantPromoCodeManagerTest, 1); // Simulate showing the promo code offers suggestions popup. - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( test_query_id, is_autocomplete_enabled, autoselect_first_suggestion, - test_name, test_prefix, form_control_type, - suggestions_handler->GetWeakPtr(), - /*context=*/context); + test_field_, suggestions_handler->GetWeakPtr(), + /*context=*/context)); // Simulate selecting a promo code offer suggestion. merchant_promo_code_manager_->OnSingleFieldSuggestionSelected( @@ -360,19 +564,17 @@ TEST_F(MerchantPromoCodeManagerTest, base::HistogramTester histogram_tester; auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); int test_query_id = 2; - std::u16string test_name = u"Some Field Name"; - std::u16string test_prefix = u"SomePrefix"; std::u16string test_promo_code = u"test_promo_code"; bool autoselect_first_suggestion = false; bool is_autocomplete_enabled = true; - std::string form_control_type = "Some Type"; std::string last_committed_origin_url = "https://www.example.com"; FormData form_data; form_data.main_frame_origin = url::Origin::Create(GURL(last_committed_origin_url)); - TestFormStructure form_structure{form_data}; + FormStructure form_structure{form_data}; SuggestionsContext context; context.form_structure = &form_structure; + AddPromoCodeFocusedFieldToSuggestionsContext(&context); SetUpPromoCodeOffer(last_committed_origin_url, GURL("https://offer-details-url.com/")); @@ -387,11 +589,10 @@ TEST_F(MerchantPromoCodeManagerTest, 0); // Simulate showing the promo code offers suggestions popup. - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( test_query_id, is_autocomplete_enabled, autoselect_first_suggestion, - test_name, test_prefix, form_control_type, - suggestions_handler->GetWeakPtr(), - /*context=*/context); + test_field_, suggestions_handler->GetWeakPtr(), + /*context=*/context)); // Simulate selecting a promo code offer suggestion. merchant_promo_code_manager_->OnSingleFieldSuggestionSelected( @@ -410,11 +611,10 @@ TEST_F(MerchantPromoCodeManagerTest, 1); // Simulate showing the promo code offers suggestions popup. - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + EXPECT_TRUE(merchant_promo_code_manager_->OnGetSingleFieldSuggestions( test_query_id, is_autocomplete_enabled, autoselect_first_suggestion, - test_name, test_prefix, form_control_type, - suggestions_handler->GetWeakPtr(), - /*context=*/context); + test_field_, suggestions_handler->GetWeakPtr(), + /*context=*/context)); // Simulate selecting a promo code offer suggestion. merchant_promo_code_manager_->OnSingleFieldSuggestionSelected( diff --git a/chromium/components/autofill/core/browser/metrics/README.md b/chromium/components/autofill/core/browser/metrics/README.md index 8e8b69649e1..d851058ae56 100644 --- a/chromium/components/autofill/core/browser/metrics/README.md +++ b/chromium/components/autofill/core/browser/metrics/README.md @@ -50,17 +50,17 @@ located, as well as how hard it was to find **all** metrics for a given feature. ### The new way -**Don't** use a class, but **do** use the `autofill::metrics` namespace. Then, +**Don't** use a class, but **do** use the `autofill::autofill_metrics` namespace. Then, combine all metrics that are part of a single feature together, so they're not intertwined with the rest of Autofill's metrics. *cool_feature_metrics.h*: +(Put this header file under components/autofill/core/browser/metrics/[optional_sub_directory]) ```c++ {.good} // [Copyright notice and include guards] -namespace autofill { -namespace metrics { +namespace autofill::autofill_metrics { enum class CoolFeatureInteractionMetric { // User accepted the cool feature. @@ -72,10 +72,9 @@ enum class CoolFeatureInteractionMetric { kMaxValue = kIgnored, }; -static void LogCoolFeatureInteraction(CoolFeatureInteractionMetric metric); +void LogCoolFeatureInteraction(CoolFeatureInteractionMetric metric); -} // namespace metrics -} // namespace autofill +} // namespace autofill::autofill_metrics ``` *cool_feature_metrics.cc*: @@ -83,12 +82,10 @@ static void LogCoolFeatureInteraction(CoolFeatureInteractionMetric metric); ```c++ {.good} // [Copyright notice] -#include "components/autofill/core/browser/metrics/cool_feature_metrics.h" +#include "components/autofill/core/browser/metrics/[optional_sub_directory]/cool_feature_metrics.h" -namespace autofill { -namespace metrics { +namespace autofill::autofill_metrics { -// static void LogCoolFeatureInteraction(CoolFeatureInteractionMetric metric) { base::UmaHistogramEnumeration("Autofill.CoolFeatureInteraction", metric); } @@ -96,9 +93,8 @@ void LogCoolFeatureInteraction(CoolFeatureInteractionMetric metric) { // If there are other metrics related to this feature, they'd go here, and it // would be very obvious they're related! -} // namespace metrics -} // namespace autofill +} // namespace autofill::autofill_metrics ``` The calling code of this function would be -`autofill::metrics::LogCoolFeatureInteraction(~)`. +`autofill::autofill_metrics::LogCoolFeatureInteraction(~)`. diff --git a/chromium/components/autofill/core/browser/metrics/autofill_metrics.cc b/chromium/components/autofill/core/browser/metrics/autofill_metrics.cc index 8fea197ba19..d77e28d1fc4 100644 --- a/chromium/components/autofill/core/browser/metrics/autofill_metrics.cc +++ b/chromium/components/autofill/core/browser/metrics/autofill_metrics.cc @@ -335,18 +335,6 @@ int GetFieldTypeGroupPredictionQualityMetric( case PHONE_HOME_CITY_AND_NUMBER: case PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX: case PHONE_HOME_WHOLE_NUMBER: - case PHONE_FAX_NUMBER: - case PHONE_FAX_CITY_CODE: - case PHONE_FAX_COUNTRY_CODE: - case PHONE_FAX_CITY_AND_NUMBER: - case PHONE_FAX_WHOLE_NUMBER: - case ADDRESS_BILLING_LINE1: - case ADDRESS_BILLING_LINE2: - case ADDRESS_BILLING_APT_NUM: - case ADDRESS_BILLING_CITY: - case ADDRESS_BILLING_STATE: - case ADDRESS_BILLING_ZIP: - case ADDRESS_BILLING_COUNTRY: case CREDIT_CARD_NAME_FULL: case CREDIT_CARD_NUMBER: case CREDIT_CARD_EXP_MONTH: @@ -358,25 +346,10 @@ int GetFieldTypeGroupPredictionQualityMetric( case CREDIT_CARD_VERIFICATION_CODE: case COMPANY_NAME: case FIELD_WITH_DEFAULT_VALUE: - case PHONE_BILLING_NUMBER: - case PHONE_BILLING_CITY_CODE: - case PHONE_BILLING_COUNTRY_CODE: - case PHONE_BILLING_CITY_AND_NUMBER: - case PHONE_BILLING_WHOLE_NUMBER: - case NAME_BILLING_FIRST: - case NAME_BILLING_MIDDLE: - case NAME_BILLING_LAST: - case NAME_BILLING_MIDDLE_INITIAL: - case NAME_BILLING_FULL: - case NAME_BILLING_SUFFIX: case MERCHANT_EMAIL_SIGNUP: case MERCHANT_PROMO_CODE: case PASSWORD: case ACCOUNT_CREATION_PASSWORD: - case ADDRESS_BILLING_STREET_ADDRESS: - case ADDRESS_BILLING_SORTING_CODE: - case ADDRESS_BILLING_DEPENDENT_LOCALITY: - case ADDRESS_BILLING_LINE3: case NOT_ACCOUNT_CREATION_PASSWORD: case USERNAME: case USERNAME_AND_EMAIL_ADDRESS: @@ -401,8 +374,10 @@ int GetFieldTypeGroupPredictionQualityMetric( case NAME_FULL_WITH_HONORIFIC_PREFIX: case BIRTHDATE_DAY: case BIRTHDATE_MONTH: - case BIRTHDATE_YEAR_4_DIGITS: + case BIRTHDATE_4_DIGIT_YEAR: + case IBAN_VALUE: case MAX_VALID_FIELD_TYPE: + case CREDIT_CARD_STANDALONE_VERIFICATION_CODE: NOTREACHED() << field_type << " type is not in that group."; group = GROUP_AMBIGUOUS; break; @@ -860,14 +835,6 @@ void AutofillMetrics::LogCreditCardSaveNotOfferedDueToMaxStrikesMetric( } // static -void AutofillMetrics::LogLocalCardMigrationNotOfferedDueToMaxStrikesMetric( - SaveTypeMetric metric) { - UMA_HISTOGRAM_ENUMERATION( - "Autofill.StrikeDatabase.LocalCardMigrationNotOfferedDueToMaxStrikes", - metric); -} - -// static void AutofillMetrics::LogUploadOfferedCardOriginMetric( UploadOfferedCardOriginMetric metric) { DCHECK_LT(metric, NUM_UPLOAD_OFFERED_CARD_ORIGIN_METRICS); @@ -1120,227 +1087,42 @@ void AutofillMetrics::LogScanCreditCardCompleted( } // static -void AutofillMetrics::LogLocalCardMigrationDecisionMetric( - LocalCardMigrationDecisionMetric metric) { - UMA_HISTOGRAM_ENUMERATION("Autofill.LocalCardMigrationDecision", metric); -} - -// static -void AutofillMetrics::LogLocalCardMigrationBubbleOfferMetric( - LocalCardMigrationBubbleOfferMetric metric, - bool is_reshow) { - DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_BUBBLE_OFFER_METRICS); - std::string histogram_name = "Autofill.LocalCardMigrationBubbleOffer."; - histogram_name += is_reshow ? "Reshows" : "FirstShow"; - base::UmaHistogramEnumeration(histogram_name, metric, - NUM_LOCAL_CARD_MIGRATION_BUBBLE_OFFER_METRICS); -} - -// static -void AutofillMetrics::LogLocalCardMigrationBubbleResultMetric( - LocalCardMigrationBubbleResultMetric metric, - bool is_reshow) { - DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_BUBBLE_RESULT_METRICS); - std::string suffix = is_reshow ? ".Reshows" : ".FirstShow"; - base::UmaHistogramEnumeration( - "Autofill.LocalCardMigrationBubbleResult" + suffix, metric, - NUM_LOCAL_CARD_MIGRATION_BUBBLE_RESULT_METRICS); -} - -// static -void AutofillMetrics::LogLocalCardMigrationDialogOfferMetric( - LocalCardMigrationDialogOfferMetric metric) { - DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_DIALOG_OFFER_METRICS); - std::string histogram_name = "Autofill.LocalCardMigrationDialogOffer"; - base::UmaHistogramEnumeration(histogram_name, metric, - NUM_LOCAL_CARD_MIGRATION_DIALOG_OFFER_METRICS); -} - -// static -void AutofillMetrics::LogLocalCardMigrationDialogUserInteractionMetric( - const base::TimeDelta& duration, - LocalCardMigrationDialogUserInteractionMetric metric) { - DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_DIALOG_USER_INTERACTION_METRICS); - base::UmaHistogramEnumeration( - "Autofill.LocalCardMigrationDialogUserInteraction", metric, - NUM_LOCAL_CARD_MIGRATION_DIALOG_USER_INTERACTION_METRICS); - - // Do not log duration metrics for - // LOCAL_CARD_MIGRATION_DIALOG_DELETE_CARD_ICON_CLICKED, as it can happen - // multiple times in one dialog. - std::string suffix; - switch (metric) { - case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_SAVE_BUTTON_CLICKED: - suffix = "Accepted"; - break; - case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_CANCEL_BUTTON_CLICKED: - suffix = "Denied"; - break; - case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_VIEW_CARDS_BUTTON_CLICKED: - case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_DONE_BUTTON_CLICKED: - suffix = "Closed"; - break; - default: - return; - } - - base::UmaHistogramLongTimes( - "Autofill.LocalCardMigrationDialogActiveDuration." + suffix, duration); -} - -// static -void AutofillMetrics::LogLocalCardMigrationDialogUserSelectionPercentageMetric( - int selected, - int total) { - UMA_HISTOGRAM_PERCENTAGE( - "Autofill.LocalCardMigrationDialogUserSelectionPercentage", - 100 * selected / total); -} - -// static -void AutofillMetrics::LogLocalCardMigrationPromptMetric( - LocalCardMigrationOrigin local_card_migration_origin, - LocalCardMigrationPromptMetric metric) { - DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_PROMPT_METRICS); - std::string histogram_name = "Autofill.LocalCardMigrationOrigin."; - // Switch to different sub-histogram depending on local card migration origin. - switch (local_card_migration_origin) { - case LocalCardMigrationOrigin::UseOfLocalCard: - histogram_name += "UseOfLocalCard"; - break; - case LocalCardMigrationOrigin::UseOfServerCard: - histogram_name += "UseOfServerCard"; - break; - case LocalCardMigrationOrigin::SettingsPage: - histogram_name += "SettingsPage"; - break; - default: - NOTREACHED(); - return; - } - base::UmaHistogramEnumeration(histogram_name, metric, - NUM_LOCAL_CARD_MIGRATION_PROMPT_METRICS); -} - -// static -void AutofillMetrics::LogOfferNotificationBubbleOfferMetric( - AutofillOfferData::OfferType offer_type, - bool is_reshow) { - std::string histogram_name = "Autofill.OfferNotificationBubbleOffer."; - // Switch to different sub-histogram depending on offer type being displayed. - switch (offer_type) { - case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER: - histogram_name += "CardLinkedOffer"; - break; - case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER: - histogram_name += "GPayPromoCodeOffer"; - break; - case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER: - histogram_name += "FreeListingCouponOffer"; - break; - case AutofillOfferData::OfferType::UNKNOWN: - NOTREACHED(); - return; - } - base::UmaHistogramBoolean(histogram_name, is_reshow); -} - -// static -void AutofillMetrics::LogOfferNotificationBubbleResultMetric( - AutofillOfferData::OfferType offer_type, - OfferNotificationBubbleResultMetric metric, - bool is_reshow) { - DCHECK_LE(metric, OfferNotificationBubbleResultMetric::kMaxValue); - std::string histogram_name = "Autofill.OfferNotificationBubbleResult."; - // Switch to different sub-histogram depending on offer type being displayed. - switch (offer_type) { - case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER: - histogram_name += "CardLinkedOffer."; - break; - case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER: - histogram_name += "GPayPromoCodeOffer."; - break; - case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER: - histogram_name += "FreeListingCouponOffer."; - break; - case AutofillOfferData::OfferType::UNKNOWN: - NOTREACHED(); - return; - } - // Add subhistogram for |is_reshow| decision. - histogram_name += is_reshow ? "Reshows" : "FirstShow"; - base::UmaHistogramEnumeration(histogram_name, metric); -} - -// static -void AutofillMetrics::LogOfferNotificationBubblePromoCodeButtonClicked( - AutofillOfferData::OfferType offer_type) { - std::string histogram_name = - "Autofill.OfferNotificationBubblePromoCodeButtonClicked."; - // Switch to different sub-histogram depending on offer type being displayed. - // Card-linked offers do not have a promo code button. - switch (offer_type) { - case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER: - histogram_name += "GPayPromoCodeOffer"; +void AutofillMetrics::LogProgressDialogResultMetric( + bool is_canceled_by_user, + AutofillProgressDialogType autofill_progress_dialog_type) { + std::string dialog_type; + switch (autofill_progress_dialog_type) { + case AutofillProgressDialogType::kAndroidFIDOProgressDialog: + dialog_type = "AndroidFIDO"; break; - case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER: - histogram_name += "FreeListingCouponOffer"; + case AutofillProgressDialogType::kVirtualCardUnmaskProgressDialog: + dialog_type = "CardUnmask"; break; - case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER: - case AutofillOfferData::OfferType::UNKNOWN: + case AutofillProgressDialogType::kUnspecified: NOTREACHED(); return; } - base::UmaHistogramBoolean(histogram_name, true); + base::UmaHistogramBoolean( + "Autofill.ProgressDialog." + dialog_type + ".Result", + is_canceled_by_user); } -// static -void AutofillMetrics::LogOfferNotificationBubbleSuppressed( - AutofillOfferData::OfferType offer_type) { - std::string histogram_name = "Autofill.OfferNotificationBubbleSuppressed."; - // Switch to different sub-histogram depending on offer type being suppressed. - // Card-linked offers will not be suppressed. - switch (offer_type) { - case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER: - histogram_name += "GPayPromoCodeOffer"; +void AutofillMetrics::LogProgressDialogShown( + AutofillProgressDialogType autofill_progress_dialog_type) { + std::string dialog_type; + switch (autofill_progress_dialog_type) { + case AutofillProgressDialogType::kAndroidFIDOProgressDialog: + dialog_type = "AndroidFIDO"; break; - case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER: - histogram_name += "FreeListingCouponOffer"; + case AutofillProgressDialogType::kVirtualCardUnmaskProgressDialog: + dialog_type = "CardUnmask"; break; - case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER: - case AutofillOfferData::OfferType::UNKNOWN: + case AutofillProgressDialogType::kUnspecified: NOTREACHED(); return; } - base::UmaHistogramBoolean(histogram_name, true); -} - -// static -void AutofillMetrics::LogOfferNotificationInfoBarDeepLinkClicked() { - base::RecordAction(base::UserMetricsAction( - "Autofill_OfferNotificationInfoBar_DeepLinkClicked")); -} - -// static -void AutofillMetrics::LogOfferNotificationInfoBarResultMetric( - OfferNotificationInfoBarResultMetric metric) { - DCHECK_LE(metric, OfferNotificationInfoBarResultMetric::kMaxValue); - base::UmaHistogramEnumeration( - "Autofill.OfferNotificationInfoBarResult.CardLinkedOffer", metric); -} - -void AutofillMetrics::LogOfferNotificationInfoBarShown() { - base::UmaHistogramBoolean( - "Autofill.OfferNotificationInfoBarOffer.CardLinkedOffer", true); -} - -void AutofillMetrics::LogProgressDialogResultMetric(bool is_canceled_by_user) { - base::UmaHistogramBoolean("Autofill.ProgressDialog.CardUnmask.Result", - is_canceled_by_user); -} - -void AutofillMetrics::LogProgressDialogShown() { - base::UmaHistogramBoolean("Autofill.ProgressDialog.CardUnmask.Shown", true); + base::UmaHistogramBoolean("Autofill.ProgressDialog." + dialog_type + ".Shown", + true); } // static @@ -2236,11 +2018,6 @@ void AutofillMetrics::LogStoredCreditCardMetrics( } // static -void AutofillMetrics::LogSyncedOfferDataBeingValid(bool valid) { - base::UmaHistogramBoolean("Autofill.Offer.SyncedOfferDataBeingValid", valid); -} - -// static void AutofillMetrics::LogNumberOfCreditCardsSuppressedForDisuse( size_t num_cards) { UMA_HISTOGRAM_COUNTS_1000("Autofill.CreditCardsSuppressedForDisuse", @@ -3102,6 +2879,33 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted( builder.Record(ukm_recorder_); } +void AutofillMetrics::FormInteractionsUkmLogger::LogKeyMetrics( + const DenseSet<FormType>& form_types, + bool data_to_fill_available, + bool suggestions_shown, + bool edited_autofilled_field, + bool suggestion_filled, + autofill_assistant::AutofillAssistantIntent intent) { + if (!CanLog()) + return; + + ukm::builders::Autofill_KeyMetrics builder(source_id_); + builder.SetFillingReadiness(data_to_fill_available) + .SetFillingAssistance(suggestion_filled) + .SetFormTypes(FormTypesToBitVector(form_types)); + + if (intent != autofill_assistant::AutofillAssistantIntent::UNDEFINED_INTENT) + builder.SetAutofillAssistantIntent(static_cast<int64_t>(intent)); + + if (suggestions_shown) + builder.SetFillingAcceptance(suggestion_filled); + + if (suggestion_filled) + builder.SetFillingCorrectness(!edited_autofilled_field); + + builder.Record(ukm_recorder_); +} + void AutofillMetrics::FormInteractionsUkmLogger::LogFormEvent( FormEvent form_event, const DenseSet<FormType>& form_types, @@ -3328,16 +3132,6 @@ void AutofillMetrics::LogProfileUpdateWithRemovedPhoneNumberImportDecision( void AutofillMetrics::LogProfileUpdateAffectedType( ServerFieldType affected_type, AutofillClient::SaveAddressProfileOfferUserDecision decision) { - // TODO(crbug.com/1253798): Remove the special-case metric in favor of more - // general one once the majority of clients contribute to the more general - // one. - if (decision == - AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted) { - base::UmaHistogramEnumeration( - "Autofill.ProfileImport.UpdateProfileAffectedType", - ConvertSettingsVisibleFieldTypeForMetrics(affected_type)); - } - // Record the decision-specific metric. base::UmaHistogramEnumeration( base::StrCat({"Autofill.ProfileImport.UpdateProfileAffectedType", @@ -3372,25 +3166,16 @@ void AutofillMetrics::LogUpdateProfileNumberOfEditedFields( void AutofillMetrics::LogUpdateProfileNumberOfAffectedFields( int number_of_edited_fields, AutofillClient::SaveAddressProfileOfferUserDecision decision) { - // TODO(crbug.com/1253798): Remove the special-case metric in favor of more - // general one once the majority of clients contribute to the more general - // one. - if (decision == - AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted) { - base::UmaHistogramExactLinear( - "Autofill.ProfileImport.UpdateProfileNumberOfAffectedFields", - number_of_edited_fields, /*exclusive_max=*/15); - } - // Record the decision-specific metric. base::UmaHistogramExactLinear( - base::StrCat({"Autofill.ProfileImport.UpdateProfileAffectedType", - GetSaveAndUpdatePromptDecisionMetricsSuffix(decision)}), + base::StrCat( + {"Autofill.ProfileImport.UpdateProfileNumberOfAffectedFields", + GetSaveAndUpdatePromptDecisionMetricsSuffix(decision)}), number_of_edited_fields, /*exclusive_max=*/15); // But also collect an histogram for any decision. base::UmaHistogramExactLinear( - "Autofill.ProfileImport.UpdateProfileAffectedType.Any", + "Autofill.ProfileImport.UpdateProfileNumberOfAffectedFields.Any", number_of_edited_fields, /*exclusive_max=*/15); } @@ -3418,6 +3203,19 @@ void AutofillMetrics::LogPhoneNumberImportParsingResult( (with_variation_country_code << 1) | with_app_locale)); } +// static +void AutofillMetrics::LogPhoneNumberGrammarMatched(int grammar_id, + bool suffix_matched, + int num_grammars) { + DCHECK(0 <= grammar_id && grammar_id < num_grammars); + int metric = 2 * grammar_id + suffix_matched; + int max_metric = 2 * (num_grammars - 1) + 1; + // Add 1 everywhere, because UmaHistogramExactLinear is 1-based. + base::UmaHistogramExactLinear( + "Autofill.FieldPrediction.PhoneNumberGrammarUsage", metric + 1, + /*exclusive_max=*/max_metric + 2); +} + void AutofillMetrics::LogVerificationStatusOfNameTokensOnProfileUsage( const AutofillProfile& profile) { constexpr base::StringPiece base_histogram_name = @@ -3556,10 +3354,48 @@ void AutofillMetrics::LogOtpInputDialogNewOtpRequested() { void AutofillMetrics:: LogIsValueNotAutofilledOverExistingValueSameAsSubmittedValue(bool is_same) { base::UmaHistogramBoolean( - "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue", + "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2", is_same); } +// static +void AutofillMetrics::LogAutocompletePredictionCollisionState( + PredictionState prediction_state, + AutocompleteState autocomplete_state) { + if (prediction_state == PredictionState::kNone && + autocomplete_state == AutocompleteState::kNone) { + return; + } + // The buckets are calculated by using the least significant two bits to + // encode the `autocomplete_state`, and the next two bits to encode the + // `prediction_state`. + int bucket = (static_cast<int>(prediction_state) << 2) | + static_cast<int>(autocomplete_state); + // Without (kNone, kNone), 4*4 - 1 = 15 possible pairs remain. Log the bucket + // 0-based, in order to interpret the metric as an enum. + DCHECK(1 <= bucket && bucket <= 15); + UMA_HISTOGRAM_ENUMERATION("Autofill.Autocomplete.PredictionCollisionState", + bucket - 1, 15); +} + +// static +void AutofillMetrics::LogAutocompletePredictionCollisionTypes( + ServerFieldType server_type, + ServerFieldType heuristic_type) { + const std::string kHistogramName = + "Autofill.Autocomplete.PredictionCollisionType."; + if (server_type != NO_SERVER_DATA) { + base::UmaHistogramEnumeration(kHistogramName + "Server", server_type, + ServerFieldType::MAX_VALID_FIELD_TYPE); + } + base::UmaHistogramEnumeration(kHistogramName + "Heuristics", heuristic_type, + ServerFieldType::MAX_VALID_FIELD_TYPE); + base::UmaHistogramEnumeration( + kHistogramName + "ServerOrHeuristics", + server_type != NO_SERVER_DATA ? server_type : heuristic_type, + ServerFieldType::MAX_VALID_FIELD_TYPE); +} + const std::string PaymentsRpcResultToMetricsSuffix( AutofillClient::PaymentsRpcResult result) { std::string result_suffix; diff --git a/chromium/components/autofill/core/browser/metrics/autofill_metrics.h b/chromium/components/autofill/core/browser/metrics/autofill_metrics.h index 3259088da19..891ea1e9e1b 100644 --- a/chromium/components/autofill/core/browser/metrics/autofill_metrics.h +++ b/chromium/components/autofill/core/browser/metrics/autofill_metrics.h @@ -18,7 +18,7 @@ #include "base/time/time.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_profile_import_process.h" -#include "components/autofill/core/browser/data_model/autofill_offer_data.h" +#include "components/autofill/core/browser/autofill_progress_dialog_type.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_types.h" @@ -30,6 +30,7 @@ #include "components/autofill/core/common/mojom/autofill_types.mojom-forward.h" #include "components/autofill/core/common/signatures.h" #include "components/autofill/core/common/unique_ids.h" +#include "components/autofill_assistant/core/public/autofill_assistant_intent.h" #include "components/security_state/core/security_state.h" #include "services/metrics/public/cpp/ukm_recorder.h" #include "services/metrics/public/cpp/ukm_source_id.h" @@ -43,7 +44,6 @@ class Autofill_CreditCardFill; namespace autofill { class AutofillField; -class AutofillOfferData; class CreditCard; class FormEventLoggerBase; @@ -293,36 +293,6 @@ class AutofillMetrics { NUM_SAVE_CARD_PROMPT_RESULT_METRICS, }; - // Metrics to track event when the offer notification bubble is closed. - enum class OfferNotificationBubbleResultMetric { - // These values are persisted to logs. Entries should not be renumbered and - // numeric values should never be reused. - - // The user explicitly acknowledged the bubble by clicking the ok button. - OFFER_NOTIFICATION_BUBBLE_ACKNOWLEDGED = 0, - // The user explicitly closed the prompt with the close button or ESC. - OFFER_NOTIFICATION_BUBBLE_CLOSED = 1, - // The user did not interact with the prompt. - OFFER_NOTIFICATION_BUBBLE_NOT_INTERACTED = 2, - // The prompt lost focus and was deactivated. - OFFER_NOTIFICATION_BUBBLE_LOST_FOCUS = 3, - kMaxValue = OFFER_NOTIFICATION_BUBBLE_LOST_FOCUS, - }; - - // Metrics to track event when the offer notification infobar is closed. - enum class OfferNotificationInfoBarResultMetric { - // These values are persisted to logs. Entries should not be renumbered and - // numeric values should never be reused. - - // User acknowledged the infobar by clicking the ok button. - OFFER_NOTIFICATION_INFOBAR_ACKNOWLEDGED = 0, - // User explicitly closed the infobar with the close button. - OFFER_NOTIFICATION_INFOBAR_CLOSED = 1, - // InfoBar was shown but user did not interact with the it. - OFFER_NOTIFICATION_INFOBAR_IGNORED = 2, - kMaxValue = OFFER_NOTIFICATION_INFOBAR_IGNORED, - }; - // Metrics to track events in CardUnmaskAuthenticationSelectionDialog. enum class CardUnmaskAuthenticationSelectionDialogResultMetric { // These values are persisted to logs. Entries should not be renumbered and @@ -545,116 +515,6 @@ class AutofillMetrics { NUM_SCAN_CREDIT_CARD_PROMPT_METRICS, }; - // Metrics to record the decision on whether to offer local card migration. - enum class LocalCardMigrationDecisionMetric { - // All the required conditions are satisfied and main prompt is shown. - OFFERED = 0, - // Migration not offered because user uses new card. - NOT_OFFERED_USE_NEW_CARD = 1, - // Migration not offered because failed migration prerequisites. - NOT_OFFERED_FAILED_PREREQUISITES = 2, - // The Autofill StrikeDatabase decided not to allow offering migration - // because max strike count was reached. - NOT_OFFERED_REACHED_MAX_STRIKE_COUNT = 3, - // Migration not offered because no migratable cards. - NOT_OFFERED_NO_MIGRATABLE_CARDS = 4, - // Met the migration requirements but the request to Payments for upload - // details failed. - NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED = 5, - // Abandoned the migration because no supported local cards were left after - // filtering out unsupported cards. - NOT_OFFERED_NO_SUPPORTED_CARDS = 6, - // User used a local card and they only have a single migratable local card - // on file, we will offer Upstream instead. - NOT_OFFERED_SINGLE_LOCAL_CARD = 7, - // User used an unsupported local card, we will abort the migration. - NOT_OFFERED_USE_UNSUPPORTED_LOCAL_CARD = 8, - // Legal message was invalid, we will abort the migration. - NOT_OFFERED_INVALID_LEGAL_MESSAGE = 9, - kMaxValue = NOT_OFFERED_INVALID_LEGAL_MESSAGE, - }; - - // Metrics to track events when local credit card migration is offered. - enum LocalCardMigrationBubbleOfferMetric { - // The bubble is requested due to a credit card being used or - // local card migration icon in the omnibox being clicked. - LOCAL_CARD_MIGRATION_BUBBLE_REQUESTED = 0, - // The bubble is actually shown to the user. - LOCAL_CARD_MIGRATION_BUBBLE_SHOWN = 1, - NUM_LOCAL_CARD_MIGRATION_BUBBLE_OFFER_METRICS, - }; - - // Metrics to track user action result of the bubble when the bubble is - // closed. - enum LocalCardMigrationBubbleResultMetric { - // The user explicitly accepted the offer. - LOCAL_CARD_MIGRATION_BUBBLE_ACCEPTED = 0, - // The user explicitly closed the bubble with the close button or ESC. - LOCAL_CARD_MIGRATION_BUBBLE_CLOSED = 1, - // The user did not interact with the bubble. - LOCAL_CARD_MIGRATION_BUBBLE_NOT_INTERACTED = 2, - // The bubble lost its focus and was deactivated. - LOCAL_CARD_MIGRATION_BUBBLE_LOST_FOCUS = 3, - // The reason why the prompt is closed is not clear. Possible reason is the - // logging function is invoked before the closed reason is correctly set. - LOCAL_CARD_MIGRATION_BUBBLE_RESULT_UNKNOWN = 4, - NUM_LOCAL_CARD_MIGRATION_BUBBLE_RESULT_METRICS, - }; - - // Metrics to track events when local card migration dialog is offered. - enum LocalCardMigrationDialogOfferMetric { - // The dialog is shown to the user. - LOCAL_CARD_MIGRATION_DIALOG_SHOWN = 0, - // The dialog is not shown due to legal message being invalid. - LOCAL_CARD_MIGRATION_DIALOG_NOT_SHOWN_INVALID_LEGAL_MESSAGE = 1, - // The dialog is shown when migration feedback is available. - LOCAL_CARD_MIGRATION_DIALOG_FEEDBACK_SHOWN = 2, - // The dialog is shown when migration fails due to server error. - LOCAL_CARD_MIGRATION_DIALOG_FEEDBACK_SERVER_ERROR_SHOWN = 3, - NUM_LOCAL_CARD_MIGRATION_DIALOG_OFFER_METRICS, - }; - - // Metrics to track user interactions with the dialog. - enum LocalCardMigrationDialogUserInteractionMetric { - // The user explicitly accepts the offer by clicking the save button. - LOCAL_CARD_MIGRATION_DIALOG_CLOSED_SAVE_BUTTON_CLICKED = 0, - // The user explicitly denies the offer by clicking the cancel button. - LOCAL_CARD_MIGRATION_DIALOG_CLOSED_CANCEL_BUTTON_CLICKED = 1, - // The user clicks the legal message. - LOCAL_CARD_MIGRATION_DIALOG_LEGAL_MESSAGE_CLICKED = 2, - // The user clicks the view card button after successfully migrated cards. - LOCAL_CARD_MIGRATION_DIALOG_CLOSED_VIEW_CARDS_BUTTON_CLICKED = 3, - // The user clicks the done button to close dialog after migration. - LOCAL_CARD_MIGRATION_DIALOG_CLOSED_DONE_BUTTON_CLICKED = 4, - // The user clicks the trash icon to delete invalid card. - LOCAL_CARD_MIGRATION_DIALOG_DELETE_CARD_ICON_CLICKED = 5, - NUM_LOCAL_CARD_MIGRATION_DIALOG_USER_INTERACTION_METRICS, - }; - - // These metrics are logged for each local card migration origin. These are - // used to derive the conversion rate for each triggering source. - enum LocalCardMigrationPromptMetric { - // The intermediate bubble is shown to the user. - INTERMEDIATE_BUBBLE_SHOWN = 0, - // The intermediate bubble is accepted by the user. - INTERMEDIATE_BUBBLE_ACCEPTED = 1, - // The main dialog is shown to the user. - MAIN_DIALOG_SHOWN = 2, - // The main dialog is accepted by the user. - MAIN_DIALOG_ACCEPTED = 3, - NUM_LOCAL_CARD_MIGRATION_PROMPT_METRICS, - }; - - // Local card migration origin denotes from where the migration is triggered. - enum LocalCardMigrationOrigin { - // Trigger when user submitted a form using local card. - UseOfLocalCard, - // Trigger when user submitted a form using server card. - UseOfServerCard, - // Trigger from settings page. - SettingsPage, - }; - // Each of these metrics is logged only for potentially autofillable forms, // i.e. forms with at least three fields, etc. // These are used to derive certain "user happiness" metrics. For example, we @@ -1290,6 +1150,12 @@ class AutofillMetrics { const base::TimeTicks& form_parsed_timestamp, FormSignature form_signature, const FormInteractionCounts& form_interaction_counts); + void LogKeyMetrics(const DenseSet<FormType>& form_types, + bool data_to_fill_available, + bool suggestions_shown, + bool edited_autofilled_field, + bool suggestion_filled, + autofill_assistant::AutofillAssistantIntent intent); void LogFormEvent(FormEvent form_event, const DenseSet<FormType>& form_types, const base::TimeTicks& form_parsed_timestamp); @@ -1335,6 +1201,22 @@ class AutofillMetrics { const raw_ptr<FormInteractionsUkmLogger> logger_; }; + enum class PredictionState { + kNone = 0, + kServer = 1, + kHeuristics = 2, + kBoth = 3, + kMaxValue = kBoth + }; + + enum class AutocompleteState { + kNone = 0, + kValid = 1, + kGarbage = 2, + kOff = 3, + kMaxValue = kOff + }; + AutofillMetrics() = delete; AutofillMetrics(const AutofillMetrics&) = delete; AutofillMetrics& operator=(const AutofillMetrics&) = delete; @@ -1357,11 +1239,6 @@ class AutofillMetrics { static void LogCreditCardSaveNotOfferedDueToMaxStrikesMetric( SaveTypeMetric metric); - // When local card migration is not offered due to max strike limit reached, - // logs the occurrence. - static void LogLocalCardMigrationNotOfferedDueToMaxStrikesMetric( - SaveTypeMetric metric); - // When credit card upload is offered, logs whether the card being offered is // already a local card on the device or not. static void LogUploadOfferedCardOriginMetric( @@ -1417,42 +1294,11 @@ class AutofillMetrics { static void LogCreditCardUploadFeedbackMetric( CreditCardUploadFeedbackMetric metric); static void LogScanCreditCardPromptMetric(ScanCreditCardPromptMetric metric); - static void LogLocalCardMigrationDecisionMetric( - LocalCardMigrationDecisionMetric metric); - static void LogLocalCardMigrationBubbleOfferMetric( - LocalCardMigrationBubbleOfferMetric metric, - bool is_reshow); - static void LogLocalCardMigrationBubbleResultMetric( - LocalCardMigrationBubbleResultMetric metric, - bool is_reshow); - static void LogLocalCardMigrationDialogOfferMetric( - LocalCardMigrationDialogOfferMetric metric); - static void LogLocalCardMigrationDialogUserInteractionMetric( - const base::TimeDelta& duration, - LocalCardMigrationDialogUserInteractionMetric metric); - static void LogLocalCardMigrationDialogUserSelectionPercentageMetric( - int selected, - int total); - static void LogLocalCardMigrationPromptMetric( - LocalCardMigrationOrigin local_card_migration_origin, - LocalCardMigrationPromptMetric metric); - static void LogOfferNotificationBubbleOfferMetric( - AutofillOfferData::OfferType offer_type, - bool is_reshow); - static void LogOfferNotificationBubbleResultMetric( - AutofillOfferData::OfferType offer_type, - OfferNotificationBubbleResultMetric metric, - bool is_reshow); - static void LogOfferNotificationBubblePromoCodeButtonClicked( - AutofillOfferData::OfferType offer_type); - static void LogOfferNotificationBubbleSuppressed( - AutofillOfferData::OfferType offer_type); - static void LogOfferNotificationInfoBarDeepLinkClicked(); - static void LogOfferNotificationInfoBarResultMetric( - OfferNotificationInfoBarResultMetric metric); - static void LogOfferNotificationInfoBarShown(); - static void LogProgressDialogResultMetric(bool is_canceled_by_user); - static void LogProgressDialogShown(); + static void LogProgressDialogResultMetric( + bool is_canceled_by_user, + AutofillProgressDialogType autofill_progress_dialog_type); + static void LogProgressDialogShown( + AutofillProgressDialogType autofill_progress_dialog_type); static void LogVirtualCardManualFallbackBubbleShown(bool is_reshow); static void LogVirtualCardManualFallbackBubbleResultMetric( VirtualCardManualFallbackBubbleResultMetric metric, @@ -1708,9 +1554,6 @@ class AutofillMetrics { size_t server_card_count_with_card_art_image, base::TimeDelta disused_data_threshold); - // Logs whether the synced autofill offer data is valid. - static void LogSyncedOfferDataBeingValid(bool invalid); - // Log the number of autofill credit card suggestions suppressed because they // have not been used for a long time and are expired. Note that these cards // are only suppressed when the user has not typed any data into the field @@ -2051,6 +1894,15 @@ class AutofillMetrics { bool with_variation_country_code, bool with_app_locale); + // Logs that local heuristics matched phone number fields using `grammar_id`. + // `suffix_matched` indicates if the special case handling for phone number + // suffixes was triggered. + // `num_grammars` indicates the total number of phone number grammars. It is + // not logged and used for validation. + static void LogPhoneNumberGrammarMatched(int grammar_id, + bool suffix_matched, + int num_grammars); + // Logs when the virtual card metadata for one card have been updated. static void LogVirtualCardMetadataSynced(bool existing_card); @@ -2114,6 +1966,17 @@ class AutofillMetrics { static void LogIsValueNotAutofilledOverExistingValueSameAsSubmittedValue( bool is_same); + // Logs a field's (PredictionState, AutocompleteState) pair on form submit. + static void LogAutocompletePredictionCollisionState( + PredictionState prediction_state, + AutocompleteState autocomplete_state); + + // Logs a field's server and heuristic type on form submit. This is only used + // for fields with autocomplete=garbage. + static void LogAutocompletePredictionCollisionTypes( + ServerFieldType server_type, + ServerFieldType heuristic_types); + private: static void Log(AutocompleteEvent event); }; diff --git a/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc b/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc index 158a195d005..1bf6bad7c66 100644 --- a/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc +++ b/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.cc @@ -145,10 +145,10 @@ void AutofillMetricsBaseTest::OnDidGetRealPan( AutofillClient::PaymentsRpcResult result, const std::string& real_pan, bool is_virtual_card) { - payments::FullCardRequest* full_card_request = - autofill_manager() - .credit_card_access_manager_->GetOrCreateCVCAuthenticator() - ->full_card_request_.get(); + payments::FullCardRequest* full_card_request = autofill_manager() + .client() + ->GetCVCAuthenticator() + ->full_card_request_.get(); DCHECK(full_card_request); // Fake user response. @@ -164,10 +164,10 @@ void AutofillMetricsBaseTest::OnDidGetRealPan( } void AutofillMetricsBaseTest::OnDidGetRealPanWithNonHttpOkResponse() { - payments::FullCardRequest* full_card_request = - autofill_manager() - .credit_card_access_manager_->GetOrCreateCVCAuthenticator() - ->full_card_request_.get(); + payments::FullCardRequest* full_card_request = autofill_manager() + .client() + ->GetCVCAuthenticator() + ->full_card_request_.get(); DCHECK(full_card_request); // Fake user response. diff --git a/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.h b/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.h index c869713d05d..82a00417be0 100644 --- a/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.h +++ b/chromium/components/autofill/core/browser/metrics/autofill_metrics_test_base.h @@ -86,6 +86,28 @@ class AutofillMetricsBaseTest : public testing::Test { void ResetDriverToCommitMetrics() { autofill_driver_.reset(); } + void ChangeTextField(const FormData& form, + const FormFieldData& field, + base::TimeTicks timestamp = {}) { + autofill_manager().OnTextFieldDidChange(form, field, gfx::RectF(), + timestamp); + } + + void FillAutofillFormData(const FormData& form, + base::TimeTicks timestamp = {}) { + autofill_manager().OnDidFillAutofillFormData(form, timestamp); + } + + void SeeForm(const FormData& form) { + autofill_manager().OnFormsSeen({form}, {}); + } + + void SubmitForm(const FormData& form) { + autofill_manager().OnFormSubmitted( + form, /*known_success=*/false, + mojom::SubmissionSource::FORM_SUBMISSION); + } + // Mocks a credit card fetching was completed. This mock starts from the // BrowserAutofillManager. Use these if your test does not depends on // OnDidGetRealPan but just need to mock the card fetching result (so that diff --git a/chromium/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc index 12031ebda35..dc84f884673 100644 --- a/chromium/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc +++ b/chromium/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc @@ -35,6 +35,7 @@ #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/form_structure_test_api.h" #include "components/autofill/core/browser/metrics/autofill_metrics_test_base.h" #include "components/autofill/core/browser/metrics/form_events/address_form_event_logger.h" #include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h" @@ -50,7 +51,6 @@ #include "components/autofill/core/browser/test_autofill_tick_clock.h" #include "components/autofill/core/browser/test_browser_autofill_manager.h" #include "components/autofill/core/browser/test_form_data_importer.h" -#include "components/autofill/core/browser/test_form_structure.h" #include "components/autofill/core/browser/test_personal_data_manager.h" #include "components/autofill/core/browser/ui/popup_item_ids.h" #include "components/autofill/core/browser/ui/popup_types.h" @@ -118,6 +118,7 @@ using UkmFieldFillStatusType = ukm::builders::Autofill_FieldFillStatus; using UkmFormEventType = ukm::builders::Autofill_FormEvent; using UkmEditedAutofilledFieldAtSubmission = ukm::builders::Autofill_EditedAutofilledFieldAtSubmission; +using UkmAutofillKeyMetricsType = ukm::builders::Autofill_KeyMetrics; using ExpectedUkmMetricsRecord = std::vector<std::pair<const char*, int64_t>>; using ExpectedUkmMetrics = std::vector<ExpectedUkmMetricsRecord>; @@ -409,14 +410,12 @@ TEST_F(AutofillMetricsTest, NumberOfAutofilledFieldsAtSubmission) { autofill_manager().AddSeenForm(form, heuristic_types, server_types); // Simulate user changing the second field of the form. - autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[1]); form.fields.at(1).is_autofilled = false; // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // Test that the correct bucket for the number of filled fields received a // count while the others remain at zero counts. @@ -489,14 +488,12 @@ TEST_F(AutofillMetricsTest, autofill_manager().AddSeenForm(form, heuristic_types, server_types); // Simulate user changing the second and forth field of the form. - autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[1]); form.fields.at(1).is_autofilled = false; // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // Test that the correct bucket for the number of filled fields with an // unrecognized autocomplete attriute received a count while the others remain @@ -525,326 +522,131 @@ TEST_F(AutofillMetricsTest, } } -// Test that we log the perfect filling metric correctly for an address form in -// which every field is autofilled. -TEST_F(AutofillMetricsTest, PerfectFillingForAddresses_AllAutofillFilled) { - // Set up our form data with two autofilled fields. - FormData form = - test::GetFormData({.description_for_logging = "PerectFilling", - .fields = - { - {.label = u"Name", - .name = u"name", - .value = u"Elvis Aaron Presley", - .is_autofilled = true}, - {.label = u"Email", - .name = u"email", - .value = u"buddy@gmail.com", - .is_autofilled = true}, - {.label = u"City", - .name = u"city", - .value = u"Munich", - .is_autofilled = true}, - }, - .unique_renderer_id = test::MakeFormRendererId(), - .main_frame_origin = url::Origin::Create( - autofill_client_->form_origin())}); - - std::vector<ServerFieldType> heuristic_types = {NAME_FULL, EMAIL_ADDRESS, - ADDRESS_HOME_CITY}; - std::vector<ServerFieldType> server_types = {NAME_FULL, EMAIL_ADDRESS, - ADDRESS_HOME_CITY}; - - // Simulate having seen this form on page load. - autofill_manager().AddSeenForm(form, heuristic_types, server_types); - - // Simulate form submission. - base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); - - // Here, it is expected that there is a count for perfect filling for - // addresses. - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"), - BucketsAre(Bucket(false, 0), Bucket(true, 1))); - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"), - BucketsAre(Bucket(false, 0), Bucket(true, 0))); -} - -// Test that we log the perfect filling metric correctly for an address form in -// which every field is autofilled or empty. -TEST_F(AutofillMetricsTest, - PerfectFillingForAddresses_AllAutofillFilledOrEmpty) { - // Set up our form data with two autofilled fields. - FormData form = - test::GetFormData({.description_for_logging = "PerectFilling", - .fields = - { - {.label = u"Name", - .name = u"name", - .value = u"Elvis Aaron Presley", - .is_autofilled = true}, - {.label = u"Email", - .name = u"email", - .value = u"buddy@gmail.com", - .is_autofilled = true}, - {.label = u"City", - .name = u"city", - .value = u"", - .is_autofilled = false}, - }, - .unique_renderer_id = test::MakeFormRendererId(), - .main_frame_origin = url::Origin::Create( - autofill_client_->form_origin())}); - - std::vector<ServerFieldType> heuristic_types = {NAME_FULL, EMAIL_ADDRESS, - ADDRESS_HOME_CITY}; - std::vector<ServerFieldType> server_types = {NAME_FULL, EMAIL_ADDRESS, - ADDRESS_HOME_CITY}; - - // Simulate having seen this form on page load. - autofill_manager().AddSeenForm(form, heuristic_types, server_types); - - // Simulate form submission. - base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); - - // Here, it is expected that there is a count for perfect filling for - // addresses. - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"), - BucketsAre(Bucket(false, 0), Bucket(true, 1))); - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"), - BucketsAre(Bucket(false, 0), Bucket(true, 0))); -} - -// Test that we log the perfect filling metric correctly for an address form in -// which a non-empty field is not autofilled. -TEST_F(AutofillMetricsTest, PerfectFillingForAddresses_NotAllAutofilled) { - // Set up our form data with two autofilled fields. - FormData form = - test::GetFormData({.description_for_logging = "PerectFilling", - .fields = - { - {.label = u"Name", - .name = u"name", - .value = u"Elvis Aaron Presley", - .is_autofilled = true}, - {.label = u"Email", - .name = u"email", - .value = u"buddy@gmail.com", - .is_autofilled = true}, - {.label = u"City", - .name = u"city", - .value = u"Munich", - .is_autofilled = false}, - }, - .unique_renderer_id = test::MakeFormRendererId(), - .main_frame_origin = url::Origin::Create( - autofill_client_->form_origin())}); - - std::vector<ServerFieldType> heuristic_types = {NAME_FULL, EMAIL_ADDRESS, - ADDRESS_HOME_CITY}; - std::vector<ServerFieldType> server_types = {NAME_FULL, EMAIL_ADDRESS, - ADDRESS_HOME_CITY}; - - // Simulate having seen this form on page load. - autofill_manager().AddSeenForm(form, heuristic_types, server_types); - - // Simulate form submission. - base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); - - // Here, it is expected that there is a count for non-perfect filling for - // addresses. - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"), - BucketsAre(Bucket(false, 1), Bucket(true, 0))); - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"), - BucketsAre(Bucket(false, 0), Bucket(true, 0))); -} - -// Test that we log the perfect filling metric correctly for a credit card form -// in which every field is autofilled. -TEST_F(AutofillMetricsTest, PerfectFillingForCreditCards_AllAutofilled) { - // Set up our form data with two autofilled fields. - FormData form = - test::GetFormData({.description_for_logging = "PerectFilling", - .fields = - { - {.label = u"Name", - .name = u"name", - .value = u"Elvis Aaron Presley", - .is_autofilled = true}, - {.label = u"CCNumber", - .name = u"ccnumber", - .value = u"01230123012399", - .is_autofilled = true}, - }, - .unique_renderer_id = test::MakeFormRendererId(), - .main_frame_origin = url::Origin::Create( - autofill_client_->form_origin())}); - - std::vector<ServerFieldType> heuristic_types = {CREDIT_CARD_NAME_FULL, - CREDIT_CARD_NUMBER}; - std::vector<ServerFieldType> server_types = {CREDIT_CARD_NAME_FULL, - CREDIT_CARD_NUMBER}; - - // Simulate having seen this form on page load. - autofill_manager().AddSeenForm(form, heuristic_types, server_types); - - // Simulate form submission. - base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); - - // Here, it is expected that there is a count for perfect filling for credit - // cards. - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"), - BucketsAre(Bucket(false, 0), Bucket(true, 0))); - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"), - BucketsAre(Bucket(false, 0), Bucket(true, 1))); -} - -// Test that we log the perfect filling metric correctly for a credit card form -// in which not every field is autofilled or empty. -TEST_F(AutofillMetricsTest, PerfectFillingForCreditCards_NotAllAutofilled) { - // Set up our form data with two autofilled fields. - FormData form = - test::GetFormData({.description_for_logging = "PerectFilling", - .fields = - { - {.label = u"Name", - .name = u"name", - .value = u"Elvis Aaron Presley", - .is_autofilled = true}, - {.label = u"CCNumber", - .name = u"ccnumber", - .value = u"01230123012399", - .is_autofilled = false}, - }, - .unique_renderer_id = test::MakeFormRendererId(), - .main_frame_origin = url::Origin::Create( - autofill_client_->form_origin())}); - - std::vector<ServerFieldType> heuristic_types = {CREDIT_CARD_NAME_FULL, - CREDIT_CARD_NUMBER}; - std::vector<ServerFieldType> server_types = {CREDIT_CARD_NAME_FULL, - CREDIT_CARD_NUMBER}; - - // Simulate having seen this form on page load. - autofill_manager().AddSeenForm(form, heuristic_types, server_types); - - // Simulate form submission. - base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); - - // Here, it is expected that there is a count for non-perfect filling for - // credit cards. - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"), - BucketsAre(Bucket(false, 0), Bucket(true, 0))); - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"), - BucketsAre(Bucket(false, 1), Bucket(true, 0))); -} - -// Test that we log the perfect filling metric correctly for a form that -// contains both credit card and address information. Here, the form is fully -// autofilled resulting in a perfect count for both addresses and credit cards. -TEST_F(AutofillMetricsTest, PerfectFillingForMixedForm_AllAutofilled) { - // Set up our form data with two autofilled fields. - FormData form = - test::GetFormData({.description_for_logging = "PerectFilling", - .fields = - { - {.label = u"Name", - .name = u"name", - .value = u"Elvis Aaron Presley", - .is_autofilled = true}, - {.label = u"CCNumber", - .name = u"ccnumber", - .value = u"01230123012399", - .is_autofilled = true}, - }, - .unique_renderer_id = test::MakeFormRendererId(), - .main_frame_origin = url::Origin::Create( - autofill_client_->form_origin())}); - - std::vector<ServerFieldType> heuristic_types = {NAME_FULL, - CREDIT_CARD_NUMBER}; - std::vector<ServerFieldType> server_types = {NAME_FULL, CREDIT_CARD_NUMBER}; - - // Simulate having seen this form on page load. - autofill_manager().AddSeenForm(form, heuristic_types, server_types); +struct Field { + ServerFieldType field_type; + bool is_autofilled = true; + absl::optional<std::u16string> value = absl::nullopt; +}; - // Simulate form submission. - base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); +struct PerfectFillingTestCase { + std::string description; + std::vector<Field> fields; + std::vector<Bucket> address_buckets; + std::vector<Bucket> credit_card_buckets; +}; - // Here, it is expected that there is a count for perfect filling for credit - // cards and for addresses. - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"), - BucketsAre(Bucket(false, 0), Bucket(true, 1))); - EXPECT_THAT( - histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"), - BucketsAre(Bucket(false, 0), Bucket(true, 1))); -} +class AutofillPerfectFillingMetricsTest + : public AutofillMetricsTest, + public ::testing::WithParamInterface<PerfectFillingTestCase> { + public: + std::vector<test::FieldDataDescription> GetFields(std::vector<Field> fields) { + std::vector<test::FieldDataDescription> fields_to_return; + for (const auto& field : fields) { + test::FieldDataDescription f; + if (field.value) { + f.value = field.value; + } else if (field.field_type == NAME_FULL || + field.field_type == CREDIT_CARD_NAME_FULL) { + f.value = u"Elvis Aaron Presley"; + } else if (field.field_type == EMAIL_ADDRESS) { + f.value = u"buddy@gmail.com"; + } else if (field.field_type == ADDRESS_HOME_CITY) { + f.value = u"Munich"; + } else if (field.field_type == CREDIT_CARD_NUMBER) { + f.value = u"01230123012399"; + } else { + NOTREACHED(); + } + f.role = field.field_type; + f.is_autofilled = field.is_autofilled; + fields_to_return.push_back(f); + } + return fields_to_return; + } +}; -// Test that we log the perfect filling metric correctly for a form that -// contains both credit card and address information. Here, the form is not -// fully autofilled resulting in a non-perfect count for both addresses and -// credit cards -TEST_F(AutofillMetricsTest, PerfectFillingForMixedForm_NotAllAutofilled) { - // Set up our form data with two autofilled fields. +INSTANTIATE_TEST_SUITE_P( + AutofillPerfectFillingMetricsTest, + AutofillPerfectFillingMetricsTest, + testing::Values( + // Test that we log the perfect filling metric correctly for an address + // form in which every field is autofilled. + PerfectFillingTestCase{ + "PerfectFillingForAddresses_AllAutofillFilled", + {{NAME_FULL}, {EMAIL_ADDRESS}, {ADDRESS_HOME_CITY}}, + {Bucket(false, 0), Bucket(true, 1)}, + {Bucket(false, 0), Bucket(true, 0)}}, + // Test that we log the perfect filling metric correctly for an address + // form in which every field is autofilled or empty. + PerfectFillingTestCase{ + "PerfectFillingForAddresses_AllAutofillFilledOrEmpty", + {{NAME_FULL}, {EMAIL_ADDRESS}, {ADDRESS_HOME_CITY, false, u""}}, + {Bucket(false, 0), Bucket(true, 1)}, + {Bucket(false, 0), Bucket(true, 0)}}, + // Test that we log the perfect filling metric correctly for an address + // form in which a non-empty field is not autofilled. + PerfectFillingTestCase{ + "PerfectFillingForAddresses_NotAllAutofilled", + {{NAME_FULL}, {EMAIL_ADDRESS}, {ADDRESS_HOME_CITY, false}}, + {Bucket(false, 1), Bucket(true, 0)}, + {Bucket(false, 0), Bucket(true, 0)}}, + // Test that we log the perfect filling metric correctly for a credit + // card form in which every field is autofilled. + PerfectFillingTestCase{"PerfectFillingForCreditCards_AllAutofilled", + {{CREDIT_CARD_NAME_FULL}, {CREDIT_CARD_NUMBER}}, + {Bucket(false, 0), Bucket(true, 0)}, + {Bucket(false, 0), Bucket(true, 1)}}, + // Test that we log the perfect filling metric correctly for a credit + // card form in which not every field is autofilled or empty. + PerfectFillingTestCase{ + "PerfectFillingForCreditCards_NotAllAutofilled", + {{CREDIT_CARD_NAME_FULL}, {CREDIT_CARD_NUMBER, false}}, + {Bucket(false, 0), Bucket(true, 0)}, + {Bucket(false, 1), Bucket(true, 0)}}, + // Test that we log the perfect filling metric correctly for a form that + // contains both credit card and address information. Here, the form is + // fully autofilled resulting in a perfect count for both addresses and + // credit cards. + PerfectFillingTestCase{"PerfectFillingForMixedForm_AllAutofilled", + {{NAME_FULL}, {CREDIT_CARD_NUMBER}}, + {Bucket(false, 0), Bucket(true, 1)}, + {Bucket(false, 0), Bucket(true, 1)}}, + // Test that we log the perfect filling metric correctly for a form that + // contains both credit card and address information. Here, the form is + // not fully autofilled resulting in a non-perfect count for both + // addresses and credit cards. + PerfectFillingTestCase{"PerfectFillingForMixedForm_NotAllAutofilled", + {{NAME_FULL}, {CREDIT_CARD_NUMBER, false}}, + {Bucket(false, 1), Bucket(true, 0)}, + {Bucket(false, 1), Bucket(true, 0)}})); + +TEST_P(AutofillPerfectFillingMetricsTest, + PerfectFilling_Addresses_CreditCards) { + auto test_case = GetParam(); FormData form = - test::GetFormData({.description_for_logging = "PerectFilling", - .fields = - { - {.label = u"Name", - .name = u"name", - .value = u"Elvis Aaron Presley", - .is_autofilled = true}, - {.label = u"CCNumber", - .name = u"ccnumber", - .value = u"01230123012399", - .is_autofilled = false}, - }, + test::GetFormData({.description_for_logging = test_case.description, + .fields = GetFields(test_case.fields), .unique_renderer_id = test::MakeFormRendererId(), .main_frame_origin = url::Origin::Create( autofill_client_->form_origin())}); - std::vector<ServerFieldType> heuristic_types = {NAME_FULL, - CREDIT_CARD_NUMBER}; - std::vector<ServerFieldType> server_types = {NAME_FULL, CREDIT_CARD_NUMBER}; + std::vector<ServerFieldType> field_types; + for (const auto& f : test_case.fields) + field_types.push_back(f.field_type); // Simulate having seen this form on page load. - autofill_manager().AddSeenForm(form, heuristic_types, server_types); + autofill_manager().AddSeenForm(form, field_types, field_types); // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); - // Here, it is expected that there is a count for non-perfect filling for - // credit cards and for addresses. EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.PerfectFilling.Addresses"), - BucketsAre(Bucket(false, 1), Bucket(true, 0))); + BucketsAre(test_case.address_buckets)); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.PerfectFilling.CreditCards"), - BucketsAre(Bucket(false, 1), Bucket(true, 0))); + BucketsAre(test_case.credit_card_buckets)); } // Test that we log quality metrics appropriately. @@ -893,8 +695,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // Auxiliary function for GetAllSamples() expectations. auto b = [](ServerFieldType field_type, @@ -997,8 +798,7 @@ TEST_F(AutofillMetricsTest, ProfileImportStatus_NoImport) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); using Metric = AutofillMetrics::AddressProfileImportStatusMetric; EXPECT_THAT( @@ -1045,8 +845,7 @@ TEST_F(AutofillMetricsTest, ProfileImportStatus_RegularImport) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); using Metric = AutofillMetrics::AddressProfileImportStatusMetric; EXPECT_THAT( @@ -1105,8 +904,7 @@ TEST_F(AutofillMetricsTest, ProfileImportStatus_UnionImport) { base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); using Metric = AutofillMetrics::AddressProfileImportStatusMetric; EXPECT_THAT( @@ -1154,8 +952,7 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_AllFulfilled) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); std::vector<AddressProfileImportRequirementExpectations> expectations = { {AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, true}, @@ -1235,8 +1032,7 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_MissingHomeLineOne) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); std::vector<AddressProfileImportRequirementExpectations> expectations = { {AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, true}, @@ -1320,8 +1116,7 @@ TEST_F(AutofillMetricsTest, // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); std::vector<AddressProfileImportRequirementExpectations> expectations = { {AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, true}, @@ -1411,8 +1206,7 @@ TEST_F(AutofillMetricsTest, // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); std::vector<AddressProfileImportRequirementExpectations> expectations = { {AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, false}, @@ -1505,8 +1299,7 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_NonUniqueEmail) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); std::vector<AddressProfileImportRequirementExpectations> expectations = { {AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, true}, @@ -1587,8 +1380,7 @@ TEST_F(AutofillMetricsTest, ProfileImportRequirements_OnlyAddressLineOne) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); std::vector<AddressProfileImportRequirementExpectations> expectations = { {AddressImportRequirements::STATE_VALID_REQUIREMENT_FULFILLED, true}, @@ -1701,8 +1493,7 @@ TEST_F(AutofillMetricsTest, // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // Rationalization quality. { @@ -1769,8 +1560,7 @@ TEST_F(AutofillMetricsTest, // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // Rationalization quality. { @@ -1976,11 +1766,9 @@ TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) { FormSignature form_signature = Collapse(CalculateFormSignature(form)); FormStructure form_structure(form); - std::vector<FormStructure*> forms; - forms.push_back(&form_structure); std::vector<ServerFieldType> field_types; - for (size_t i = 0; i < forms[0]->field_count(); ++i) + for (size_t i = 0; i < form_structure.field_count(); ++i) field_types.push_back(UNKNOWN_TYPE); // Simulate having seen this form on page load. @@ -1997,7 +1785,8 @@ TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) { std::string response_string = SerializeAndEncode(response); FormStructure::ParseApiQueryResponse( - response_string, forms, test::GetEncodedSignatures(forms), + response_string, {&form_structure}, + test::GetEncodedSignatures({&form_structure}), autofill_manager().form_interactions_ukm_logger(), nullptr); ASSERT_EQ(test_ukm_recorder_ @@ -2093,11 +1882,9 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { FormSignature form_signature = Collapse(CalculateFormSignature(form)); FormStructure form_structure(form); - std::vector<FormStructure*> forms; - forms.push_back(&form_structure); std::vector<ServerFieldType> field_types; - for (size_t i = 0; i < forms[0]->field_count(); ++i) + for (size_t i = 0; i < form_structure.field_count(); ++i) field_types.push_back(UNKNOWN_TYPE); // Simulate having seen this form on page load. @@ -2116,7 +1903,8 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { std::string response_string = SerializeAndEncode(response); FormStructure::ParseApiQueryResponse( - response_string, forms, test::GetEncodedSignatures(forms), + response_string, {&form_structure}, + test::GetEncodedSignatures({&form_structure}), autofill_manager().form_interactions_ukm_logger(), nullptr); ASSERT_EQ(test_ukm_recorder_ @@ -2129,7 +1917,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { test_ukm_recorder_, form, UkmLogRepeatedServerTypePredictionRationalized::kEntryName, {{{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName, - form_signature.value()}, + *form_signature}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, field_signature[0].value()}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, @@ -2149,7 +1937,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { kFieldNewOverallTypeName, ADDRESS_HOME_COUNTRY}}, {{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName, - form_signature.value()}, + *form_signature}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, field_signature[1].value()}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, @@ -2169,7 +1957,7 @@ TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName, ADDRESS_HOME_COUNTRY}}, {{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName, - form_signature.value()}, + *form_signature}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, field_signature[2].value()}, {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, @@ -2246,8 +2034,7 @@ TEST_F(AutofillMetricsTest, // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // Rationalization quality. { @@ -2330,8 +2117,7 @@ TEST_F(AutofillMetricsTest, // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // Auxiliary function for GetAllSamples() expectations. auto b = [](ServerFieldType field_type, @@ -2581,8 +2367,7 @@ TEST_P(QualityMetricsTest, Classification) { // Run the form submission code while tracking the histograms. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); ExpectedUkmMetrics expected_ukm_metrics; AppendFieldTypeUkm(form, heuristic_types, server_types, actual_types, @@ -2735,11 +2520,7 @@ TEST_F(AutofillMetricsTest, TimingMetrics) { field.is_autofilled = false; form.fields.push_back(field); - // Simulate a OnFormsSeen() call that should trigger the recording. - std::vector<FormData> forms; - forms.push_back(form); - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); // Because these metrics are related to timing, it is not possible to know in // advance which bucket the sample will fall into, so we just need to make @@ -2747,8 +2528,22 @@ TEST_F(AutofillMetricsTest, TimingMetrics) { EXPECT_FALSE( histogram_tester.GetAllSamples("Autofill.Timing.DetermineHeuristicTypes") .empty()); - EXPECT_FALSE( - histogram_tester.GetAllSamples("Autofill.Timing.ParseForm").empty()); + if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) { + EXPECT_FALSE( + histogram_tester.GetAllSamples("Autofill.Timing.ParseForm").empty()); + } else { + EXPECT_FALSE( + histogram_tester.GetAllSamples("Autofill.Timing.ParseFormsAsync") + .empty()); + EXPECT_FALSE( + histogram_tester + .GetAllSamples("Autofill.Timing.ParseFormsAsync.RunHeuristics") + .empty()); + EXPECT_FALSE( + histogram_tester + .GetAllSamples("Autofill.Timing.ParseFormsAsync.UpdateCache") + .empty()); + } } // Test that we log quality metrics appropriately when an upload is triggered @@ -2808,138 +2603,64 @@ TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) { autofill_manager().AddSeenForm(form, heuristic_types, server_types); // Simulate text input on one of the fields. - autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[0]); + autofill_manager().SetSeenFormPredictions(form.global_id(), heuristic_types, + server_types); // Trigger a form upload and metrics by Resetting the manager. base::HistogramTester histogram_tester; autofill_manager().Reset(); - // Heuristic predictions. - { - std::string aggregate_histogram = - "Autofill.FieldPredictionQuality.Aggregate.Heuristic.NoSubmission"; - std::string by_field_type_histogram = - "Autofill.FieldPredictionQuality.ByFieldType.Heuristic.NoSubmission"; - // False Negative: - histogram_tester.ExpectBucketCount( - aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), - 1); - // Match: - histogram_tester.ExpectBucketCount(aggregate_histogram, - AutofillMetrics::TRUE_POSITIVE, 2); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - NAME_FULL, AutofillMetrics::TRUE_POSITIVE), - 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::TRUE_POSITIVE), - 1); - // Mismatch: - histogram_tester.ExpectBucketCount( - aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - EMAIL_ADDRESS, AutofillMetrics::FALSE_NEGATIVE_MISMATCH), - 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_MISMATCH), - 1); - // False Positives: - histogram_tester.ExpectBucketCount( - aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - NAME_FULL, AutofillMetrics::FALSE_POSITIVE_EMPTY), - 1); - histogram_tester.ExpectBucketCount( - aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_UNKNOWN), - 1); + auto Buck = [](ServerFieldType field_type, + AutofillMetrics::FieldTypeQualityMetric metric, size_t n) { + return Bucket(GetFieldTypeGroupPredictionQualityMetric(field_type, metric), + n); + }; - // Sanity Check: - histogram_tester.ExpectTotalCount(aggregate_histogram, 6); - histogram_tester.ExpectTotalCount(by_field_type_histogram, 7); + for (const std::string source : {"Heuristic", "Server", "Overall"}) { + EXPECT_THAT(histogram_tester.GetAllSamples( + "Autofill.FieldPredictionQuality.Aggregate." + source + + ".NoSubmission"), + BucketsAre(Bucket(AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1), + Bucket(AutofillMetrics::TRUE_POSITIVE, 2), + Bucket(AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1), + Bucket(AutofillMetrics::FALSE_POSITIVE_EMPTY, 1), + Bucket(AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1))) + << "source=" << source; } + // Heuristic predictions. + EXPECT_THAT( + histogram_tester.GetAllSamples( + "Autofill.FieldPredictionQuality.ByFieldType.Heuristic.NoSubmission"), + BucketsAre( + Buck(ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, + 1), + Buck(NAME_FULL, AutofillMetrics::TRUE_POSITIVE, 1), + Buck(PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::TRUE_POSITIVE, 1), + Buck(EMAIL_ADDRESS, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1), + Buck(PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_MISMATCH, 1), + Buck(NAME_FULL, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1), + Buck(PHONE_HOME_NUMBER, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1))); + // Server predictions override heuristics, so server and overall will be the // same. for (const std::string source : {"Server", "Overall"}) { - std::string aggregate_histogram = - "Autofill.FieldPredictionQuality.Aggregate." + source + ".NoSubmission"; - std::string by_field_type_histogram = - "Autofill.FieldPredictionQuality.ByFieldType." + source + - ".NoSubmission"; - - // Unknown. - histogram_tester.ExpectBucketCount( - aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), - 1); - // Match: - histogram_tester.ExpectBucketCount(aggregate_histogram, - AutofillMetrics::TRUE_POSITIVE, 2); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - EMAIL_ADDRESS, AutofillMetrics::TRUE_POSITIVE), - 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::TRUE_POSITIVE), - 1); - // Mismatch: - histogram_tester.ExpectBucketCount( - aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - NAME_FULL, AutofillMetrics::FALSE_NEGATIVE_MISMATCH), - 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_MISMATCH), - 1); - - // False Positives: - histogram_tester.ExpectBucketCount( - aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_EMPTY), - 1); - histogram_tester.ExpectBucketCount( - aggregate_histogram, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1); - histogram_tester.ExpectBucketCount( - by_field_type_histogram, - GetFieldTypeGroupPredictionQualityMetric( - EMAIL_ADDRESS, AutofillMetrics::FALSE_POSITIVE_UNKNOWN), - 1); - - // Sanity Check: - histogram_tester.ExpectTotalCount(aggregate_histogram, 6); - histogram_tester.ExpectTotalCount(by_field_type_histogram, 7); + EXPECT_THAT( + histogram_tester.GetAllSamples( + "Autofill.FieldPredictionQuality.ByFieldType." + source + + ".NoSubmission"), + BucketsAre( + Buck(ADDRESS_HOME_COUNTRY, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, + 1), + Buck(EMAIL_ADDRESS, AutofillMetrics::TRUE_POSITIVE, 1), + Buck(PHONE_HOME_WHOLE_NUMBER, AutofillMetrics::TRUE_POSITIVE, 1), + Buck(NAME_FULL, AutofillMetrics::FALSE_NEGATIVE_MISMATCH, 1), + Buck(NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_MISMATCH, 1), + Buck(NAME_FIRST, AutofillMetrics::FALSE_POSITIVE_EMPTY, 1), + Buck(EMAIL_ADDRESS, AutofillMetrics::FALSE_POSITIVE_UNKNOWN, 1))) + << "source=" << source; } } @@ -2976,9 +2697,9 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) { field.autocomplete_attribute = ""; form.fields.push_back(field); - std::unique_ptr<TestFormStructure> form_structure = - std::make_unique<TestFormStructure>(form); - TestFormStructure* form_structure_ptr = form_structure.get(); + std::unique_ptr<FormStructure> form_structure = + std::make_unique<FormStructure>(form); + FormStructure* form_structure_ptr = form_structure.get(); form_structure->DetermineHeuristicTypes(nullptr, nullptr); ASSERT_TRUE( autofill_manager() @@ -3100,8 +2821,7 @@ TEST_F(AutofillMetricsTest, UpiVirtualPaymentAddress) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.UserHappiness", AutofillMetrics::USER_DID_ENTER_UPI_VPA, 1); @@ -3170,8 +2890,7 @@ TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); for (const std::string source : {"Heuristic", "Server", "Overall"}) { std::string aggregate_histogram = @@ -3238,14 +2957,10 @@ TEST_F(AutofillMetricsTest, StoredProfileCountAutofillableFormSubmission) { test::CreateTestFormField("Phone", "phone", "", "text", &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); - // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SeeForm(form); + SubmitForm(form); // An autofillable form was submitted, and the number of stored profiles is // logged. @@ -3273,14 +2988,10 @@ TEST_F(AutofillMetricsTest, StoredProfileCountNonAutofillableFormSubmission) { test::CreateTestFormField("Email", "email", "", "text", &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); - // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SeeForm(form); + SubmitForm(form); // A non-autofillable form was submitted, and number of stored profiles is NOT // logged. @@ -3330,12 +3041,10 @@ TEST_F(AutofillMetricsTest, TypeOfEditedAutofilledFieldsUkmLogging) { base::HistogramTester histogram_tester; // Simulate text input in the first and second fields. - autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[0]); // Simulate form submission. - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); ExpectedUkmMetricsRecord name_field_ukm_record{ {UkmEditedAutofilledFieldAtSubmission::kFieldSignatureName, Collapse(CalculateFieldSignatureForField(form.fields[0])).value()}, @@ -3387,14 +3096,11 @@ TEST_F(AutofillMetricsTest, TypeOfEditedAutofilledFieldsUmaLogging) { base::HistogramTester histogram_tester; // Simulate text input in the first and second fields. - autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(), - TimeTicks()); - autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[0]); + ChangeTextField(form, form.fields[1]); // Simulate form submission. - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // The |NAME_FULL| field was edited (bucket 112). histogram_tester.ExpectBucketCount( @@ -3467,14 +3173,11 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields) { base::HistogramTester histogram_tester; // Simulate text input in the first and second fields. - autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(), - TimeTicks()); - autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[0]); + ChangeTextField(form, form.fields[1]); // Simulate form submission. - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // An autofillable form was submitted, and the number of edited autofilled // fields is logged. @@ -3522,8 +3225,7 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields_NoSubmission) { base::HistogramTester histogram_tester; // Simulate text input in the first field. - autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[0]); // We expect metrics to be logged when the manager is reset. autofill_manager().Reset(); @@ -3551,27 +3253,23 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) { test::CreateTestFormField("Email", "email", "", "text", &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); - // Ensure no metrics are logged when small form support is disabled (min // number of fields enforced). { base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); autofill_manager().Reset(); histogram_tester.ExpectTotalCount("Autofill.DeveloperEngagement", 0); } // Add another field to the form, so that it becomes fillable. test::CreateTestFormField("Phone", "phone", "", "text", &field); - forms.back().fields.push_back(field); + form.fields.push_back(field); // Expect the "form parsed without hints" metric to be logged. { base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); autofill_manager().Reset(); histogram_tester.ExpectUniqueSample( "Autofill.DeveloperEngagement", @@ -3585,19 +3283,18 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) { // local heuristics to detect field types in the rest of the form. test::CreateTestFormField("", "", "", "text", &field); field.autocomplete_attribute = "given-name"; - forms.back().fields.push_back(field); + form.fields.push_back(field); test::CreateTestFormField("", "", "", "text", &field); field.autocomplete_attribute = "email"; - forms.back().fields.push_back(field); + form.fields.push_back(field); test::CreateTestFormField("", "", "", "text", &field); field.autocomplete_attribute = "address-line1"; - forms.back().fields.push_back(field); + form.fields.push_back(field); // Expect the "form parsed with field type hints" metric to be logged. { base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); autofill_manager().Reset(); histogram_tester.ExpectBucketCount( "Autofill.DeveloperEngagement", @@ -3611,14 +3308,13 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) { // Add a field with an author-specified UPI-VPA field type in the form. test::CreateTestFormField("", "", "", "text", &field); field.autocomplete_attribute = "upi-vpa"; - forms.back().fields.push_back(field); + form.fields.push_back(field); // Expect the "form parsed with type hints" metric, and the // "author-specified upi-vpa type" metric to be logged. { base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); autofill_manager().Reset(); histogram_tester.ExpectBucketCount( "Autofill.DeveloperEngagement", @@ -3648,12 +3344,9 @@ TEST_F(AutofillMetricsTest, test::CreateTestFormField("Email", "email", "", "text", &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); - // Ensure no entries are logged when loading a non-fillable form. { - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); autofill_manager().Reset(); EXPECT_EQ(0ul, test_ukm_recorder_->entries_count()); @@ -3661,17 +3354,16 @@ TEST_F(AutofillMetricsTest, // Add another field to the form, so that it becomes fillable. test::CreateTestFormField("Phone", "phone", "", "text", &field); - forms.back().fields.push_back(field); + form.fields.push_back(field); // Expect the "form parsed without field type hints" metric and the // "form loaded" form interaction event to be logged. { - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); autofill_manager().Reset(); VerifyDeveloperEngagementUkm( - test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false, + test_ukm_recorder_, form, /*is_for_credit_card=*/false, {FormType::kAddressForm}, {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS}); } @@ -3695,11 +3387,9 @@ TEST_F(AutofillMetricsTest, test::CreateTestFormField("Email", "email", "", "text", &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); - // Add another field to the form, so that it becomes fillable. test::CreateTestFormField("Phone", "phone", "", "text", &field); - forms.back().fields.push_back(field); + form.fields.push_back(field); // Add some fields with an author-specified field type to the form. // We need to add at least three fields, because a form must have at least @@ -3708,23 +3398,22 @@ TEST_F(AutofillMetricsTest, // local heuristics to detect field types in the rest of the form. test::CreateTestFormField("", "", "", "text", &field); field.autocomplete_attribute = "given-name"; - forms.back().fields.push_back(field); + form.fields.push_back(field); test::CreateTestFormField("", "", "", "text", &field); field.autocomplete_attribute = "email"; - forms.back().fields.push_back(field); + form.fields.push_back(field); test::CreateTestFormField("", "", "", "text", &field); field.autocomplete_attribute = "address-line1"; - forms.back().fields.push_back(field); + form.fields.push_back(field); // Expect the "form parsed without field type hints" metric and the // "form loaded" form interaction event to be logged. { - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); autofill_manager().Reset(); VerifyDeveloperEngagementUkm( - test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false, + test_ukm_recorder_, form, /*is_for_credit_card=*/false, {FormType::kAddressForm}, {AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS}); } @@ -3753,15 +3442,12 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) { field.autocomplete_attribute = "address-line1"; form.fields.push_back(field); - std::vector<FormData> forms(1, form); - { SCOPED_TRACE("VPA and other autocomplete hint present"); - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); VerifyDeveloperEngagementUkm( - test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false, + test_ukm_recorder_, form, /*is_for_credit_card=*/false, /* UPI VPA has Unknown form type.*/ {FormType::kAddressForm, FormType::kUnknownFormType}, {AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, @@ -4149,12 +3835,12 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { test::CreateTestFormField("Name on card", "cc-name", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NAME_FULL); - test::CreateTestFormField("Credit card", "card", "", "text", &field); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); - test::CreateTestFormField("Month", "card_month", "", "text", &field); + test::CreateTestFormField("Expiration date", "expdate", "", "text", &field); form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_EXP_MONTH); + field_types.push_back(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); // Simulate having seen this form on page load. // |form_structure| will be owned by `autofill_manager()`. @@ -4261,8 +3947,7 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { { base::UserActionTester user_action_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); EXPECT_EQ(1, user_action_tester.GetActionCount("Autofill_OnWillSubmitForm")); EXPECT_EQ(1, user_action_tester.GetActionCount( @@ -4338,13 +4023,10 @@ TEST_F(AutofillMetricsTest, UpiVpaUkmTest) { &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); - { - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); - VerifySubmitFormUkm(test_ukm_recorder_, forms.back(), + VerifySubmitFormUkm(test_ukm_recorder_, form, AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, /*is_for_credit_card=*/false, /* has_upi_vpa_field */ true, @@ -4440,8 +4122,7 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) { { base::UserActionTester user_action_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); EXPECT_EQ(1, user_action_tester.GetActionCount("Autofill_OnWillSubmitForm")); EXPECT_EQ(1, user_action_tester.GetActionCount( @@ -4516,12 +4197,12 @@ TEST_F(AutofillMetricsTest, PolledCreditCardSuggestions_DebounceLogs) { test::CreateTestFormField("Name on card", "cc-name", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NAME_FULL); - test::CreateTestFormField("Credit card", "card", "", "text", &field); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); - test::CreateTestFormField("Month", "card_month", "", "text", &field); + test::CreateTestFormField("Expiration date", "expdate", "", "text", &field); form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_EXP_MONTH); + field_types.push_back(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); // Simulate having seen this form on page load. // |form_structure| will be owned by `autofill_manager()`. @@ -4576,7 +4257,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -4705,12 +4386,8 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardParsedFormEvents) { field_types.push_back(CREDIT_CARD_VERIFICATION_CODE); // Simulate seeing and parsing the form. - std::vector<FormData> forms; - forms.push_back(form); - base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FormEvents.CreditCard.WithNoData", FORM_EVENT_DID_PARSE_FORM, 1); @@ -4735,7 +4412,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardInteractedFormEvents) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -4790,7 +4467,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardPopupSuppressedFormEvents) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -4850,7 +4527,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardShownFormEvents) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -4932,7 +4609,7 @@ TEST_P(AutofillMetricsIFrameTest, VirtualCreditCardShownFormEvents) { test::CreateTestFormField("CVC", "cvc", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_VERIFICATION_CODE); - test::CreateTestFormField("Credit card", "card", "", "text", &field); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -5101,7 +4778,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSelectedFormEvents) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -5248,7 +4925,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -5318,8 +4995,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) { autofill_manager().suggestion_generator()->MakeFrontendId( guid, std::string())); OnCreditCardFetchingSuccessful(u"6011000990139424"); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1); @@ -5500,7 +5176,7 @@ TEST_F(AutofillMetricsTest, CreditCardUnmaskingPreflightCall) { FormData form; FormFieldData field; std::vector<ServerFieldType> field_types; - test::CreateTestFormField("Credit card", "card", "", "text", &field); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -5636,7 +5312,7 @@ TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration_ServerCard) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -5839,7 +5515,7 @@ TEST_F(AutofillMetricsTest, 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -5851,8 +5527,7 @@ TEST_F(AutofillMetricsTest, base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_NO_CARD, 1); @@ -5882,7 +5557,8 @@ TEST_P(AutofillMetricsIFrameTest, 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", "411111111", "text", &field); + test::CreateTestFormField("Credit card", "cardnum", "411111111", "text", + &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -5894,8 +5570,7 @@ TEST_P(AutofillMetricsIFrameTest, base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_WRONG_SIZE_CARD, 1); @@ -5928,8 +5603,8 @@ TEST_P(AutofillMetricsIFrameTest, 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", "4444444444444444", "text", - &field); + test::CreateTestFormField("Credit card", "cardnum", "4444444444444444", + "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -5941,8 +5616,7 @@ TEST_P(AutofillMetricsIFrameTest, base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_FAIL_LUHN_CHECK_CARD, 1); @@ -5976,8 +5650,8 @@ TEST_P(AutofillMetricsIFrameTest, 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", "5105105105105100", "text", - &field); + test::CreateTestFormField("Credit card", "cardnum", "5105105105105100", + "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -5989,8 +5663,7 @@ TEST_P(AutofillMetricsIFrameTest, base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, 1); @@ -6024,8 +5697,8 @@ TEST_P(AutofillMetricsIFrameTest, 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", "4111111111111111", "text", - &field); + test::CreateTestFormField("Credit card", "cardnum", "4111111111111111", + "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -6037,8 +5710,7 @@ TEST_P(AutofillMetricsIFrameTest, base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 1); @@ -6072,8 +5744,8 @@ TEST_P(AutofillMetricsIFrameTest, 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", "4111111111111111", "text", - &field); + test::CreateTestFormField("Credit card", "cardnum", "4111111111111111", + "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -6091,8 +5763,7 @@ TEST_P(AutofillMetricsIFrameTest, autofill_manager().suggestion_generator()->MakeFrontendId(guid, std::string())); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 0); @@ -6145,8 +5816,7 @@ TEST_F(AutofillMetricsTest, ShouldNotLogFormEventNoCardForAddressForm) { base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_NO_CARD, 0); @@ -6176,7 +5846,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -6188,8 +5858,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { // Simulating submission with no filled data. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", @@ -6221,8 +5890,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1); @@ -6266,8 +5934,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // Trigger UploadFormDataAsyncCallback. autofill_manager().Reset(); histogram_tester.ExpectBucketCount( @@ -6315,8 +5982,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(), autofill_manager().suggestion_generator()->MakeFrontendId( guid, std::string())); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -6363,8 +6029,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { form.fields.front()); OnCreditCardFetchingSuccessful(u"6011000990139424", /*is_virtual_card=*/true); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_VIRTUAL_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -6410,8 +6075,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(), autofill_manager().suggestion_generator()->MakeFrontendId( guid, std::string())); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -6457,8 +6121,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { autofill_manager().suggestion_generator()->MakeFrontendId( guid, std::string())); OnCreditCardFetchingSuccessful(u"6011000990139424"); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1); @@ -6507,8 +6170,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { // Simulating multiple submissions. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); VerifySubmitFormUkm(test_ukm_recorder_, form, AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, @@ -6516,8 +6178,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { /* has_upi_vpa_field=*/false, {FormType::kCreditCardForm}); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); VerifyUkm( test_ukm_recorder_, form, UkmFormSubmittedType::kEntryName, @@ -6618,8 +6279,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { // interaction. base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); @@ -6725,7 +6385,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -6737,8 +6397,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { // Simulating submission with no filled data. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -6762,8 +6421,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1); @@ -6791,8 +6449,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(), autofill_manager().suggestion_generator()->MakeFrontendId( guid, std::string())); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -6822,8 +6479,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { form.fields.front()); OnCreditCardFetchingSuccessful(u"6011000990139424", /*is_virtual_card=*/true); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_VIRTUAL_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -6852,8 +6508,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(), autofill_manager().suggestion_generator()->MakeFrontendId( guid, std::string())); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -6911,10 +6566,8 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { // Simulating multiple submissions. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -6986,8 +6639,7 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { // interaction. base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); @@ -7068,7 +6720,7 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -7143,8 +6795,7 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) { guid, std::string())); OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, "6011000990139424"); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); EXPECT_THAT( histogram_tester.GetAllSamples( "Autofill.FormEvents.CreditCard.WithOffer"), @@ -7192,8 +6843,7 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) { guid, std::string())); OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, "6011000990139424"); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard.WithOffer", FORM_EVENT_SUGGESTIONS_SHOWN, 1); @@ -7256,8 +6906,7 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) { guid, std::string())); OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, "6011000990139424"); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // Histograms without ".WithOffer" should be recorded. histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard", FORM_EVENT_SUGGESTIONS_SHOWN, 1); @@ -7336,8 +6985,7 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) { // previously filled card info. autofill_manager().OnAskForValuesToFillTest(form, field); autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard.WithOffer", FORM_EVENT_SUGGESTIONS_SHOWN, 2); @@ -7395,8 +7043,7 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) { std::string()); // Submitting the form without the filled suggestion. - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard.WithOffer", FORM_EVENT_SUGGESTIONS_SHOWN, 1); @@ -7461,8 +7108,7 @@ TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) { mojom::RendererFormDataAction::kFill, 0, form, form.fields.back(), autofill_manager().suggestion_generator()->MakeFrontendId( guid, std::string())); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); EXPECT_THAT( histogram_tester.GetAllSamples( "Autofill.FormEvents.CreditCard.WithOffer"), @@ -7523,12 +7169,8 @@ TEST_F(AutofillMetricsTest, MixedParsedFormEvents) { field_types.push_back(CREDIT_CARD_VERIFICATION_CODE); // Simulate seeing and parsing the form. - std::vector<FormData> forms; - forms.push_back(form); - base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address.WithNoData", FORM_EVENT_DID_PARSE_FORM, 1); histogram_tester.ExpectUniqueSample( @@ -7560,12 +7202,8 @@ TEST_F(AutofillMetricsTest, AddressParsedFormEvents) { field_types.push_back(ADDRESS_HOME_STREET_ADDRESS); // Simulate seeing and parsing the form. - std::vector<FormData> forms; - forms.push_back(form); - base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address.WithNoData", FORM_EVENT_DID_PARSE_FORM, 1); @@ -8022,8 +7660,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { // Simulating submission with no filled data. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -8048,8 +7685,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { // triggered. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // Trigger UploadFormDataAsyncCallback. autofill_manager().Reset(); histogram_tester.ExpectBucketCount( @@ -8075,8 +7711,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1); @@ -8099,8 +7734,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(), autofill_manager().suggestion_generator()->MakeFrontendId(std::string(), guid)); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -8117,10 +7751,8 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { // Simulating multiple submissions. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); + SubmitForm(form); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), @@ -8144,8 +7776,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { // interaction. base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), @@ -8203,8 +7834,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { // Simulating submission with no filled data. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -8223,8 +7853,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1); @@ -8247,8 +7876,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(), autofill_manager().suggestion_generator()->MakeFrontendId(std::string(), guid)); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -8266,10 +7894,8 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { // Simulating multiple submissions. base::HistogramTester histogram_tester; autofill_manager().OnAskForValuesToFillTest(form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); + SubmitForm(form); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), BucketsInclude( @@ -8301,8 +7927,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { // interaction. base::HistogramTester histogram_tester; autofill_manager().DidShowSuggestions(true /* is_new_popup */, form, field); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), BucketsInclude( @@ -8368,7 +7993,7 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) { 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); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); @@ -8683,13 +8308,11 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { form.fields.push_back(field); test::CreateTestFormField("Unknown", "unknown", "", "text", &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); // Expect no notifications when the form is first seen. { base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); histogram_tester.ExpectTotalCount("Autofill.FormSubmittedState", 0); VerifyDeveloperEngagementUkm( @@ -8705,8 +8328,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { { base::HistogramTester histogram_tester; base::UserActionTester user_action_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FormSubmittedState", AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1); @@ -8738,13 +8360,11 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { // Non fillable form. form.fields[0].value = u"Unknown Person"; form.fields[1].value = u"unknown.person@gmail.com"; - forms.front() = form; { base::HistogramTester histogram_tester; base::UserActionTester user_action_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FormSubmittedState", AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1); @@ -8777,14 +8397,12 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { form.fields[0].value = u"Elvis Aaron Presley"; form.fields[1].value = u"theking@gmail.com"; form.fields[2].value = u"12345678901"; - forms.front() = form; // Autofilled none with no suggestions shown. { base::HistogramTester histogram_tester; base::UserActionTester user_action_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FormSubmittedState", AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS, @@ -8821,8 +8439,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { { base::HistogramTester histogram_tester; base::UserActionTester user_action_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FormSubmittedState", AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS, 1); @@ -8866,14 +8483,12 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { // Mark one of the fields as autofilled. form.fields[1].is_autofilled = true; - forms.front() = form; // Autofilled some of the fields. { base::HistogramTester histogram_tester; base::UserActionTester user_action_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FormSubmittedState", AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME, 1); @@ -8905,14 +8520,12 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { // Mark all of the fillable fields as autofilled. form.fields[0].is_autofilled = true; form.fields[2].is_autofilled = true; - forms.front() = form; // Autofilled all the fields. { base::HistogramTester histogram_tester; base::UserActionTester user_action_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FormSubmittedState", AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL, 1); @@ -8940,10 +8553,6 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { VerifyUkm(test_ukm_recorder_, form, UkmFieldFillStatusType::kEntryName, expected_field_fill_status_ukm_metrics); } - - // Clear out the third field's value. - form.fields[2].value = std::u16string(); - forms.front() = form; } // Verify that we correctly log the submitted form's state with fields @@ -8970,15 +8579,12 @@ TEST_F( &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); - // Verify if the form is otherwise filled with a field having // |only_fill_when_focused|=true, we consider the form is all filled. { base::HistogramTester histogram_tester; base::UserActionTester user_action_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); VerifyDeveloperEngagementUkm( test_ukm_recorder_, form, /*is_for_credit_card=*/false, {FormType::kAddressForm}, @@ -8992,8 +8598,7 @@ TEST_F( form.fields[2].value = u"12345678901"; form.fields[2].is_autofilled = true; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FormSubmittedState", AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL, 1); @@ -9100,13 +8705,10 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_EmptyForm) { form.action = GURL("http://example.com/submit.html"); form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin()); - std::vector<FormData> forms(1, form); - // Expect a notification when the form is first seen. { base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); histogram_tester.ExpectTotalCount("Autofill.UserHappiness", 0); histogram_tester.ExpectTotalCount("Autofill.UserHappiness.CreditCard", 0); histogram_tester.ExpectTotalCount("Autofill.UserHappiness.Address", 0); @@ -9132,25 +8734,18 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { // Construct a valid credit card form. FormFieldData field; - std::vector<ServerFieldType> field_types; test::CreateTestFormField("Card Number", "card_number", "", "text", &field); form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_NAME_FULL); test::CreateTestFormField("Expiration", "cc_exp", "", "text", &field); form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_EXP_MONTH); test::CreateTestFormField("Verification", "verification", "", "text", &field); form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_VERIFICATION_CODE); - - std::vector<FormData> forms(1, form); // Expect a notification when the form is first seen. { SCOPED_TRACE("First seen"); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness", AutofillMetrics::FORMS_LOADED, 1); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.CreditCard", @@ -9161,8 +8756,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { { SCOPED_TRACE("Initial typing"); base::HistogramTester histogram_tester; - autofill_manager().OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), TimeTicks()); + ChangeTextField(form, form.fields.front()); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness", AutofillMetrics::USER_DID_TYPE, 1); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.CreditCard", @@ -9170,7 +8764,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { } autofill_manager().Reset(); - autofill_manager().AddSeenForm(form, field_types, field_types); + SeeForm(form); // Simulate suggestions shown twice with separate popups. { @@ -9190,7 +8784,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { } autofill_manager().Reset(); - autofill_manager().AddSeenForm(form, field_types, field_types); + SeeForm(form); // Simulate suggestions shown twice for a single edit (i.e. multiple // keystrokes in a single field). @@ -9225,7 +8819,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { { SCOPED_TRACE("Invoke autofill"); base::HistogramTester histogram_tester; - autofill_manager().OnDidFillAutofillFormData(form, TimeTicks()); + FillAutofillFormData(form); histogram_tester.ExpectBucketCount("Autofill.UserHappiness", AutofillMetrics::USER_DID_AUTOFILL, 1); histogram_tester.ExpectBucketCount( @@ -9246,11 +8840,9 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(), autofill_manager().suggestion_generator()->MakeFrontendId( guid, std::string())); - autofill_manager().OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), TimeTicks()); + ChangeTextField(form, form.fields.front()); // Simulate a second keystroke; make sure we don't log the metric twice. - autofill_manager().OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), TimeTicks()); + ChangeTextField(form, form.fields.front()); histogram_tester.ExpectBucketCount( "Autofill.UserHappiness", AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1); @@ -9269,7 +8861,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { { SCOPED_TRACE("Invoke autofill again"); base::HistogramTester histogram_tester; - autofill_manager().OnDidFillAutofillFormData(form, TimeTicks()); + FillAutofillFormData(form); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness", AutofillMetrics::USER_DID_AUTOFILL, 1); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.CreditCard", @@ -9280,8 +8872,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) { { SCOPED_TRACE("Edit another autofilled field"); base::HistogramTester histogram_tester; - autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[1]); histogram_tester.ExpectUniqueSample( "Autofill.UserHappiness", AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1); @@ -9311,13 +8902,10 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { test::CreateTestFormField("Phone", "phone", "", "text", &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); - // Expect a notification when the form is first seen. { base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness", AutofillMetrics::FORMS_LOADED, 1); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.Address", @@ -9327,8 +8915,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { // Simulate typing. { base::HistogramTester histogram_tester; - autofill_manager().OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), TimeTicks()); + ChangeTextField(form, form.fields.front()); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness", AutofillMetrics::USER_DID_TYPE, 1); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.Address", @@ -9352,8 +8939,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { } autofill_manager().Reset(); - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); // Simulate suggestions shown twice for a single edit (i.e. multiple // keystrokes in a single field). { @@ -9384,7 +8970,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { // Simulate invoking autofill. { base::HistogramTester histogram_tester; - autofill_manager().OnDidFillAutofillFormData(form, TimeTicks()); + FillAutofillFormData(form); histogram_tester.ExpectBucketCount("Autofill.UserHappiness", AutofillMetrics::USER_DID_AUTOFILL, 1); histogram_tester.ExpectBucketCount( @@ -9404,11 +8990,9 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { mojom::RendererFormDataAction::kFill, 0, form, form.fields.front(), autofill_manager().suggestion_generator()->MakeFrontendId(std::string(), guid)); - autofill_manager().OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), TimeTicks()); + ChangeTextField(form, form.fields.front()); // Simulate a second keystroke; make sure we don't log the metric twice. - autofill_manager().OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), TimeTicks()); + ChangeTextField(form, form.fields.front()); histogram_tester.ExpectBucketCount( "Autofill.UserHappiness", AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1); @@ -9426,7 +9010,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { // Simulate invoking autofill again. { base::HistogramTester histogram_tester; - autofill_manager().OnDidFillAutofillFormData(form, TimeTicks()); + FillAutofillFormData(form); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness", AutofillMetrics::USER_DID_AUTOFILL, 1); histogram_tester.ExpectUniqueSample("Autofill.UserHappiness.Address", @@ -9436,8 +9020,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { // Simulate editing another autofilled field. { base::HistogramTester histogram_tester; - autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[1]); histogram_tester.ExpectUniqueSample( "Autofill.UserHappiness", AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1); @@ -9573,8 +9156,6 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { test::CreateTestFormField("Phone", "phone", "", "text", &field); form.fields.push_back(field); - const std::vector<FormData> forms(1, form); - // Fill additional form. FormData second_form = form; second_form.host_frame = test::MakeLocalFrameToken(); @@ -9582,9 +9163,8 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { test::CreateTestFormField("Second Phone", "second_phone", "", "text", &field); second_form.fields.push_back(field); - std::vector<FormData> second_forms(1, second_form); - // Fill the field values for form submission. + FormData submitted_form = form; form.fields[0].value = u"Elvis Aaron Presley"; form.fields[1].value = u"theking@gmail.com"; form.fields[2].value = u"12345678901"; @@ -9600,15 +9180,13 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { { SCOPED_TRACE("Test 1"); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(test::WithoutValues(form)); base::TimeTicks parse_time = autofill_manager() .form_structures() .begin() ->second->form_parsed_timestamp(); test_clock.SetNowTicks(parse_time + base::Microseconds(17)); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectTotalCount( "Autofill.FillDuration.FromLoad.WithAutofill", 0); @@ -9626,18 +9204,15 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { { SCOPED_TRACE("Test 2"); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(test::WithoutValues(form)); base::TimeTicks parse_time = autofill_manager() .form_structures() .begin() ->second->form_parsed_timestamp(); - autofill_manager().OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), - parse_time + base::Microseconds(3)); + ChangeTextField(form, form.fields.front(), + parse_time + base::Microseconds(3)); test_clock.SetNowTicks(parse_time + base::Microseconds(17)); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectTotalCount( "Autofill.FillDuration.FromLoad.WithAutofill", 0); @@ -9657,17 +9232,14 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { { SCOPED_TRACE("Test 3"); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(test::WithoutValues(form)); base::TimeTicks parse_time = autofill_manager() .form_structures() .begin() ->second->form_parsed_timestamp(); - autofill_manager().OnDidFillAutofillFormData( - form, parse_time + base::Microseconds(5)); + FillAutofillFormData(form, parse_time + base::Microseconds(5)); test_clock.SetNowTicks(parse_time + base::Microseconds(17)); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FillDuration.FromLoad.WithAutofill", 16, 1); @@ -9689,21 +9261,17 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { SCOPED_TRACE("Test 4"); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(test::WithoutValues(form)); base::TimeTicks parse_time = autofill_manager() .form_structures() .begin() ->second->form_parsed_timestamp(); - autofill_manager().OnDidFillAutofillFormData( - form, parse_time + base::Microseconds(5)); + FillAutofillFormData(form, parse_time + base::Microseconds(5)); - autofill_manager().OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), - parse_time + base::Microseconds(3)); + ChangeTextField(form, form.fields.front(), + parse_time + base::Microseconds(3)); test_clock.SetNowTicks(parse_time + base::Microseconds(17)); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FillDuration.FromLoad.WithAutofill", 16, 1); @@ -9723,22 +9291,17 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { { SCOPED_TRACE("Test 5"); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(test::WithoutValues(form)); base::TimeTicks parse_time = autofill_manager() .form_structures() .begin() ->second->form_parsed_timestamp(); - autofill_manager().OnFormsSeen(/*updated_forms=*/second_forms, - /*removed_forms=*/{}); - autofill_manager().OnDidFillAutofillFormData( - form, parse_time + base::Microseconds(5)); - autofill_manager().OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), - parse_time + base::Microseconds(3)); + SeeForm(test::WithoutValues(second_form)); + FillAutofillFormData(form, parse_time + base::Microseconds(5)); + ChangeTextField(form, form.fields.front(), + parse_time + base::Microseconds(3)); test_clock.SetNowTicks(parse_time + base::Microseconds(17)); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.FillDuration.FromLoad.WithAutofill", 16, 1); @@ -9758,18 +9321,15 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { { SCOPED_TRACE("Test 6"); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().OnFormsSeen(/*updated_forms=*/second_forms, - /*removed_forms=*/{}); + SeeForm(test::WithoutValues(form)); + SeeForm(test::WithoutValues(second_form)); base::TimeTicks parse_time{}; for (const auto& kv : autofill_manager().form_structures()) { if (kv.second->form_parsed_timestamp() > parse_time) parse_time = kv.second->form_parsed_timestamp(); } test_clock.SetNowTicks(parse_time + base::Microseconds(17)); - autofill_manager().OnFormSubmitted(second_form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(second_form); histogram_tester.ExpectTotalCount( "Autofill.FillDuration.FromLoad.WithAutofill", 0); @@ -10026,19 +9586,9 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { test::CreateTestFormField("Organization", "organization", "", "text", &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); - - // Fill second form. FormData second_form = form; - std::vector<FormData> second_forms(1, second_form); - - // Fill a third form. FormData third_form = form; - std::vector<FormData> third_forms(1, third_form); - - // Fill a fourth form. FormData fourth_form = form; - std::vector<FormData> fourth_forms(1, fourth_form); // Fill the field values for the first form submission. form.fields[0].value = u"Albert Canuck"; @@ -10070,10 +9620,8 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log NEW_PROFILE_CREATED for the metric since a new profile is // submitted. - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SeeForm(test::WithoutValues(form)); + SubmitForm(form); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.ProfileActionOnFormSubmitted"), BucketsAre(Bucket(AutofillMetrics::NEW_PROFILE_CREATED, 1), @@ -10082,10 +9630,8 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log EXISTING_PROFILE_USED for the metric since the same profile // is submitted. - autofill_manager().OnFormsSeen(/*updated_forms=*/second_forms, - /*removed_forms=*/{}); - autofill_manager().OnFormSubmitted(second_form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SeeForm(test::WithoutValues(second_form)); + SubmitForm(second_form); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.ProfileActionOnFormSubmitted"), BucketsAre(Bucket(AutofillMetrics::NEW_PROFILE_CREATED, 1), @@ -10094,10 +9640,8 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log NEW_PROFILE_CREATED for the metric since a new profile is // submitted. - autofill_manager().OnFormsSeen(/*updated_forms=*/third_forms, - /*removed_forms=*/{}); - autofill_manager().OnFormSubmitted(third_form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SeeForm(test::WithoutValues(third_form)); + SubmitForm(third_form); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.ProfileActionOnFormSubmitted"), BucketsAre(Bucket(AutofillMetrics::NEW_PROFILE_CREATED, 2), @@ -10106,10 +9650,8 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log EXISTING_PROFILE_UPDATED for the metric since the profile was // updated. - autofill_manager().OnFormsSeen(/*updated_forms=*/fourth_forms, - /*removed_forms=*/{}); - autofill_manager().OnFormSubmitted(fourth_form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SeeForm(test::WithoutValues(fourth_form)); + SubmitForm(fourth_form); EXPECT_THAT( histogram_tester.GetAllSamples("Autofill.ProfileActionOnFormSubmitted"), BucketsAre(Bucket(AutofillMetrics::NEW_PROFILE_CREATED, 2), @@ -10296,12 +9838,15 @@ TEST_F(AutofillMetricsTest, NonsecureCreditCardForm) { test::CreateTestFormField("Name on card", "cc-name", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NAME_FULL); - test::CreateTestFormField("Credit card", "card", "", "text", &field); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); - test::CreateTestFormField("Month", "card_month", "", "text", &field); + test::CreateTestFormField("Month", "cardmonth", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_EXP_MONTH); + test::CreateTestFormField("Expiration date", "expdate", "", "text", &field); + form.fields.push_back(field); + field_types.push_back(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); // Simulate having seen this form on page load. // |form_structure| will be owned by `autofill_manager()`. @@ -10318,8 +9863,7 @@ TEST_F(AutofillMetricsTest, NonsecureCreditCardForm) { // Simulate submitting the credit card form. { base::HistogramTester histograms; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histograms.ExpectBucketCount( "Autofill.FormEvents.CreditCard.OnNonsecurePage", FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); @@ -10357,12 +9901,12 @@ TEST_F(AutofillMetricsTest, test::CreateTestFormField("Name on card", "cc-name", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NAME_FULL); - test::CreateTestFormField("Credit card", "card", "", "text", &field); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); - test::CreateTestFormField("Month", "card_month", "", "text", &field); + test::CreateTestFormField("Expiration date", "expdate", "", "text", &field); form.fields.push_back(field); - field_types.push_back(CREDIT_CARD_EXP_MONTH); + field_types.push_back(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); // Simulate having seen this form on page load. // |form_structure| will be owned by `autofill_manager()`. @@ -10379,8 +9923,7 @@ TEST_F(AutofillMetricsTest, // Simulate submitting the credit card form. { base::HistogramTester histograms; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histograms.ExpectBucketCount("Autofill.FormEvents.CreditCard", FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); histograms.ExpectBucketCount("Autofill.FormEvents.CreditCard", @@ -10482,7 +10025,7 @@ TEST_F(AutofillMetricsTest, DISABLED_AutofillSuggestionShownTest) { test::CreateTestFormField("Name on card", "cc-name", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NAME_FULL); - test::CreateTestFormField("Credit card", "card", "", "text", &field); + test::CreateTestFormField("Credit card", "cardnum", "", "text", &field); form.fields.push_back(field); field_types.push_back(CREDIT_CARD_NUMBER); test::CreateTestFormField("Month", "card_month", "", "text", &field); @@ -10643,15 +10186,12 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel_FromFormEvents) { test::CreateTestFormField("Phone", "phone", "", "text", &field); form.fields.push_back(field); - std::vector<FormData> forms(1, form); - // Simulate seeing the form. { base::HistogramTester histogram_tester; autofill_client_->set_security_level( security_state::SecurityLevel::DANGEROUS); - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); histogram_tester.ExpectBucketCount( "Autofill.UserHappiness.Address.DANGEROUS", AutofillMetrics::FORMS_LOADED, 1); @@ -10934,7 +10474,7 @@ TEST_F(AutofillMetricsTest, FrameHasNoForm) { // Verify that we correctly log metrics if a frame has // autocomplete="one-time-code". TEST_F(AutofillMetricsTest, FrameHasAutocompleteOneTimeCode) { - FormData form; + FormData form; // Form with one time code. form.host_frame = test::MakeLocalFrameToken(); form.unique_renderer_id = test::MakeFormRendererId(); form.name = u"TestForm"; @@ -10943,18 +10483,14 @@ TEST_F(AutofillMetricsTest, FrameHasAutocompleteOneTimeCode) { form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin()); FormFieldData field; - - std::vector<FormData> forms_with_one_time_code(1, form); - test::CreateTestFormField("", "", "", "password", &field); field.autocomplete_attribute = "one-time-code"; - forms_with_one_time_code.back().fields.push_back(field); + form.fields.push_back(field); test::CreateTestFormField("", "", "", "password", &field); - forms_with_one_time_code.back().fields.push_back(field); + form.fields.push_back(field); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen( - /*updated_forms=*/forms_with_one_time_code, /*removed_forms=*/{}); + SeeForm(form); autofill_driver_.reset(); // Verifies that autocomplete="one-time-code" in a form is correctly recorded. histogram_tester.ExpectBucketCount( @@ -10968,7 +10504,7 @@ TEST_F(AutofillMetricsTest, FrameHasAutocompleteOneTimeCode) { // Verify that we correctly log metrics if a frame does not have // autocomplete="one-time-code". TEST_F(AutofillMetricsTest, FrameDoesNotHaveAutocompleteOneTimeCode) { - FormData form; + FormData form; // Form without one time code. form.host_frame = test::MakeLocalFrameToken(); form.unique_renderer_id = test::MakeFormRendererId(); form.name = u"TestForm"; @@ -10977,14 +10513,11 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHaveAutocompleteOneTimeCode) { form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin()); FormFieldData field; - std::vector<FormData> forms_without_one_time_code(1, form); - test::CreateTestFormField("", "", "", "password", &field); - forms_without_one_time_code.back().fields.push_back(field); + form.fields.push_back(field); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen( - /*updated_forms=*/forms_without_one_time_code, /*removed_forms=*/{}); + SeeForm(form); autofill_driver_.reset(); histogram_tester.ExpectBucketCount( "Autofill.WebOTP.OneTimeCode.FromAutocomplete", @@ -10997,7 +10530,7 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHaveAutocompleteOneTimeCode) { // Verify that we correctly log metrics when a phone number field does not have // autocomplete attribute but there are at least 3 fields in the form. TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithoutAutocomplete) { - FormData form; + FormData form; // Form with phone number. form.host_frame = test::MakeLocalFrameToken(); form.unique_renderer_id = test::MakeFormRendererId(); form.name = u"TestForm"; @@ -11005,22 +10538,18 @@ TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithoutAutocomplete) { form.action = GURL("http://example.com/submit.html"); form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin()); - FormFieldData field; - - std::vector<FormData> forms_with_phone_number(1, form); - // At least 3 fields are necessary for FormStructure to compute proper field // types if autocomplete attribute value is not available. + FormFieldData field; test::CreateTestFormField("Phone", "phone", "", "tel", &field); - forms_with_phone_number.back().fields.push_back(field); + form.fields.push_back(field); test::CreateTestFormField("Last Name", "lastname", "", "text", &field); - forms_with_phone_number.back().fields.push_back(field); + form.fields.push_back(field); test::CreateTestFormField("First Name", "firstname", "", "text", &field); - forms_with_phone_number.back().fields.push_back(field); + form.fields.push_back(field); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen( - /*updated_forms=*/forms_with_phone_number, /*removed_forms=*/{}); + SeeForm(form); autofill_driver_.reset(); histogram_tester.ExpectBucketCount( "Autofill.WebOTP.PhoneNumberCollection.ParseResult", @@ -11033,7 +10562,7 @@ TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithoutAutocomplete) { // Verify that we correctly log metrics when a phone number field does not have // autocomplete attribute and there are less than 3 fields in the form. TEST_F(AutofillMetricsTest, FrameHasSinglePhoneNumberFieldWithoutAutocomplete) { - FormData form; + FormData form; // Form with single phone number field. form.host_frame = test::MakeLocalFrameToken(); form.unique_renderer_id = test::MakeFormRendererId(); form.name = u"TestForm"; @@ -11041,18 +10570,14 @@ TEST_F(AutofillMetricsTest, FrameHasSinglePhoneNumberFieldWithoutAutocomplete) { form.action = GURL("http://example.com/submit.html"); form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin()); - FormFieldData field; - std::vector<FormData> forms_with_single_phone_number_field(1, form); - // At least 3 fields are necessary for FormStructure to compute proper field // types if autocomplete attribute value is not available. + FormFieldData field; test::CreateTestFormField("Phone", "phone", "", "tel", &field); - forms_with_single_phone_number_field.back().fields.push_back(field); + form.fields.push_back(field); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen( - /*updated_forms=*/forms_with_single_phone_number_field, - /*removed_forms=*/{}); + SeeForm(form); autofill_driver_.reset(); histogram_tester.ExpectBucketCount( "Autofill.WebOTP.PhoneNumberCollection.ParseResult", @@ -11065,14 +10590,12 @@ TEST_F(AutofillMetricsTest, FrameHasSinglePhoneNumberFieldWithoutAutocomplete) { // Verify that we correctly log metrics when a phone number field has // autocomplete attribute. TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithAutocomplete) { - FormData form; + FormData form; // Form with phone number. CreateSimpleForm(autofill_client_->form_origin(), form); AddAutoCompleteFieldToForm("phone", form); - std::vector<FormData> forms_with_phone_number(1, form); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen( - /*updated_forms=*/forms_with_phone_number, /*removed_forms=*/{}); + SeeForm(form); autofill_driver_.reset(); histogram_tester.ExpectBucketCount( "Autofill.WebOTP.PhoneNumberCollection.ParseResult", @@ -11094,14 +10617,11 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHavePhoneNumberField) { form.main_frame_origin = url::Origin::Create(autofill_client_->form_origin()); FormFieldData field; - std::vector<FormData> forms_without_phone_number(1, form); - test::CreateTestFormField("", "", "", "password", &field); - forms_without_phone_number.back().fields.push_back(field); + form.fields.push_back(field); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen( - /*updated_forms=*/forms_without_phone_number, /*removed_forms=*/{}); + SeeForm(form); autofill_driver_.reset(); histogram_tester.ExpectBucketCount( "Autofill.WebOTP.PhoneNumberCollection.ParseResult", @@ -11114,143 +10634,81 @@ TEST_F(AutofillMetricsTest, FrameDoesNotHavePhoneNumberField) { // ContentAutofillDriver is not visible to TestAutofillDriver on iOS. // In addition, WebOTP will not ship on iOS. #if !BUILDFLAG(IS_IOS) -// Verify that we correctly log PhoneCollectionMetricState::kNone. -TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateNone) { - FormData form; - CreateSimpleForm(autofill_client_->form_origin(), form); - AddAutoCompleteFieldToForm("password", form); - std::vector<FormData> forms(1, form); - base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().ReportAutofillWebOTPMetrics(false); - histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - PhoneCollectionMetricState::kNone, 1); - histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - 1); -} - -// Verify that we correctly log PhoneCollectionMetricState::kOTC. -TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateOTC) { - FormData form; - CreateSimpleForm(autofill_client_->form_origin(), form); - AddAutoCompleteFieldToForm("one-time-code", form); - - std::vector<FormData> forms(1, form); - base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().ReportAutofillWebOTPMetrics(false); - histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - PhoneCollectionMetricState::kOTC, 1); - histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - 1); -} - -// Verify that we correctly log PhoneCollectionMetricState::kWebOTP. -TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateWebOTP) { - // If WebOTP is used, even if there is no form on the page we still need to - // report it. - base::HistogramTester histogram_tester; - autofill_manager().ReportAutofillWebOTPMetrics(true); - histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - PhoneCollectionMetricState::kWebOTP, 1); - histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - 1); -} - -// Verify that we correctly log PhoneCollectionMetricState::kWebOTPPlusOTC. -TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateWebOTPPlusOTC) { - FormData form; - CreateSimpleForm(autofill_client_->form_origin(), form); - AddAutoCompleteFieldToForm("one-time-code", form); - - std::vector<FormData> forms(1, form); - base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().ReportAutofillWebOTPMetrics(true); - histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - PhoneCollectionMetricState::kWebOTPPlusOTC, - 1); - histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - 1); -} - -// Verify that we correctly log PhoneCollectionMetricState::kPhone. -TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhone) { - FormData form; - CreateSimpleForm(autofill_client_->form_origin(), form); - AddAutoCompleteFieldToForm("tel", form); - - std::vector<FormData> forms(1, form); - base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().ReportAutofillWebOTPMetrics(false); - histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - PhoneCollectionMetricState::kPhone, 1); - histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - 1); -} +struct WebOTPPhoneCollectionMetricsTestCase { + std::vector<std::string> autocomplete_field; + PhoneCollectionMetricState phone_collection_metric_state; + bool report_autofill_web_otp_metrics = false; +}; -// Verify that we correctly log PhoneCollectionMetricState::kPhonePlusOTC. -TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhonePlusOTC) { - FormData form; - CreateSimpleForm(autofill_client_->form_origin(), form); - AddAutoCompleteFieldToForm("tel", form); - AddAutoCompleteFieldToForm("one-time-code", form); +class WebOTPPhoneCollectionMetricsTest + : public AutofillMetricsTest, + public ::testing::WithParamInterface< + WebOTPPhoneCollectionMetricsTestCase> {}; - std::vector<FormData> forms(1, form); - base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().ReportAutofillWebOTPMetrics(false); - histogram_tester.ExpectBucketCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - PhoneCollectionMetricState::kPhonePlusOTC, - 1); - histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - 1); -} +INSTANTIATE_TEST_SUITE_P( + WebOTPPhoneCollectionMetricsTest, + WebOTPPhoneCollectionMetricsTest, + testing::Values( + // Verify that we correctly log PhoneCollectionMetricState::kNone. + WebOTPPhoneCollectionMetricsTestCase{{"password"}, + PhoneCollectionMetricState::kNone}, + // Verify that we correctly log PhoneCollectionMetricState::kOTC. + WebOTPPhoneCollectionMetricsTestCase{{"one-time-code"}, + PhoneCollectionMetricState::kOTC}, + // Verify that we correctly log PhoneCollectionMetricState::kWebOTP. + WebOTPPhoneCollectionMetricsTestCase{ + {}, + PhoneCollectionMetricState::kWebOTP, + true}, + // Verify that we correctly log + // PhoneCollectionMetricState::kWebOTPPlusOTC. + WebOTPPhoneCollectionMetricsTestCase{ + {"one-time-code"}, + PhoneCollectionMetricState::kWebOTPPlusOTC, + true}, + // Verify that we correctly log PhoneCollectionMetricState::kPhone. + WebOTPPhoneCollectionMetricsTestCase{ + {"tel"}, + PhoneCollectionMetricState::kPhone}, + // Verify that we correctly log + // PhoneCollectionMetricState::kPhonePlusOTC. + WebOTPPhoneCollectionMetricsTestCase{ + {"tel", "one-time-code"}, + PhoneCollectionMetricState::kPhonePlusOTC}, + // Verify that we correctly log + // PhoneCollectionMetricState::kPhonePlusWebOTP. + WebOTPPhoneCollectionMetricsTestCase{ + {"tel"}, + PhoneCollectionMetricState::kPhonePlusWebOTP, + true}, + // Verify that we correctly log + // PhoneCollectionMetricState::kPhonePlusWebOTPPlusOTC. + WebOTPPhoneCollectionMetricsTestCase{ + {"tel", "one-time-code"}, + PhoneCollectionMetricState::kPhonePlusWebOTPPlusOTC, + true})); + +TEST_P(WebOTPPhoneCollectionMetricsTest, + TestWebOTPPhoneCollectionMetricsState) { + auto test_case = GetParam(); + + if (!test_case.autocomplete_field.empty()) { + FormData form; + CreateSimpleForm(autofill_client_->form_origin(), form); + for (const auto& autocomplete : test_case.autocomplete_field) + AddAutoCompleteFieldToForm(autocomplete, form); -// Verify that we correctly log PhoneCollectionMetricState::kPhonePlusWebOTP. -TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStatePhonePlusWebOTP) { - FormData form; - CreateSimpleForm(autofill_client_->form_origin(), form); - AddAutoCompleteFieldToForm("tel", form); + SeeForm(form); + } - std::vector<FormData> forms(1, form); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().ReportAutofillWebOTPMetrics(true); - histogram_tester.ExpectBucketCount( - "Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - PhoneCollectionMetricState::kPhonePlusWebOTP, 1); - histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - 1); -} + autofill_manager().ReportAutofillWebOTPMetrics( + test_case.report_autofill_web_otp_metrics); -// Verify that we correctly log -// PhoneCollectionMetricState::kPhonePlusWebOTPPlusOTC. -TEST_F(AutofillMetricsTest, - WebOTPPhoneCollectionMetricsStatePhonePlusWebOTPPlusOTC) { - FormData form; - CreateSimpleForm(autofill_client_->form_origin(), form); - AddAutoCompleteFieldToForm("tel", form); - AddAutoCompleteFieldToForm("one-time-code", form); - - std::vector<FormData> forms(1, form); - base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); - autofill_manager().ReportAutofillWebOTPMetrics(true); - histogram_tester.ExpectBucketCount( - "Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - PhoneCollectionMetricState::kPhonePlusWebOTPPlusOTC, 1); - histogram_tester.ExpectTotalCount("Autofill.WebOTP.PhonePlusWebOTPPlusOTC", - 1); + EXPECT_THAT( + histogram_tester.GetAllSamples("Autofill.WebOTP.PhonePlusWebOTPPlusOTC"), + BucketsAre(Bucket(test_case.phone_collection_metric_state, 1))); } // Verify that proper PhoneCollectionMetricsState is logged to UKM. @@ -11266,10 +10724,8 @@ TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateLoggedToUKM) { // Document uses OntTimeCode AddAutoCompleteFieldToForm("one-time-code", form); - std::vector<FormData> forms(1, form); base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); autofill_manager().ReportAutofillWebOTPMetrics(true); entries = test_ukm_recorder_->GetEntriesByName( @@ -11300,20 +10756,17 @@ TEST_F(AutofillMetricsTest, AutocompleteOneTimeCodeFormFilledDuration) { field.autocomplete_attribute = "one-time-code"; form.fields.push_back(field); - std::vector<FormData> forms(1, form); form.fields[0].value = u"123456"; { base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); base::TimeTicks parse_time = autofill_manager() .form_structures() .begin() ->second->form_parsed_timestamp(); test_clock.SetNowTicks(parse_time + base::Microseconds(17)); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectTotalCount( "Autofill.WebOTP.OneTimeCode.FillDuration.FromLoad", 1); @@ -11324,20 +10777,16 @@ TEST_F(AutofillMetricsTest, AutocompleteOneTimeCodeFormFilledDuration) { { base::HistogramTester histogram_tester; - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); base::TimeTicks parse_time = autofill_manager() .form_structures() .begin() ->second->form_parsed_timestamp(); - autofill_manager().OnDidFillAutofillFormData( - form, parse_time + base::Microseconds(5)); - autofill_manager().OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), - parse_time + base::Microseconds(3)); + FillAutofillFormData(form, parse_time + base::Microseconds(5)); + ChangeTextField(form, form.fields.front(), + parse_time + base::Microseconds(3)); test_clock.SetNowTicks(parse_time + base::Microseconds(17)); - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.WebOTP.OneTimeCode.FillDuration.FromInteraction", 14, 1); @@ -11450,9 +10899,7 @@ TEST_F(AutofillMetricsTest, LogNumberOfAutocompleteEntriesCleanedUp) { TEST_F(AutofillMetricsTest, FormEventMetrics_BySyncState) { FormData form; FormStructure form_structure(form); - std::vector<FormData> forms(1, form); - autofill_manager().OnFormsSeen(/*updated_forms=*/forms, - /*removed_forms=*/{}); + SeeForm(form); autofill_manager().Reset(); { @@ -11574,8 +11021,7 @@ TEST_P(AutofillMetricsFunnelTest, LogFunnelMetrics) { const bool user_submitted_form = GetParam() >= 4; // Simulate that the autofill manager has seen this form on page load. - autofill_manager().OnFormsSeen(/*updated_forms=*/{form}, - /*removed_forms=*/{}); + SeeForm(form); if (!user_saw_suggestion) { // Remove the profile to prevent suggestion from being shown. @@ -11604,8 +11050,7 @@ TEST_P(AutofillMetricsFunnelTest, LogFunnelMetrics) { // Simulate form submission. if (user_submitted_form) { - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); } ResetDriverToCommitMetrics(); @@ -11661,6 +11106,12 @@ TEST_P(AutofillMetricsFunnelTest, LogFunnelMetrics) { "Autofill.Autocomplete.NotOff.FillingAcceptance.Address", 1, 1); histogram_tester.ExpectTotalCount( "Autofill.Autocomplete.Off.FillingAcceptance.Address", 0); + VerifyUkm(test_ukm_recorder_, form, UkmAutofillKeyMetricsType::kEntryName, + {{{UkmAutofillKeyMetricsType::kFillingReadinessName, 1}, + {UkmAutofillKeyMetricsType::kFillingAcceptanceName, 1}, + {UkmAutofillKeyMetricsType::kFillingCorrectnessName, 1}, + {UkmAutofillKeyMetricsType::kFillingAssistanceName, 1}, + {UkmAutofillKeyMetricsType::kFormTypesName, 2}}}); } else { histogram_tester.ExpectTotalCount( "Autofill.KeyMetrics.FillingReadiness.Address", 0); @@ -11721,19 +11172,16 @@ TEST_F(AutofillMetricsFunnelTest, AblationState) { base::HistogramTester histogram_tester; // Simulate that the autofill manager has seen this form on page load. - autofill_manager().OnFormsSeen(/*updated_forms=*/{form}, - /*removed_forms=*/{}); + SeeForm(form); // Simulate interacting with the form. autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); // Don't simulate a suggestion but simulate the user typing. - autofill_manager().OnTextFieldDidChange(form, form.fields[0], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[0]); // Simulate form submission. - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); ResetDriverToCommitMetrics(); @@ -11806,13 +11254,11 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogEmptyForm) { base::HistogramTester histogram_tester; // Simulate page load. - autofill_manager().OnFormsSeen(/*updated_forms=*/{form_}, - /*removed_forms=*/{}); + SeeForm(form_); autofill_manager().OnAskForValuesToFillTest(form_, form_.fields[0]); // Simulate form submission. - autofill_manager().OnFormSubmitted(form_, false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form_); ResetDriverToCommitMetrics(); @@ -11826,6 +11272,11 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogEmptyForm) { "Autofill.KeyMetrics.FillingAssistance.Address", 0, 1); histogram_tester.ExpectTotalCount( "Autofill.KeyMetrics.FormSubmission.NotAutofilled.Address", 0); + + VerifyUkm(test_ukm_recorder_, form_, UkmAutofillKeyMetricsType::kEntryName, + {{{UkmAutofillKeyMetricsType::kFillingReadinessName, 1}, + {UkmAutofillKeyMetricsType::kFillingAssistanceName, 0}, + {UkmAutofillKeyMetricsType::kFormTypesName, 2}}}); } // Validate Autofill.KeyMetrics.* in case the user has no address profile on @@ -11835,19 +11286,15 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogNoProfile) { // Simulate that no data is available. personal_data().ClearProfiles(); - autofill_manager().OnFormsSeen(/*updated_forms=*/{form_}, - /*removed_forms=*/{}); + SeeForm(form_); autofill_manager().OnAskForValuesToFillTest(form_, form_.fields[0]); // Simulate user typing the address. - autofill_manager().OnTextFieldDidChange(form_, form_.fields[0], gfx::RectF(), - TimeTicks()); - autofill_manager().OnTextFieldDidChange(form_, form_.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form_, form_.fields[0]); + ChangeTextField(form_, form_.fields[1]); // Simulate form submission. - autofill_manager().OnFormSubmitted(form_, false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form_); ResetDriverToCommitMetrics(); @@ -11861,6 +11308,11 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogNoProfile) { "Autofill.KeyMetrics.FillingAssistance.Address", 0, 1); histogram_tester.ExpectBucketCount( "Autofill.KeyMetrics.FormSubmission.NotAutofilled.Address", 1, 1); + + VerifyUkm(test_ukm_recorder_, form_, UkmAutofillKeyMetricsType::kEntryName, + {{{UkmAutofillKeyMetricsType::kFillingReadinessName, 0}, + {UkmAutofillKeyMetricsType::kFillingAssistanceName, 0}, + {UkmAutofillKeyMetricsType::kFormTypesName, 2}}}); } // Validate Autofill.KeyMetrics.* in case the user does not accept a suggestion. @@ -11868,21 +11320,17 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserDoesNotAcceptSuggestion) { base::HistogramTester histogram_tester; // Simulate that suggestion is shown but user does not accept it. - autofill_manager().OnFormsSeen(/*updated_forms=*/{form_}, - /*removed_forms=*/{}); + SeeForm(form_); autofill_manager().OnAskForValuesToFillTest(form_, form_.fields[0]); autofill_manager().DidShowSuggestions( /*has_autofill_suggestions=*/true, form_, form_.fields[0]); // Simulate user typing the address. - autofill_manager().OnTextFieldDidChange(form_, form_.fields[0], gfx::RectF(), - TimeTicks()); - autofill_manager().OnTextFieldDidChange(form_, form_.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form_, form_.fields[0]); + ChangeTextField(form_, form_.fields[1]); // Simulate form submission. - autofill_manager().OnFormSubmitted(form_, false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form_); ResetDriverToCommitMetrics(); @@ -11896,6 +11344,12 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserDoesNotAcceptSuggestion) { "Autofill.KeyMetrics.FillingAssistance.Address", 0, 1); histogram_tester.ExpectBucketCount( "Autofill.KeyMetrics.FormSubmission.NotAutofilled.Address", 1, 1); + + VerifyUkm(test_ukm_recorder_, form_, UkmAutofillKeyMetricsType::kEntryName, + {{{UkmAutofillKeyMetricsType::kFillingReadinessName, 1}, + {UkmAutofillKeyMetricsType::kFillingAcceptanceName, 0}, + {UkmAutofillKeyMetricsType::kFillingAssistanceName, 0}, + {UkmAutofillKeyMetricsType::kFormTypesName, 2}}}); } // Validate Autofill.KeyMetrics.* in case the user has to fix the filled data. @@ -11903,8 +11357,7 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledData) { base::HistogramTester histogram_tester; // Simulate that suggestion is shown and user accepts it. - autofill_manager().OnFormsSeen(/*updated_forms=*/{form_}, - /*removed_forms=*/{}); + SeeForm(form_); autofill_manager().OnAskForValuesToFillTest(form_, form_.fields[0]); autofill_manager().DidShowSuggestions( /*has_autofill_suggestions=*/true, form_, form_.fields[0]); @@ -11914,12 +11367,10 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledData) { kTestGuid)); // Simulate user fixing the address. - autofill_manager().OnTextFieldDidChange(form_, form_.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form_, form_.fields[1]); // Simulate form submission. - autofill_manager().OnFormSubmitted(form_, false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form_); ResetDriverToCommitMetrics(); @@ -11933,6 +11384,13 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledData) { "Autofill.KeyMetrics.FillingAssistance.Address", 1, 1); histogram_tester.ExpectBucketCount( "Autofill.KeyMetrics.FormSubmission.Autofilled.Address", 1, 1); + + VerifyUkm(test_ukm_recorder_, form_, UkmAutofillKeyMetricsType::kEntryName, + {{{UkmAutofillKeyMetricsType::kFillingReadinessName, 1}, + {UkmAutofillKeyMetricsType::kFillingAcceptanceName, 1}, + {UkmAutofillKeyMetricsType::kFillingCorrectnessName, 0}, + {UkmAutofillKeyMetricsType::kFillingAssistanceName, 1}, + {UkmAutofillKeyMetricsType::kFormTypesName, 2}}}); } // Validate Autofill.KeyMetrics.* in case the user fixes the filled data but @@ -11941,8 +11399,7 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledDataButDoesNotSubmit) { base::HistogramTester histogram_tester; // Simulate that suggestion is shown and user accepts it. - autofill_manager().OnFormsSeen(/*updated_forms=*/{form_}, - /*removed_forms=*/{}); + SeeForm(form_); autofill_manager().OnAskForValuesToFillTest(form_, form_.fields[0]); autofill_manager().DidShowSuggestions( /*has_autofill_suggestions=*/true, form_, form_.fields[0]); @@ -11952,8 +11409,7 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledDataButDoesNotSubmit) { kTestGuid)); // Simulate user fixing the address. - autofill_manager().OnTextFieldDidChange(form_, form_.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form_, form_.fields[1]); // Don't submit form. @@ -11969,6 +11425,13 @@ TEST_F(AutofillMetricsKeyMetricsTest, LogUserFixesFilledDataButDoesNotSubmit) { "Autofill.KeyMetrics.FillingAssistance.Address", 0); histogram_tester.ExpectBucketCount( "Autofill.KeyMetrics.FormSubmission.Autofilled.Address", 0, 1); + + VerifyUkm(test_ukm_recorder_, form_, UkmAutofillKeyMetricsType::kEntryName, + {{{UkmAutofillKeyMetricsType::kFillingReadinessName, 0}, + {UkmAutofillKeyMetricsType::kFillingAcceptanceName, 0}, + {UkmAutofillKeyMetricsType::kFillingCorrectnessName, 0}, + {UkmAutofillKeyMetricsType::kFillingAssistanceName, 0}, + {UkmAutofillKeyMetricsType::kFormTypesName, 2}}}); } TEST_F(AutofillMetricsTest, GetFieldTypeUserEditStatusMetric) { @@ -12000,8 +11463,7 @@ TEST_F(AutofillMetricsTest, PageLanguageMetricsExpectedCase) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.ParsedFieldTypesUsingTranslatedPageLanguage", language_code, 1); @@ -12024,8 +11486,7 @@ TEST_F(AutofillMetricsTest, PageLanguageMetricsInvalidLanguage) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.ParsedFieldTypesUsingTranslatedPageLanguage", 0, 1); @@ -12068,8 +11529,7 @@ TEST_F(AutofillMetricsTest, AutofilledStateFieldSource) { // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectUniqueSample( "Autofill.AutofilledFieldAtSubmission.ByStateSelectionField", @@ -12080,22 +11540,22 @@ TEST_F(AutofillMetricsTest, AutofilledStateFieldSource) { // Tests the following 4 cases when |kAutofillPreventOverridingPrefilledValues| // is enabled: -// 1. The field is not autofilled since it has a prefilled value but the value +// 1. The field is not autofilled since it has an initial value but the value // is edited before the form submission and is same as the value that was // to be autofilled in the field. -// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue| +// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2| // should emit true for this case. -// 2. The field is not autofilled since it has a prefilled value but the value +// 2. The field is not autofilled since it has an initial value but the value // is edited before the form submission and is different than the value that // was to be autofilled in the field. -// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue| +// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2| // should emit false for this case. -// 3. The field had a prefilled value that was similar to the value to be +// 3. The field had an initial value that was similar to the value to be // autofilled in the field. -// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue| +// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2| // should not record anything in this case. // 4. Selection fields are always overridden by Autofill. -// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue| +// |Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2| // should not record anything in this case. TEST_F(AutofillMetricsTest, IsValueNotAutofilledOverExistingValueSameAsSubmittedValue) { @@ -12106,20 +11566,23 @@ TEST_F(AutofillMetricsTest, FormData form = test::GetFormData( {.description_for_logging = "AutofilledStateFieldSource", - .fields = {{.role = ServerFieldType::NAME_FULL}, - {.role = ServerFieldType::ADDRESS_HOME_CITY, - .value = u"Sacremento"}, // Case #1 - {.role = ServerFieldType::ADDRESS_HOME_STATE, - .value = u"CA", - .form_control_type = "select-one", - .select_options = {{u"TN", u"Tennesse"}, - {u"CA", u"California"}, - {u"WA", u"Washington DC"}}}, // Case #4 - {.role = ServerFieldType::ADDRESS_HOME_ZIP, - .value = u"00000"}, // Case #2 - {.role = ServerFieldType::PHONE_HOME_WHOLE_NUMBER, - .value = u"12345678901"}, // Case #3 - {.role = ServerFieldType::ADDRESS_HOME_COUNTRY}}}); + .fields = { + {.role = ServerFieldType::NAME_FULL}, + {.role = ServerFieldType::ADDRESS_HOME_CITY, + .value = u"Sacremento", + .properties_mask = FieldPropertiesFlags::kUserTyped}, // Case #1 + {.role = ServerFieldType::ADDRESS_HOME_STATE, + .value = u"CA", + .form_control_type = "select-one", + .select_options = {{u"TN", u"Tennesse"}, + {u"CA", u"California"}, + {u"WA", u"Washington DC"}}}, // Case #4 + {.role = ServerFieldType::ADDRESS_HOME_ZIP, + .value = u"00000", + .properties_mask = FieldPropertiesFlags::kUserTyped}, // Case #2 + {.role = ServerFieldType::PHONE_HOME_WHOLE_NUMBER, + .value = u"12345678901"}, // Case #3 + {.role = ServerFieldType::ADDRESS_HOME_COUNTRY}}}); std::vector<ServerFieldType> heuristic_types = { NAME_FULL, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, @@ -12129,7 +11592,8 @@ TEST_F(AutofillMetricsTest, ADDRESS_HOME_ZIP, PHONE_HOME_WHOLE_NUMBER, ADDRESS_HOME_COUNTRY}; // Simulate having seen this form on page load. - autofill_manager().AddSeenForm(form, heuristic_types, server_types); + autofill_manager().AddSeenForm(form, heuristic_types, server_types, + /*preserve_values_in_form_structure=*/true); autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); autofill_manager().DidShowSuggestions( @@ -12144,25 +11608,22 @@ TEST_F(AutofillMetricsTest, // Case #1: Change submitted value to expected autofilled value for the field. // The histogram should emit true for this. form.fields[1].value = u"Memphis"; - autofill_manager().OnTextFieldDidChange(form, form.fields[1], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[1]); // Case #2: Change submitted value such that it different than expected // autofilled value for the field. The histogram should emit false for this. form.fields[3].value = u"00001"; - autofill_manager().OnTextFieldDidChange(form, form.fields[3], gfx::RectF(), - TimeTicks()); + ChangeTextField(form, form.fields[3]); // Simulate form submission. base::HistogramTester histogram_tester; - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); histogram_tester.ExpectBucketCount( - "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue", + "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2", true, 1); histogram_tester.ExpectBucketCount( - "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue", + "Autofill.IsValueNotAutofilledOverExistingValueSameAsSubmittedValue2", false, 1); } @@ -12178,8 +11639,7 @@ TEST_F(AutofillMetricsTest, FormInteractionsAreCounted) { // WHEN // Simulate manual text field change. const auto field = form.fields[0]; - autofill_manager().OnTextFieldDidChange(form, field, gfx::RectF(), - TimeTicks()); + ChangeTextField(form, field); // Simulate Autocomplete filling twice. autofill_manager().OnSingleFieldSuggestionSelected( u"", POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY); @@ -12192,8 +11652,7 @@ TEST_F(AutofillMetricsTest, FormInteractionsAreCounted) { autofill_manager().suggestion_generator()->MakeFrontendId(std::string(), guid)); // Simulate form submission. - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // THEN VerifySubmitFormUkm( @@ -12215,8 +11674,7 @@ TEST_F(AutofillMetricsTest, FormInteractionsAreInitiallyZero) { // WHEN // Simulate form submission. - autofill_manager().OnFormSubmitted(form, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); + SubmitForm(form); // THEN VerifySubmitFormUkm( @@ -12306,15 +11764,6 @@ class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest { this)); } - void SeeForm() { - std::vector<ServerFieldType> field_types = { - CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER, - CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, CREDIT_CARD_VERIFICATION_CODE}; - autofill_manager().AddSeenForm(form_, field_types, field_types); - autofill_manager().OnFormsSeen(/*updated_forms=*/{form_}, - /*removed_forms=*/{}); - } - CreditCardAndCvc& fill_data() { return credit_card_with_cvc_; } // Any call to FillForm() should be followed by a SetFormValues() call to @@ -12351,11 +11800,6 @@ class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest { } } - void SubmitForm() { - autofill_manager().OnFormSubmitted(form_, /*known_success=*/false, - SubmissionSource::FORM_SUBMISSION); - } - FormFieldData& GetFieldById(FieldGlobalId field) { auto it = base::ranges::find(form_.fields, field, &FormFieldData::global_id); @@ -12415,7 +11859,7 @@ TEST_F(AutofillMetricsSeamlessnessTest, DoNotLogCreditCardSeamlessFillsMetricIfNotAutofilled) { using UkmBuilder = ukm::builders::Autofill_CreditCardFill; base::HistogramTester histogram_tester; - SeeForm(); + SeeForm(form_); // Fake manual fill. SetFormValues( @@ -12426,7 +11870,7 @@ TEST_F(AutofillMetricsSeamlessnessTest, // Fakes an Autofill. // This fills nothing because all fields have been manually filled. FillForm(FormFieldData()); - SubmitForm(); + SubmitForm(form_); ResetDriverToCommitMetrics(); for (auto fill : {kFills, kFillable}) { @@ -12471,7 +11915,7 @@ TEST_F(AutofillMetricsSeamlessnessTest, return histogram_tester.GetAllSamples(metric.str()); }; - SeeForm(); + SeeForm(form_); fill_data().cvc = u""; @@ -12498,7 +11942,7 @@ TEST_F(AutofillMetricsSeamlessnessTest, SetFormValues({CREDIT_CARD_NUMBER}, /*is_autofilled=*/true, /*is_user_typed=*/false); - SubmitForm(); + SubmitForm(form_); ResetDriverToCommitMetrics(); // Bitmask metrics. diff --git a/chromium/components/autofill/core/browser/metrics/form_events/form_event_logger_base.cc b/chromium/components/autofill/core/browser/metrics/form_events/form_event_logger_base.cc index 41272e27bb5..d76b215fc93 100644 --- a/chromium/components/autofill/core/browser/metrics/form_events/form_event_logger_base.cc +++ b/chromium/components/autofill/core/browser/metrics/form_events/form_event_logger_base.cc @@ -154,6 +154,7 @@ void FormEventLoggerBase::OnWillSubmitForm(AutofillSyncSigninState sync_state, if (has_logged_will_submit_) return; has_logged_will_submit_ = true; + submitted_form_types_ = form.GetFormTypes(); LogWillSubmitForm(form); @@ -256,11 +257,11 @@ void FormEventLoggerBase::LogUkmInteractedWithForm( } void FormEventLoggerBase::RecordFunnelAndKeyMetrics() { - LogBuffer funnel_rows; - LogBuffer key_metrics_rows; + LogBuffer funnel_rows(IsLoggingActive(log_manager_)); + LogBuffer key_metrics_rows(IsLoggingActive(log_manager_)); - funnel_rows << Tr{} << "Form Type: " << form_type_name_; - key_metrics_rows << Tr{} << "Form Type: " << form_type_name_; + LOG_AF(funnel_rows) << Tr{} << "Form Type: " << form_type_name_; + LOG_AF(key_metrics_rows) << Tr{} << "Form Type: " << form_type_name_; UmaHistogramBoolean("Autofill.Funnel.ParsedAsType." + form_type_name_, has_parsed_form_); @@ -270,28 +271,29 @@ void FormEventLoggerBase::RecordFunnelAndKeyMetrics() { UmaHistogramBoolean( "Autofill.Funnel.InteractionAfterParsedAsType." + form_type_name_, has_logged_interacted_); - funnel_rows << Tr{} << "InteractionAfterParsedAsType" - << has_logged_interacted_; + LOG_AF(funnel_rows) << Tr{} << "InteractionAfterParsedAsType" + << has_logged_interacted_; if (has_logged_interacted_) { UmaHistogramBoolean( "Autofill.Funnel.SuggestionAfterInteraction." + form_type_name_, has_logged_suggestions_shown_); - funnel_rows << Tr{} << "SuggestionAfterInteraction" - << has_logged_suggestions_shown_; + LOG_AF(funnel_rows) << Tr{} << "SuggestionAfterInteraction" + << has_logged_suggestions_shown_; } if (has_logged_interacted_ && has_logged_suggestions_shown_) { UmaHistogramBoolean( "Autofill.Funnel.FillAfterSuggestion." + form_type_name_, has_logged_suggestion_filled_); - funnel_rows << Tr{} << "FillAfterSuggestion" - << has_logged_suggestion_filled_; + LOG_AF(funnel_rows) << Tr{} << "FillAfterSuggestion" + << has_logged_suggestion_filled_; } if (has_logged_interacted_ && has_logged_suggestions_shown_ && has_logged_suggestion_filled_) { UmaHistogramBoolean( "Autofill.Funnel.SubmissionAfterFill." + form_type_name_, has_logged_will_submit_); - funnel_rows << Tr{} << "SubmissionAfterFill" << has_logged_will_submit_; + LOG_AF(funnel_rows) << Tr{} << "SubmissionAfterFill" + << has_logged_will_submit_; } // Log key success metrics, always preconditioned on a form submission (except // for the Autofill.KeyMetrics.FormSubmission metrics which measure whether @@ -304,16 +306,16 @@ void FormEventLoggerBase::RecordFunnelAndKeyMetrics() { UmaHistogramBoolean( "Autofill.KeyMetrics.FillingReadiness." + form_type_name_, has_logged_data_to_fill_available_); - key_metrics_rows << Tr{} << "FillingReadiness" - << has_logged_data_to_fill_available_; + LOG_AF(key_metrics_rows) + << Tr{} << "FillingReadiness" << has_logged_data_to_fill_available_; if (has_logged_suggestions_shown_) { // Whether a user accepted a filling suggestion they saw for a form that // was later submitted. UmaHistogramBoolean( "Autofill.KeyMetrics.FillingAcceptance." + form_type_name_, has_logged_suggestion_filled_); - key_metrics_rows << Tr{} << "FillingAcceptance" - << has_logged_suggestion_filled_; + LOG_AF(key_metrics_rows) + << Tr{} << "FillingAcceptance" << has_logged_suggestion_filled_; UmaHistogramBoolean( base::StrCat({"Autofill.Autocomplete.", (has_logged_autocomplete_off_ ? "Off" : "NotOff"), @@ -326,15 +328,22 @@ void FormEventLoggerBase::RecordFunnelAndKeyMetrics() { UmaHistogramBoolean( "Autofill.KeyMetrics.FillingCorrectness." + form_type_name_, !has_logged_edited_autofilled_field_); - key_metrics_rows << Tr{} << "FillingCorrectness" - << !has_logged_edited_autofilled_field_; + LOG_AF(key_metrics_rows) << Tr{} << "FillingCorrectness" + << !has_logged_edited_autofilled_field_; } // Whether a submitted form was filled. UmaHistogramBoolean( "Autofill.KeyMetrics.FillingAssistance." + form_type_name_, has_logged_suggestion_filled_); - key_metrics_rows << Tr{} << "FillingAssistance" - << has_logged_suggestion_filled_; + LOG_AF(key_metrics_rows) + << Tr{} << "FillingAssistance" << has_logged_suggestion_filled_; + + if (form_interactions_ukm_logger_) { + form_interactions_ukm_logger_->LogKeyMetrics( + submitted_form_types_, has_logged_data_to_fill_available_, + has_logged_suggestions_shown_, has_logged_edited_autofilled_field_, + has_logged_suggestion_filled_, intent_); + } } if (has_logged_typed_into_non_filled_field_ || has_logged_suggestion_filled_) { @@ -345,20 +354,18 @@ void FormEventLoggerBase::RecordFunnelAndKeyMetrics() { (has_logged_suggestion_filled_ ? "Autofilled." : "NotAutofilled."), form_type_name_}), has_logged_will_submit_); - key_metrics_rows << Tr{} << "FormSubmission.Autofilled" - << has_logged_suggestion_filled_; - key_metrics_rows << Tr{} << "FormSubmission.Submission" - << has_logged_will_submit_; + LOG_AF(key_metrics_rows) + << Tr{} << "FormSubmission.Autofilled" << has_logged_suggestion_filled_; + LOG_AF(key_metrics_rows) + << Tr{} << "FormSubmission.Submission" << has_logged_will_submit_; } - if (log_manager_) { - log_manager_->Log() << LoggingScope::kMetrics << LogMessage::kFunnelMetrics - << Tag{"table"} << std::move(funnel_rows) - << CTag{"table"}; - log_manager_->Log() << LoggingScope::kMetrics << LogMessage::kKeyMetrics - << Tag{"table"} << std::move(key_metrics_rows) - << CTag{"table"}; - } + LOG_AF(log_manager_) << LoggingScope::kMetrics << LogMessage::kFunnelMetrics + << Tag{"table"} << std::move(funnel_rows) + << CTag{"table"}; + LOG_AF(log_manager_) << LoggingScope::kMetrics << LogMessage::kKeyMetrics + << Tag{"table"} << std::move(key_metrics_rows) + << CTag{"table"}; } void FormEventLoggerBase::RecordAblationMetrics() { @@ -409,4 +416,9 @@ void FormEventLoggerBase::RecordAblationMetrics() { } } +void FormEventLoggerBase::SetAutofillAssistantIntentForFilling( + const autofill_assistant::AutofillAssistantIntent intent) { + intent_ = intent; +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/metrics/form_events/form_event_logger_base.h b/chromium/components/autofill/core/browser/metrics/form_events/form_event_logger_base.h index cfd570010e3..fe0903a6657 100644 --- a/chromium/components/autofill/core/browser/metrics/form_events/form_event_logger_base.h +++ b/chromium/components/autofill/core/browser/metrics/form_events/form_event_logger_base.h @@ -16,7 +16,7 @@ #include "components/autofill/core/browser/metrics/form_events/form_events.h" #include "components/autofill/core/browser/sync_utils.h" #include "components/autofill/core/common/form_field_data.h" -#include "third_party/abseil-cpp/absl/types/optional.h" +#include "components/autofill_assistant/core/public/autofill_assistant_intent.h" namespace autofill { @@ -69,6 +69,9 @@ class FormEventLoggerBase { void OnTypedIntoNonFilledField(); void OnEditedAutofilledField(); + void SetAutofillAssistantIntentForFilling( + const autofill_assistant::AutofillAssistantIntent intent); + // See BrowserAutofillManager::SuggestionContext for the definitions of the // AblationGroup parameters. void SetAblationStatus(AblationGroup ablation_group, @@ -139,6 +142,13 @@ class FormEventLoggerBase { // The last field that was polled for suggestions. FormFieldData last_polled_field_; + // The Autofill Assistant intent triggering Autofill, if existing + autofill_assistant::AutofillAssistantIntent intent_ = + autofill_assistant::AutofillAssistantIntent::UNDEFINED_INTENT; + + // Form types of the submitted form + DenseSet<FormType> submitted_form_types_; + // Weak reference. raw_ptr<AutofillMetrics::FormInteractionsUkmLogger> form_interactions_ukm_logger_; diff --git a/chromium/components/autofill/core/browser/metrics/payments/local_card_migration_metrics.cc b/chromium/components/autofill/core/browser/metrics/payments/local_card_migration_metrics.cc new file mode 100644 index 00000000000..f506255fade --- /dev/null +++ b/chromium/components/autofill/core/browser/metrics/payments/local_card_migration_metrics.cc @@ -0,0 +1,114 @@ +// Copyright 2022 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/metrics/payments/local_card_migration_metrics.h" + +#include "base/metrics/histogram_functions.h" +#include "base/metrics/histogram_macros.h" + +namespace autofill::autofill_metrics { + +void LogLocalCardMigrationBubbleOfferMetric( + LocalCardMigrationBubbleOfferMetric metric, + bool is_reshow) { + DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_BUBBLE_OFFER_METRICS); + std::string histogram_name = "Autofill.LocalCardMigrationBubbleOffer."; + histogram_name += is_reshow ? "Reshows" : "FirstShow"; + base::UmaHistogramEnumeration(histogram_name, metric, + NUM_LOCAL_CARD_MIGRATION_BUBBLE_OFFER_METRICS); +} + +void LogLocalCardMigrationBubbleResultMetric( + LocalCardMigrationBubbleResultMetric metric, + bool is_reshow) { + DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_BUBBLE_RESULT_METRICS); + std::string suffix = is_reshow ? ".Reshows" : ".FirstShow"; + base::UmaHistogramEnumeration( + "Autofill.LocalCardMigrationBubbleResult" + suffix, metric, + NUM_LOCAL_CARD_MIGRATION_BUBBLE_RESULT_METRICS); +} + +void LogLocalCardMigrationDecisionMetric( + LocalCardMigrationDecisionMetric metric) { + UMA_HISTOGRAM_ENUMERATION("Autofill.LocalCardMigrationDecision", metric); +} + +void LogLocalCardMigrationDialogOfferMetric( + LocalCardMigrationDialogOfferMetric metric) { + DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_DIALOG_OFFER_METRICS); + std::string histogram_name = "Autofill.LocalCardMigrationDialogOffer"; + base::UmaHistogramEnumeration(histogram_name, metric, + NUM_LOCAL_CARD_MIGRATION_DIALOG_OFFER_METRICS); +} + +void LogLocalCardMigrationDialogUserInteractionMetric( + const base::TimeDelta& duration, + LocalCardMigrationDialogUserInteractionMetric metric) { + DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_DIALOG_USER_INTERACTION_METRICS); + base::UmaHistogramEnumeration( + "Autofill.LocalCardMigrationDialogUserInteraction", metric, + NUM_LOCAL_CARD_MIGRATION_DIALOG_USER_INTERACTION_METRICS); + + // Do not log duration metrics for + // LOCAL_CARD_MIGRATION_DIALOG_DELETE_CARD_ICON_CLICKED, as it can happen + // multiple times in one dialog. + std::string suffix; + switch (metric) { + case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_SAVE_BUTTON_CLICKED: + suffix = "Accepted"; + break; + case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_CANCEL_BUTTON_CLICKED: + suffix = "Denied"; + break; + case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_VIEW_CARDS_BUTTON_CLICKED: + case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_DONE_BUTTON_CLICKED: + suffix = "Closed"; + break; + default: + return; + } + + base::UmaHistogramLongTimes( + "Autofill.LocalCardMigrationDialogActiveDuration." + suffix, duration); +} + +void LogLocalCardMigrationDialogUserSelectionPercentageMetric(int selected, + int total) { + UMA_HISTOGRAM_PERCENTAGE( + "Autofill.LocalCardMigrationDialogUserSelectionPercentage", + 100 * selected / total); +} + +void LogLocalCardMigrationNotOfferedDueToMaxStrikesMetric( + AutofillMetrics::SaveTypeMetric metric) { + UMA_HISTOGRAM_ENUMERATION( + "Autofill.StrikeDatabase.LocalCardMigrationNotOfferedDueToMaxStrikes", + metric); +} + +void LogLocalCardMigrationPromptMetric( + LocalCardMigrationOrigin local_card_migration_origin, + LocalCardMigrationPromptMetric metric) { + DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_PROMPT_METRICS); + std::string histogram_name = "Autofill.LocalCardMigrationOrigin."; + // Switch to different sub-histogram depending on local card migration origin. + switch (local_card_migration_origin) { + case LocalCardMigrationOrigin::UseOfLocalCard: + histogram_name += "UseOfLocalCard"; + break; + case LocalCardMigrationOrigin::UseOfServerCard: + histogram_name += "UseOfServerCard"; + break; + case LocalCardMigrationOrigin::SettingsPage: + histogram_name += "SettingsPage"; + break; + default: + NOTREACHED(); + return; + } + base::UmaHistogramEnumeration(histogram_name, metric, + NUM_LOCAL_CARD_MIGRATION_PROMPT_METRICS); +} + +} // namespace autofill::autofill_metrics diff --git a/chromium/components/autofill/core/browser/metrics/payments/local_card_migration_metrics.h b/chromium/components/autofill/core/browser/metrics/payments/local_card_migration_metrics.h new file mode 100644 index 00000000000..0126c0594dc --- /dev/null +++ b/chromium/components/autofill/core/browser/metrics/payments/local_card_migration_metrics.h @@ -0,0 +1,176 @@ +// Copyright 2022 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_METRICS_PAYMENTS_LOCAL_CARD_MIGRATION_METRICS_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_LOCAL_CARD_MIGRATION_METRICS_H_ + +#include "base/time/time.h" +#include "components/autofill/core/browser/metrics/autofill_metrics.h" + +namespace autofill::autofill_metrics { + +// Metrics to track events when local credit card migration is offered. +enum LocalCardMigrationBubbleOfferMetric { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // The bubble is requested due to a credit card being used or + // local card migration icon in the omnibox being clicked. + LOCAL_CARD_MIGRATION_BUBBLE_REQUESTED = 0, + // The bubble is actually shown to the user. + LOCAL_CARD_MIGRATION_BUBBLE_SHOWN = 1, + NUM_LOCAL_CARD_MIGRATION_BUBBLE_OFFER_METRICS, +}; + +// Metrics to track user action result of the bubble when the bubble is +// closed. +enum LocalCardMigrationBubbleResultMetric { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // The user explicitly accepted the offer. + LOCAL_CARD_MIGRATION_BUBBLE_ACCEPTED = 0, + // The user explicitly closed the bubble with the close button or ESC. + LOCAL_CARD_MIGRATION_BUBBLE_CLOSED = 1, + // The user did not interact with the bubble. + LOCAL_CARD_MIGRATION_BUBBLE_NOT_INTERACTED = 2, + // The bubble lost its focus and was deactivated. + LOCAL_CARD_MIGRATION_BUBBLE_LOST_FOCUS = 3, + // The reason why the prompt is closed is not clear. Possible reason is the + // logging function is invoked before the closed reason is correctly set. + LOCAL_CARD_MIGRATION_BUBBLE_RESULT_UNKNOWN = 4, + NUM_LOCAL_CARD_MIGRATION_BUBBLE_RESULT_METRICS, +}; + +// Metrics to record the decision on whether to offer local card migration. +enum class LocalCardMigrationDecisionMetric { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // All the required conditions are satisfied and main prompt is shown. + OFFERED = 0, + // Migration not offered because user uses new card. + NOT_OFFERED_USE_NEW_CARD = 1, + // Migration not offered because failed migration prerequisites. + NOT_OFFERED_FAILED_PREREQUISITES = 2, + // The Autofill StrikeDatabase decided not to allow offering migration + // because max strike count was reached. + NOT_OFFERED_REACHED_MAX_STRIKE_COUNT = 3, + // Migration not offered because no migratable cards. + NOT_OFFERED_NO_MIGRATABLE_CARDS = 4, + // Met the migration requirements but the request to Payments for upload + // details failed. + NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED = 5, + // Abandoned the migration because no supported local cards were left after + // filtering out unsupported cards. + NOT_OFFERED_NO_SUPPORTED_CARDS = 6, + // User used a local card and they only have a single migratable local card + // on file, we will offer Upstream instead. + NOT_OFFERED_SINGLE_LOCAL_CARD = 7, + // User used an unsupported local card, we will abort the migration. + NOT_OFFERED_USE_UNSUPPORTED_LOCAL_CARD = 8, + // Legal message was invalid, we will abort the migration. + NOT_OFFERED_INVALID_LEGAL_MESSAGE = 9, + kMaxValue = NOT_OFFERED_INVALID_LEGAL_MESSAGE, +}; + +// Metrics to track events when local card migration dialog is offered. +enum LocalCardMigrationDialogOfferMetric { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // The dialog is shown to the user. + LOCAL_CARD_MIGRATION_DIALOG_SHOWN = 0, + // The dialog is not shown due to legal message being invalid. + LOCAL_CARD_MIGRATION_DIALOG_NOT_SHOWN_INVALID_LEGAL_MESSAGE = 1, + // The dialog is shown when migration feedback is available. + LOCAL_CARD_MIGRATION_DIALOG_FEEDBACK_SHOWN = 2, + // The dialog is shown when migration fails due to server error. + LOCAL_CARD_MIGRATION_DIALOG_FEEDBACK_SERVER_ERROR_SHOWN = 3, + NUM_LOCAL_CARD_MIGRATION_DIALOG_OFFER_METRICS, +}; + +// Metrics to track user interactions with the dialog. +enum LocalCardMigrationDialogUserInteractionMetric { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // The user explicitly accepts the offer by clicking the save button. + LOCAL_CARD_MIGRATION_DIALOG_CLOSED_SAVE_BUTTON_CLICKED = 0, + // The user explicitly denies the offer by clicking the cancel button. + LOCAL_CARD_MIGRATION_DIALOG_CLOSED_CANCEL_BUTTON_CLICKED = 1, + // The user clicks the legal message. + LOCAL_CARD_MIGRATION_DIALOG_LEGAL_MESSAGE_CLICKED = 2, + // The user clicks the view card button after successfully migrated cards. + LOCAL_CARD_MIGRATION_DIALOG_CLOSED_VIEW_CARDS_BUTTON_CLICKED = 3, + // The user clicks the done button to close dialog after migration. + LOCAL_CARD_MIGRATION_DIALOG_CLOSED_DONE_BUTTON_CLICKED = 4, + // The user clicks the trash icon to delete invalid card. + LOCAL_CARD_MIGRATION_DIALOG_DELETE_CARD_ICON_CLICKED = 5, + NUM_LOCAL_CARD_MIGRATION_DIALOG_USER_INTERACTION_METRICS, +}; + +// These metrics are logged for each local card migration origin. These are +// used to derive the conversion rate for each triggering source. +enum LocalCardMigrationPromptMetric { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // The intermediate bubble is shown to the user. + INTERMEDIATE_BUBBLE_SHOWN = 0, + // The intermediate bubble is accepted by the user. + INTERMEDIATE_BUBBLE_ACCEPTED = 1, + // The main dialog is shown to the user. + MAIN_DIALOG_SHOWN = 2, + // The main dialog is accepted by the user. + MAIN_DIALOG_ACCEPTED = 3, + NUM_LOCAL_CARD_MIGRATION_PROMPT_METRICS, +}; + +// Local card migration origin denotes from where the migration is triggered. +enum LocalCardMigrationOrigin { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // Trigger when user submitted a form using local card. + UseOfLocalCard, + // Trigger when user submitted a form using server card. + UseOfServerCard, + // Trigger from settings page. + SettingsPage, +}; + +void LogLocalCardMigrationBubbleOfferMetric( + LocalCardMigrationBubbleOfferMetric metric, + bool is_reshow); + +void LogLocalCardMigrationBubbleResultMetric( + LocalCardMigrationBubbleResultMetric metric, + bool is_reshow); + +void LogLocalCardMigrationDecisionMetric( + LocalCardMigrationDecisionMetric metric); + +void LogLocalCardMigrationDialogOfferMetric( + LocalCardMigrationDialogOfferMetric metric); + +void LogLocalCardMigrationDialogUserInteractionMetric( + const base::TimeDelta& duration, + LocalCardMigrationDialogUserInteractionMetric metric); + +void LogLocalCardMigrationDialogUserSelectionPercentageMetric(int selected, + int total); + +// When local card migration is not offered due to max strike limit reached, +// logs the occurrence. +void LogLocalCardMigrationNotOfferedDueToMaxStrikesMetric( + AutofillMetrics::SaveTypeMetric metric); + +void LogLocalCardMigrationPromptMetric( + LocalCardMigrationOrigin local_card_migration_origin, + LocalCardMigrationPromptMetric metric); + +} // namespace autofill::autofill_metrics + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_LOCAL_CARD_MIGRATION_METRICS_H_ diff --git a/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.cc b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.cc index 4a7b997d46d..f82c7cb1ae7 100644 --- a/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.cc +++ b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.cc @@ -7,11 +7,118 @@ #include <unordered_map> #include "base/metrics/histogram_functions.h" +#include "base/metrics/user_metrics.h" #include "base/notreached.h" #include "components/autofill/core/browser/data_model/autofill_offer_data.h" namespace autofill::autofill_metrics { +void LogOfferNotificationBubbleOfferMetric( + AutofillOfferData::OfferType offer_type, + bool is_reshow) { + std::string histogram_name = "Autofill.OfferNotificationBubbleOffer."; + // Switch to different sub-histogram depending on offer type being displayed. + switch (offer_type) { + case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER: + histogram_name += "CardLinkedOffer"; + break; + case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER: + histogram_name += "GPayPromoCodeOffer"; + break; + case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER: + histogram_name += "FreeListingCouponOffer"; + break; + case AutofillOfferData::OfferType::UNKNOWN: + NOTREACHED(); + return; + } + base::UmaHistogramBoolean(histogram_name, is_reshow); +} + +void LogOfferNotificationBubblePromoCodeButtonClicked( + AutofillOfferData::OfferType offer_type) { + std::string histogram_name = + "Autofill.OfferNotificationBubblePromoCodeButtonClicked."; + // Switch to different sub-histogram depending on offer type being displayed. + // Card-linked offers do not have a promo code button. + switch (offer_type) { + case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER: + histogram_name += "GPayPromoCodeOffer"; + break; + case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER: + histogram_name += "FreeListingCouponOffer"; + break; + case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER: + case AutofillOfferData::OfferType::UNKNOWN: + NOTREACHED(); + return; + } + base::UmaHistogramBoolean(histogram_name, true); +} + +void LogOfferNotificationBubbleResultMetric( + AutofillOfferData::OfferType offer_type, + OfferNotificationBubbleResultMetric metric, + bool is_reshow) { + DCHECK_LE(metric, OfferNotificationBubbleResultMetric::kMaxValue); + std::string histogram_name = "Autofill.OfferNotificationBubbleResult."; + // Switch to different sub-histogram depending on offer type being displayed. + switch (offer_type) { + case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER: + histogram_name += "CardLinkedOffer."; + break; + case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER: + histogram_name += "GPayPromoCodeOffer."; + break; + case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER: + histogram_name += "FreeListingCouponOffer."; + break; + case AutofillOfferData::OfferType::UNKNOWN: + NOTREACHED(); + return; + } + // Add subhistogram for |is_reshow| decision. + histogram_name += is_reshow ? "Reshows" : "FirstShow"; + base::UmaHistogramEnumeration(histogram_name, metric); +} + +void LogOfferNotificationBubbleSuppressed( + AutofillOfferData::OfferType offer_type) { + std::string histogram_name = "Autofill.OfferNotificationBubbleSuppressed."; + // Switch to different sub-histogram depending on offer type being suppressed. + // Card-linked offers will not be suppressed. + switch (offer_type) { + case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER: + histogram_name += "GPayPromoCodeOffer"; + break; + case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER: + histogram_name += "FreeListingCouponOffer"; + break; + case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER: + case AutofillOfferData::OfferType::UNKNOWN: + NOTREACHED(); + return; + } + base::UmaHistogramBoolean(histogram_name, true); +} + +void LogOfferNotificationInfoBarDeepLinkClicked() { + base::RecordAction(base::UserMetricsAction( + "Autofill_OfferNotificationInfoBar_DeepLinkClicked")); +} + +void LogOfferNotificationInfoBarResultMetric( + OfferNotificationInfoBarResultMetric metric) { + DCHECK_LE(metric, OfferNotificationInfoBarResultMetric::kMaxValue); + base::UmaHistogramEnumeration( + "Autofill.OfferNotificationInfoBarResult.CardLinkedOffer", metric); +} + +void LogOfferNotificationInfoBarShown() { + base::UmaHistogramBoolean( + "Autofill.OfferNotificationInfoBarOffer.CardLinkedOffer", true); +} + void LogStoredOfferMetrics( const std::vector<std::unique_ptr<AutofillOfferData>>& offers) { std::unordered_map<AutofillOfferData::OfferType, int> offer_count; @@ -25,27 +132,39 @@ void LogStoredOfferMetrics( offer_count[offer->GetOfferType()]++; + std::string related_merchant_count_histogram_name = + "Autofill.Offer.StoredOfferRelatedMerchantCount"; + // Switch to different sub-histogram depending on offer type being + // displayed. + switch (offer->GetOfferType()) { + case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER: + related_merchant_count_histogram_name += ".GPayPromoCodeOffer"; + break; + case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER: + related_merchant_count_histogram_name += ".CardLinkedOffer"; + break; + case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER: + case AutofillOfferData::OfferType::UNKNOWN: + NOTREACHED(); + continue; + } + base::UmaHistogramCounts1000(related_merchant_count_histogram_name, + offer->GetMerchantOrigins().size()); + if (offer->GetOfferType() == AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER) { - base::UmaHistogramCounts1000( - "Autofill.Offer.StoredOfferRelatedMerchantCount", - offer->GetMerchantOrigins().size()); base::UmaHistogramCounts1000("Autofill.Offer.StoredOfferRelatedCardCount", offer->GetEligibleInstrumentIds().size()); } } - if (offer_count[AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER] > 0) { - base::UmaHistogramCounts1000( - "Autofill.Offer.StoredOfferCount.GPayPromoCodeOffer", - offer_count[AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER]); - } + base::UmaHistogramCounts1000( + "Autofill.Offer.StoredOfferCount2.GPayPromoCodeOffer", + offer_count[AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER]); - if (offer_count[AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER] > 0) { - base::UmaHistogramCounts1000( - "Autofill.Offer.StoredOfferCount.CardLinkedOffer", - offer_count[AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER]); - } + base::UmaHistogramCounts1000( + "Autofill.Offer.StoredOfferCount2.CardLinkedOffer", + offer_count[AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER]); } void LogOffersSuggestionsPopupShown(bool first_time_being_logged) { @@ -86,4 +205,29 @@ void LogIndividualOfferSuggestionEvent( base::UmaHistogramEnumeration(histogram_name, event); } +// static +void LogSyncedOfferDataBeingValid(bool valid) { + base::UmaHistogramBoolean("Autofill.Offer.SyncedOfferDataBeingValid", valid); +} + +void LogPageLoadsWithOfferIconShown(AutofillOfferData::OfferType offer_type) { + std::string histogram_name = "Autofill.PageLoadsWithOfferIconShowing"; + // Switch to different sub-histogram depending on offer type being displayed. + switch (offer_type) { + case AutofillOfferData::OfferType::FREE_LISTING_COUPON_OFFER: + histogram_name += ".FreeListingCouponOffer"; + break; + case AutofillOfferData::OfferType::GPAY_CARD_LINKED_OFFER: + histogram_name += "CardLinkedOffer"; + break; + case AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER: + histogram_name += ".GPayPromoCodeOffer"; + break; + case AutofillOfferData::OfferType::UNKNOWN: + NOTREACHED(); + return; + } + base::UmaHistogramBoolean(histogram_name, true); +} + } // namespace autofill::autofill_metrics diff --git a/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.h b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.h index 38cc516e439..c57c4bd5507 100644 --- a/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.h +++ b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics.h @@ -12,10 +12,35 @@ namespace autofill::autofill_metrics { -// Logs the offer data associated with a profile. This should be called each -// time a Chrome profile is launched. -void LogStoredOfferMetrics( - const std::vector<std::unique_ptr<AutofillOfferData>>& offers); +// Metrics to track event when the offer notification bubble is closed. +enum class OfferNotificationBubbleResultMetric { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // The user explicitly acknowledged the bubble by clicking the ok button. + OFFER_NOTIFICATION_BUBBLE_ACKNOWLEDGED = 0, + // The user explicitly closed the prompt with the close button or ESC. + OFFER_NOTIFICATION_BUBBLE_CLOSED = 1, + // The user did not interact with the prompt. + OFFER_NOTIFICATION_BUBBLE_NOT_INTERACTED = 2, + // The prompt lost focus and was deactivated. + OFFER_NOTIFICATION_BUBBLE_LOST_FOCUS = 3, + kMaxValue = OFFER_NOTIFICATION_BUBBLE_LOST_FOCUS, +}; + +// Metrics to track event when the offer notification infobar is closed. +enum class OfferNotificationInfoBarResultMetric { + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + + // User acknowledged the infobar by clicking the ok button. + OFFER_NOTIFICATION_INFOBAR_ACKNOWLEDGED = 0, + // User explicitly closed the infobar with the close button. + OFFER_NOTIFICATION_INFOBAR_CLOSED = 1, + // InfoBar was shown but user did not interact with the it. + OFFER_NOTIFICATION_INFOBAR_IGNORED = 2, + kMaxValue = OFFER_NOTIFICATION_INFOBAR_IGNORED, +}; // Metrics to track events related to the offers suggestions popup. enum class OffersSuggestionsPopupEvent { @@ -63,6 +88,21 @@ enum class OffersSuggestionsEvent { kMaxValue = kOfferSuggestionSeeOfferDetailsSelectedOnce, }; +void LogOfferNotificationBubbleOfferMetric( + AutofillOfferData::OfferType offer_type, + bool is_reshow); + +void LogOfferNotificationBubblePromoCodeButtonClicked( + AutofillOfferData::OfferType offer_type); + +void LogOfferNotificationBubbleResultMetric( + AutofillOfferData::OfferType offer_type, + OfferNotificationBubbleResultMetric metric, + bool is_reshow); + +void LogOfferNotificationBubbleSuppressed( + AutofillOfferData::OfferType offer_type); + // Log that the offers suggestions popup was shown. If |first_time_being_logged| // is true, it represents that it has not been logged yet for the promo code // offer field that the user is on, so additional logging is needed for the @@ -73,6 +113,23 @@ void LogOffersSuggestionsPopupShown(bool first_time_being_logged); void LogIndividualOfferSuggestionEvent(OffersSuggestionsEvent event, AutofillOfferData::OfferType offer_type); +void LogOfferNotificationInfoBarDeepLinkClicked(); +void LogOfferNotificationInfoBarResultMetric( + OfferNotificationInfoBarResultMetric metric); +void LogOfferNotificationInfoBarShown(); + +// Logs the offer data associated with a profile. This should be called each +// time a Chrome profile is launched. +void LogStoredOfferMetrics( + const std::vector<std::unique_ptr<AutofillOfferData>>& offers); + +// Logs whether the synced autofill offer data is valid. +void LogSyncedOfferDataBeingValid(bool invalid); + +// Log the presence of the offer notification icon shows on navigation event +// for |offer_type|. +void LogPageLoadsWithOfferIconShown(AutofillOfferData::OfferType offer_type); + } // namespace autofill::autofill_metrics #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_OFFERS_METRICS_H_ diff --git a/chromium/components/autofill/core/browser/metrics/payments/offers_metrics_unittest.cc b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics_unittest.cc index d30bdc96067..98dbcc7ece0 100644 --- a/chromium/components/autofill/core/browser/metrics/payments/offers_metrics_unittest.cc +++ b/chromium/components/autofill/core/browser/metrics/payments/offers_metrics_unittest.cc @@ -27,12 +27,22 @@ TEST_F(OffersMetricsTest, LogStoredOfferMetrics) { AutofillOfferData offer1 = test::GetCardLinkedOfferData1(); AutofillOfferData offer2 = test::GetCardLinkedOfferData2(); AutofillOfferData offer3 = test::GetPromoCodeOfferData(); + AutofillOfferData offer4 = test::GetPromoCodeOfferData(); + + // Add the test case of having several cards linked to an offer. offer2.SetEligibleInstrumentIdForTesting({222222, 999999, 888888}); + + // Add the test case of having two merchant origins related to an offer. offer2.SetMerchantOriginForTesting( {GURL("http://www.example2.com"), GURL("https://www.example3.com/")}); + + // Add the test case of having no merchant origins related to an offer. + offer4.SetMerchantOriginForTesting({}); + offers.push_back(std::make_unique<AutofillOfferData>(offer1)); offers.push_back(std::make_unique<AutofillOfferData>(offer2)); offers.push_back(std::make_unique<AutofillOfferData>(offer3)); + offers.push_back(std::make_unique<AutofillOfferData>(offer4)); base::HistogramTester histogram_tester; @@ -43,14 +53,37 @@ TEST_F(OffersMetricsTest, LogStoredOfferMetrics) { }; // Validate the count metrics. - EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferCount.CardLinkedOffer"), + EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferCount2.CardLinkedOffer"), BucketsAre(Bucket(2, 1))); - EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferCount.GPayPromoCodeOffer"), - BucketsAre(Bucket(1, 1))); - EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferRelatedMerchantCount"), - BucketsAre(Bucket(1, 1), Bucket(2, 1))); + EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferCount2.GPayPromoCodeOffer"), + BucketsAre(Bucket(2, 1))); + EXPECT_THAT( + SamplesOf( + "Autofill.Offer.StoredOfferRelatedMerchantCount.CardLinkedOffer"), + BucketsAre(Bucket(1, 1), Bucket(2, 1))); + EXPECT_THAT( + SamplesOf( + "Autofill.Offer.StoredOfferRelatedMerchantCount.GPayPromoCodeOffer"), + BucketsAre(Bucket(0, 1), Bucket(1, 1))); EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferRelatedCardCount"), BucketsAre(Bucket(1, 1), Bucket(3, 1))); } +TEST_F(OffersMetricsTest, LogStoredOfferMetrics_NoOffers) { + base::HistogramTester histogram_tester; + + autofill_metrics::LogStoredOfferMetrics( + std::vector<std::unique_ptr<AutofillOfferData>>()); + + auto SamplesOf = [&histogram_tester](base::StringPiece metric) { + return histogram_tester.GetAllSamples(metric); + }; + + // Validate the count metrics. + EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferCount2.CardLinkedOffer"), + BucketsAre(Bucket(0, 1))); + EXPECT_THAT(SamplesOf("Autofill.Offer.StoredOfferCount2.GPayPromoCodeOffer"), + BucketsAre(Bucket(0, 1))); +} + } // namespace autofill::autofill_metrics diff --git a/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.cc b/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.cc index 9c5f350b4fe..975cbb80575 100644 --- a/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.cc +++ b/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics.cc @@ -67,10 +67,15 @@ int GetShadowPrediction(ServerFieldType current, } void LogShadowPredictionComparison(const AutofillField& field) { -#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS) const auto& submitted_types = field.possible_types(); base::UmaHistogramSparse( + "Autofill.ShadowPredictions.DefaultHeuristicToDefaultServer", + GetShadowPrediction(field.heuristic_type(), field.server_type(), + submitted_types)); + +#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS) + base::UmaHistogramSparse( "Autofill.ShadowPredictions.ExperimentalToDefault", GetShadowPrediction(field.heuristic_type(PatternSource::kDefault), field.heuristic_type(PatternSource::kExperimental), diff --git a/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics_unittest.cc b/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics_unittest.cc index 07fd12addd7..f7f15c27651 100644 --- a/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics_unittest.cc +++ b/chromium/components/autofill/core/browser/metrics/shadow_prediction_metrics_unittest.cc @@ -30,12 +30,12 @@ constexpr int kNameFirstDifferentPredictionsValueAgreesWithOld = 21; constexpr int kNameFirstDifferentPredictionsValueAgreesWithBoth = 24; constexpr int kNameFirstDifferentPredictionsValueAgreesWithNeither = 23; constexpr int kEmailAddressDifferentPredictionsValueAgreesWithNew = 58; -#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS) constexpr int kNameFullSamePredictionValueAgrees = 43; +constexpr int kSearchTermDifferentPredictionsValueAgreesWithNew = 586; +#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS) constexpr int kNameFullDifferentPredictionsValueAgreesWithOld = 45; constexpr int kEmailAddressDifferentPredictionsValueAgreesWithOld = 57; constexpr int kSearchTermSamePredictionValueDisagrees = 584; -constexpr int kSearchTermDifferentPredictionsValueAgreesWithNew = 586; #endif namespace { @@ -96,7 +96,8 @@ TEST(AutofillShadowPredictionComparisonTest, ComparisonContainsAllTypes) { // If this test fails after adding a type, update // `AutofillPredictionsComparisonResult` in tools/metrics/histograms/enums.xml // and set `last_known_type` to the last entry in the enum. - constexpr ServerFieldType last_known_type = PHONE_HOME_NUMBER_SUFFIX; + constexpr ServerFieldType last_known_type = + CREDIT_CARD_STANDALONE_VERIFICATION_CODE; int max_comparison = GetShadowPrediction(last_known_type, NAME_FIRST, {NAME_LAST}); @@ -211,6 +212,42 @@ TEST_F(AutofillShadowPredictionMetricsTest, } #endif +// Test that Autofill.ShadowPredictions.DefaultHeuristicToDefaultServer compares +// heuristics to server predictions. +TEST_F(AutofillShadowPredictionMetricsTest, CompareHeuristicsAndServer) { +#if BUILDFLAG(USE_INTERNAL_AUTOFILL_HEADERS) + constexpr PatternSource source = PatternSource::kDefault; +#else + constexpr PatternSource source = PatternSource::kLegacy; +#endif + + FormData form = GetFormWith2Fields(autofill_client_->form_origin()); + form.fields[0].value = u"Elvis Aaron Presley"; // A known `NAME_FULL`. + form.fields[1].value = u"buddy@gmail.com"; // A known `EMAIL_ADDRESS`. + + std::vector<ServerFieldType> server_types = {NAME_FULL, EMAIL_ADDRESS}; + + // Simulate having seen this form on page load. + autofill_manager().AddSeenForm(form, + {// Field 0 + {{source, NAME_FULL}}, + // Field 1 + {{source, SEARCH_TERM}}}, + server_types); + + // Simulate form submission. + base::HistogramTester histogram_tester; + autofill_manager().OnFormSubmitted(form, /*known_success=*/false, + SubmissionSource::FORM_SUBMISSION); + + EXPECT_THAT( + histogram_tester.GetAllSamples( + "Autofill.ShadowPredictions.DefaultHeuristicToDefaultServer"), + UnorderedElementsAre( + Bucket(kNameFullSamePredictionValueAgrees, 1), + Bucket(kSearchTermDifferentPredictionsValueAgreesWithNew, 1))); +} + } // namespace } // namespace autofill::metrics diff --git a/chromium/components/autofill/core/browser/mock_autocomplete_history_manager.h b/chromium/components/autofill/core/browser/mock_autocomplete_history_manager.h index 81772fc85be..ba617b68797 100644 --- a/chromium/components/autofill/core/browser/mock_autocomplete_history_manager.h +++ b/chromium/components/autofill/core/browser/mock_autocomplete_history_manager.h @@ -17,14 +17,12 @@ class MockAutocompleteHistoryManager : public AutocompleteHistoryManager { ~MockAutocompleteHistoryManager(); MOCK_METHOD( - void, + bool, OnGetSingleFieldSuggestions, (int query_id, bool is_autocomplete_enabled, bool autoselect_first_suggestion, - const std::u16string& name, - const std::u16string& prefix, - const std::string& form_control_type, + const FormFieldData& field, base::WeakPtr<AutocompleteHistoryManager::SuggestionsHandler> handler, const SuggestionsContext& context), (override)); diff --git a/chromium/components/autofill/core/browser/mock_iban_manager.cc b/chromium/components/autofill/core/browser/mock_iban_manager.cc new file mode 100644 index 00000000000..02fd609b2a8 --- /dev/null +++ b/chromium/components/autofill/core/browser/mock_iban_manager.cc @@ -0,0 +1,14 @@ +// Copyright 2022 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/mock_iban_manager.h" + +namespace autofill { + +MockIBANManager::MockIBANManager(PersonalDataManager* personal_data_manager) + : IBANManager(personal_data_manager, /*is_off_the_record=*/false) {} + +MockIBANManager::~MockIBANManager() = default; + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/mock_iban_manager.h b/chromium/components/autofill/core/browser/mock_iban_manager.h new file mode 100644 index 00000000000..a2fbdabcc4d --- /dev/null +++ b/chromium/components/autofill/core/browser/mock_iban_manager.h @@ -0,0 +1,51 @@ +// Copyright 2022 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_MOCK_IBAN_MANAGER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_IBAN_MANAGER_H_ + +#include "base/memory/weak_ptr.h" +#include "components/autofill/core/browser/iban_manager.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace autofill { + +class MockIBANManager : public IBANManager { + public: + explicit MockIBANManager(PersonalDataManager* personal_data_manager); + + ~MockIBANManager() override; + + MOCK_METHOD(bool, + OnGetSingleFieldSuggestions, + (int query_id, + bool is_autocomplete_enabled, + bool autoselect_first_suggestion, + const FormFieldData& field, + base::WeakPtr<IBANManager::SuggestionsHandler> handler, + const SuggestionsContext& context), + (override)); + MOCK_METHOD(void, + OnWillSubmitFormWithFields, + (const std::vector<FormFieldData>& fields, + bool is_autocomplete_enabled), + (override)); + MOCK_METHOD(void, + CancelPendingQueries, + (const IBANManager::SuggestionsHandler*), + (override)); + MOCK_METHOD(void, + OnRemoveCurrentSingleFieldSuggestion, + (const std::u16string&, const std::u16string&, int), + (override)); + MOCK_METHOD(void, + OnSingleFieldSuggestionSelected, + (const std::u16string&, int), + (override)); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_IBAN_MANAGER_H_ diff --git a/chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.h b/chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.h index 3a95b96c446..e45208d0b28 100644 --- a/chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.h +++ b/chromium/components/autofill/core/browser/mock_merchant_promo_code_manager.h @@ -17,14 +17,12 @@ class MockMerchantPromoCodeManager : public MerchantPromoCodeManager { ~MockMerchantPromoCodeManager() override; MOCK_METHOD( - void, + bool, OnGetSingleFieldSuggestions, (int query_id, bool is_autocomplete_enabled, bool autoselect_first_suggestion, - const std::u16string& name, - const std::u16string& prefix, - const std::string& form_control_type, + const FormFieldData& field, base::WeakPtr<MerchantPromoCodeManager::SuggestionsHandler> handler, const SuggestionsContext& context), (override)); diff --git a/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.h b/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.h index 65d2ec16f30..f3345df2ad9 100644 --- a/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.h +++ b/chromium/components/autofill/core/browser/mock_single_field_form_fill_router.h @@ -24,14 +24,12 @@ class MockSingleFieldFormFillRouter : public SingleFieldFormFillRouter { const FormStructure* form_structure, bool is_autocomplete_enabled), (override)); - MOCK_METHOD(void, + MOCK_METHOD(bool, OnGetSingleFieldSuggestions, (int query_id, bool is_autocomplete_enabled, bool autoselect_first_suggestion, - const std::u16string& name, - const std::u16string& prefix, - const std::string& form_control_type, + const FormFieldData& field, base::WeakPtr<SingleFieldFormFiller::SuggestionsHandler> handler, const SuggestionsContext& context), (override)); diff --git a/chromium/components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.cc b/chromium/components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.cc index 8ad48047df4..ec4ba68da52 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.cc +++ b/chromium/components/autofill/core/browser/payments/autofill_offer_notification_infobar_delegate_mobile.cc @@ -13,6 +13,7 @@ #include "build/branding_buildflags.h" #include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/browser/metrics/payments/offers_metrics.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_payments_features.h" @@ -38,21 +39,21 @@ AutofillOfferNotificationInfoBarDelegateMobile:: network_icon_id_(CreditCard::IconResourceId(card.network())), deep_link_url_(offer_details_url), user_manually_closed_infobar_(false) { - AutofillMetrics::LogOfferNotificationInfoBarShown(); + autofill_metrics::LogOfferNotificationInfoBarShown(); } AutofillOfferNotificationInfoBarDelegateMobile:: ~AutofillOfferNotificationInfoBarDelegateMobile() { if (!user_manually_closed_infobar_) { - AutofillMetrics::LogOfferNotificationInfoBarResultMetric( - AutofillMetrics::OfferNotificationInfoBarResultMetric:: + autofill_metrics::LogOfferNotificationInfoBarResultMetric( + autofill_metrics::OfferNotificationInfoBarResultMetric:: OFFER_NOTIFICATION_INFOBAR_IGNORED); } } void AutofillOfferNotificationInfoBarDelegateMobile::OnOfferDeepLinkClicked( GURL url) { - AutofillMetrics::LogOfferNotificationInfoBarDeepLinkClicked(); + autofill_metrics::LogOfferNotificationInfoBarDeepLinkClicked(); infobar()->owner()->OpenURL(url, WindowOpenDisposition::NEW_FOREGROUND_TAB); } @@ -87,15 +88,15 @@ std::u16string AutofillOfferNotificationInfoBarDelegateMobile::GetButtonLabel( } void AutofillOfferNotificationInfoBarDelegateMobile::InfoBarDismissed() { - AutofillMetrics::LogOfferNotificationInfoBarResultMetric( - AutofillMetrics::OfferNotificationInfoBarResultMetric:: + autofill_metrics::LogOfferNotificationInfoBarResultMetric( + autofill_metrics::OfferNotificationInfoBarResultMetric:: OFFER_NOTIFICATION_INFOBAR_CLOSED); user_manually_closed_infobar_ = true; } bool AutofillOfferNotificationInfoBarDelegateMobile::Accept() { - AutofillMetrics::LogOfferNotificationInfoBarResultMetric( - AutofillMetrics::OfferNotificationInfoBarResultMetric:: + autofill_metrics::LogOfferNotificationInfoBarResultMetric( + autofill_metrics::OfferNotificationInfoBarResultMetric:: OFFER_NOTIFICATION_INFOBAR_ACKNOWLEDGED); user_manually_closed_infobar_ = true; return true; 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 ba148d01a98..ae16420fa7f 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 @@ -21,6 +21,7 @@ #include "build/build_config.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_driver.h" +#include "components/autofill/core/browser/autofill_progress_dialog_type.h" #include "components/autofill/core/browser/browser_autofill_manager.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h" @@ -492,7 +493,7 @@ void CreditCardAccessManager::Authenticate() { // authentication flow since the card unmask prompt will pop up. client_->CloseWebauthnDialog(); #endif - GetOrCreateCVCAuthenticator()->Authenticate( + client_->GetCVCAuthenticator()->Authenticate( card_.get(), weak_ptr_factory_.GetWeakPtr(), personal_data_manager_); break; } @@ -521,7 +522,7 @@ void CreditCardAccessManager::Authenticate() { // Delegate the task to CreditCardOtpAuthenticator. CardUnmaskChallengeOption selected_challenge_option = *card_unmask_challenge_options_it; - GetOrCreateOtpAuthenticator()->OnChallengeOptionSelected( + client_->GetOtpAuthenticator()->OnChallengeOptionSelected( card_.get(), selected_challenge_option, weak_ptr_factory_.GetWeakPtr(), virtual_card_unmask_response_details_.context_token, @@ -536,13 +537,6 @@ void CreditCardAccessManager::Authenticate() { } } -CreditCardCVCAuthenticator* -CreditCardAccessManager::GetOrCreateCVCAuthenticator() { - if (!cvc_authenticator_) - cvc_authenticator_ = std::make_unique<CreditCardCVCAuthenticator>(client_); - return cvc_authenticator_.get(); -} - #if !BUILDFLAG(IS_IOS) CreditCardFIDOAuthenticator* CreditCardAccessManager::GetOrCreateFIDOAuthenticator() { @@ -553,13 +547,6 @@ CreditCardAccessManager::GetOrCreateFIDOAuthenticator() { } #endif -CreditCardOtpAuthenticator* -CreditCardAccessManager::GetOrCreateOtpAuthenticator() { - if (!otp_authenticator_) - otp_authenticator_ = std::make_unique<CreditCardOtpAuthenticator>(client_); - return otp_authenticator_.get(); -} - void CreditCardAccessManager::OnCVCAuthenticationComplete( const CreditCardCVCAuthenticator::CVCAuthenticationResponse& response) { is_authentication_in_progress_ = false; @@ -636,7 +623,15 @@ bool CreditCardAccessManager::UserOptedInToFidoFromSettingsPageOnMobile() #if !BUILDFLAG(IS_IOS) void CreditCardAccessManager::OnFIDOAuthenticationComplete( const CreditCardFIDOAuthenticator::FidoAuthenticationResponse& response) { -#if !BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_ANDROID) + if (base::FeatureList::IsEnabled( + features::kAutofillEnableFIDOProgressDialog)) { + // Close the progress dialog when the authentication for getting the full + // card completes. + client_->CloseAutofillProgressDialog( + /*show_confirmation_before_closing=*/true); + } +#else // Close the Webauthn verify pending dialog. If FIDO authentication succeeded, // card is filled to the form, otherwise fall back to CVC authentication which // does not need the verify pending dialog either. @@ -969,6 +964,7 @@ void CreditCardAccessManager::FetchMaskedServerCard() { void CreditCardAccessManager::FetchVirtualCard() { is_authentication_in_progress_ = true; client_->ShowAutofillProgressDialog( + AutofillProgressDialogType::kVirtualCardUnmaskProgressDialog, base::BindOnce(&CreditCardAccessManager::OnVirtualCardUnmaskCancelled, weak_ptr_factory_.GetWeakPtr())); @@ -1117,7 +1113,7 @@ void CreditCardAccessManager::OnVirtualCardUnmaskCancelled() { // Virtual Card Unmask request, so we need to reset the state of the // CreditCardOtpAuthenticator as well to ensure the flow does not continue, // as continuing the flow can cause a crash. - GetOrCreateOtpAuthenticator()->Reset(); + client_->GetOtpAuthenticator()->Reset(); } AutofillMetrics::VirtualCardUnmaskFlowType flow_type; diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h index 8b693f8c7de..268df7b0f14 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h +++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h @@ -159,14 +159,9 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester, // Returns true if a |unmasked_cards_cache| contains an entry for the card. bool IsCardPresentInUnmaskedCache(const CreditCard& card) const; - // Accessors to different authenticators. They will first create the - // authenticators if they do not exist. Otherwise the accessors will simply - // return references to the authenticators. - CreditCardCVCAuthenticator* GetOrCreateCVCAuthenticator(); #if !BUILDFLAG(IS_IOS) CreditCardFIDOAuthenticator* GetOrCreateFIDOAuthenticator(); #endif - CreditCardOtpAuthenticator* GetOrCreateOtpAuthenticator(); private: // TODO(crbug.com/1249665): Remove FRIEND and change everything to _ForTesting @@ -208,9 +203,6 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester, RiskBasedVirtualCardUnmasking_Failure_VirtualCardRetrievalError); FRIEND_TEST_ALL_PREFIXES(CreditCardAccessManagerTest, RiskBasedVirtualCardUnmasking_FlowCancelled); - friend class AutofillAssistantTest; - friend class BrowserAutofillManagerTest; - friend class AutofillMetricsTest; friend class metrics::AutofillMetricsBaseTest; friend class CreditCardAccessManagerTest; @@ -220,10 +212,6 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester, fido_authenticator_ = std::move(fido_authenticator); } #endif - void set_otp_authenticator_for_testing( - std::unique_ptr<CreditCardOtpAuthenticator> otp_authenticator) { - otp_authenticator_ = std::move(otp_authenticator); - } #if defined(UNIT_TEST) // Mocks that a virtual card was selected, so unit tests that don't run the @@ -417,9 +405,6 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester, // Timestamp for when fido_authenticator_->IsUserVerifiable() is called. absl::optional<base::TimeTicks> is_user_verifiable_called_timestamp_; - // Authenticators for card unmasking. - std::unique_ptr<CreditCardCVCAuthenticator> cvc_authenticator_; - std::unique_ptr<CreditCardOtpAuthenticator> otp_authenticator_; #if !BUILDFLAG(IS_IOS) std::unique_ptr<CreditCardFIDOAuthenticator> fido_authenticator_; diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc index 0b5758a70fc..ed98c61fdfc 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc @@ -28,6 +28,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/time/clock.h" #include "base/time/time.h" #include "build/build_config.h" #include "components/autofill/core/browser/autofill_download_manager.h" @@ -211,8 +212,7 @@ class CreditCardAccessManagerTest : public testing::Test { auto otp_authenticator = std::make_unique<TestCreditCardOtpAuthenticator>(&autofill_client_); otp_authenticator_ = otp_authenticator.get(); - credit_card_access_manager_->set_otp_authenticator_for_testing( - std::move(otp_authenticator)); + autofill_client_.set_otp_authenticator(std::move(otp_authenticator)); } void TearDown() override { @@ -264,7 +264,7 @@ class CreditCardAccessManagerTest : public testing::Test { } CreditCardCVCAuthenticator* GetCVCAuthenticator() { - return credit_card_access_manager_->GetOrCreateCVCAuthenticator(); + return autofill_client_.GetCVCAuthenticator(); } void MockUserResponseForCvcAuth(std::u16string cvc, bool enable_fido) { diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc index 9043fc665d6..a2311a503ca 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc @@ -21,6 +21,7 @@ #include "build/build_config.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_experiments.h" +#include "components/autofill/core/browser/autofill_progress_dialog_type.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" #include "components/autofill/core/browser/payments/fido_authentication_strike_database.h" @@ -440,6 +441,18 @@ void CreditCardFIDOAuthenticator::OnDidGetAssertion( autofill_client_->GetLastCommittedURL().DeprecatedGetOriginAsURL(); } +#if BUILDFLAG(IS_ANDROID) + if (base::FeatureList::IsEnabled( + features::kAutofillEnableFIDOProgressDialog)) { + // Open the progress dialog when authenticating and getting the full card + // from FIDO. + autofill_client_->ShowAutofillProgressDialog( + AutofillProgressDialogType::kAndroidFIDOProgressDialog, + base::BindOnce(&CreditCardFIDOAuthenticator::CancelVerification, + weak_ptr_factory_.GetWeakPtr())); + } +#endif + full_card_request_->GetFullCardViaFIDO( *card_, AutofillClient::UnmaskCardReason::kAutofill, weak_ptr_factory_.GetWeakPtr(), std::move(response), @@ -811,18 +824,16 @@ void CreditCardFIDOAuthenticator::UpdateUserPref() { } webauthn::InternalAuthenticator* CreditCardFIDOAuthenticator::authenticator() { - if (authenticator_) - return authenticator_; - - authenticator_ = - autofill_driver_->GetOrCreateCreditCardInternalAuthenticator(); - - // |authenticator_| may be null for unsupported platforms. - if (authenticator_) { - authenticator()->SetEffectiveOrigin( - url::Origin::Create(payments::GetBaseSecureUrl())); + if (!authenticator_) { + authenticator_ = autofill_client_->CreateCreditCardInternalAuthenticator( + autofill_driver_.get()); + // `authenticator_` may be null for unsupported platforms. + if (authenticator_) { + authenticator_->SetEffectiveOrigin( + url::Origin::Create(payments::GetBaseSecureUrl())); + } } - - return authenticator_; + return authenticator_.get(); } + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h index 0bda2fe812c..dbf86222cda 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h +++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h @@ -263,7 +263,7 @@ class CreditCardFIDOAuthenticator const raw_ptr<payments::PaymentsClient> payments_client_; // Authenticator pointer to facilitate WebAuthn. - raw_ptr<webauthn::InternalAuthenticator> authenticator_ = nullptr; + std::unique_ptr<webauthn::InternalAuthenticator> authenticator_; // Responsible for getting the full card details, including the PAN and the // CVC. 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 954658510fa..c3abf087215 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 @@ -283,10 +283,16 @@ void CreditCardSaveManager::AttemptToOfferCardUploadSave( #if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) + int save_card_ui_experiment_arm = + features::kAutofillSaveCardUiExperimentSelectorInNumber.Get(); + // Adding the Save Card UI Experiment to the active experiments in upload - // request if the experiment is active. + // request if the experiment is active. If 3rd save card ui experiment, aka + // Current with Avatar and Email, is selected then we would not add + // AutofillSaveCardUiExperiment to the active experiments list, as we want the + // current footer to be displayed. if (base::FeatureList::IsEnabled(features::kAutofillSaveCardUiExperiment) && - features::kAutofillSaveCardUiExperimentSelectorInNumber.Get() != 0) { + (save_card_ui_experiment_arm == 1 || save_card_ui_experiment_arm == 2)) { upload_request_.active_experiments.push_back( "AutofillSaveCardUiExperiment"); } @@ -1021,18 +1027,17 @@ void CreditCardSaveManager::LogCardUploadDecisions( void CreditCardSaveManager::LogCardUploadDecisionsToAutofillInternals( int upload_decision_metrics) { LogManager* log_manager = client_->GetLogManager(); - if (!log_manager) - return; auto final_decision = (upload_decision_metrics_ & AutofillMetrics::UPLOAD_OFFERED) ? LogMessage::kCardUploadDecisionUploadOffered : LogMessage::kCardUploadDecisionUploadNotOffered; - auto buffer = log_manager->Log(); - buffer << LoggingScope::kCardUploadDecision << final_decision; - buffer << Tag{"div"} << Attrib{"class", "form"} << Tag{"tr"} << Tag{"td"} - << "Decision Metrics:" << CTag{"td"} << Tag{"td"} << Tag{"table"}; + LogBuffer buffer(IsLoggingActive(log_manager)); + LOG_AF(buffer) << LoggingScope::kCardUploadDecision << final_decision; + LOG_AF(buffer) << Tag{"div"} << Attrib{"class", "form"} << Tag{"tr"} + << Tag{"td"} << "Decision Metrics:" << CTag{"td"} << Tag{"td"} + << Tag{"table"}; for (int i = 0; i < AutofillMetrics::kNumCardUploadDecisionMetrics; i++) { AutofillMetrics::CardUploadDecisionMetric currentBitmaskValue = @@ -1100,9 +1105,10 @@ void CreditCardSaveManager::LogCardUploadDecisionsToAutofillInternals( result = "UPLOAD_NOT_OFFERED_INVALID_LEGAL_MESSAGE"; break; } - buffer << Tr{} << result; + LOG_AF(buffer) << Tr{} << result; } - buffer << CTag{"table"} << CTag{"td"} << CTag{"tr"} << CTag{"div"}; + LOG_AF(buffer) << CTag{"table"} << CTag{"td"} << CTag{"tr"} << CTag{"div"}; + LOG_AF(log_manager) << std::move(buffer); } void CreditCardSaveManager::LogSaveCardRequestExpirationDateReasonMetric() { 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 7c2bd346201..93bb8bfdd25 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 @@ -504,7 +504,7 @@ TEST_F(CreditCardSaveManagerTest, CreditCardDisabledDoesNotSave) { FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -513,7 +513,7 @@ TEST_F(CreditCardSaveManagerTest, CreditCardDisabledDoesNotSave) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -541,7 +541,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_OnlyCountryInAddresses) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -551,7 +551,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_OnlyCountryInAddresses) { ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -605,7 +605,7 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_FirstAndLastName) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data with credit card first and last name @@ -616,8 +616,8 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_FirstAndLastName) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -649,7 +649,7 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_LastAndFirstName) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data with credit card first and last name @@ -682,8 +682,8 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_LastAndFirstName) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Master"; - credit_card_form.fields[1].value = u"Flo"; + credit_card_form.fields[0].value = u"Doe"; + credit_card_form.fields[1].value = u"Jane"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -713,7 +713,7 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_ExpirationDateMissing) { FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form. @@ -722,7 +722,7 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_ExpirationDateMissing) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, but don't include a expiration date, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = u""; credit_card_form.fields[3].value = u""; @@ -746,7 +746,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_WithNonFocusableField) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data with non_focusable form field. @@ -758,8 +758,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_WithNonFocusableField) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -793,7 +793,7 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_WithNonFocusableField) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data with non_focusable form field. @@ -805,8 +805,8 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_WithNonFocusableField) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -832,8 +832,8 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -865,8 +865,8 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -896,7 +896,7 @@ TEST_F( FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data with non_focusable form field. @@ -908,8 +908,8 @@ TEST_F( FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -935,7 +935,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data with non_focusable form field. @@ -947,8 +947,8 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -973,8 +973,8 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -998,11 +998,12 @@ TEST_F(CreditCardSaveManagerTest, CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions().with_split_names(true)); // Use the two same forms for FormsSeen to mock the dynamic change forms. - FormsSeen(std::vector<FormData>(2, credit_card_form)); + FormsSeen({credit_card_form}); + FormsSeen({credit_card_form}); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -1030,11 +1031,12 @@ TEST_F(CreditCardSaveManagerTest, CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions().with_split_names(true)); // Use the two same forms for FormsSeen to mock the dynamic change forms. - FormsSeen(std::vector<FormData>(2, credit_card_form)); + FormsSeen({credit_card_form}); + FormsSeen({credit_card_form}); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -1064,7 +1066,7 @@ TEST_F( FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data without any non_focusable form field. @@ -1072,11 +1074,12 @@ TEST_F( CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions().with_split_names(true)); // Use the two same forms for FormsSeen to mock the dynamic change forms. - FormsSeen(std::vector<FormData>(2, credit_card_form)); + FormsSeen({credit_card_form}); + FormsSeen({credit_card_form}); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -1102,7 +1105,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data without any non_focusable form field. @@ -1110,11 +1113,12 @@ TEST_F(CreditCardSaveManagerTest, CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions().with_split_names(true)); // Use the two same forms for FormsSeen to mock the dynamic change forms. - FormsSeen(std::vector<FormData>(2, credit_card_form)); + FormsSeen({credit_card_form}); + FormsSeen({credit_card_form}); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -1140,8 +1144,8 @@ TEST_F(CreditCardSaveManagerTest, SaveCreditCardOptions_WithoutDynamicForms) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -1167,7 +1171,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FirstAndLastName) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data with credit card first and last name @@ -1178,8 +1182,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FirstAndLastName) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -1223,7 +1227,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_LastAndFirstName) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data with credit card first and last name @@ -1256,8 +1260,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_LastAndFirstName) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Master"; - credit_card_form.fields[1].value = u"Flo"; + credit_card_form.fields[0].value = u"Doe"; + credit_card_form.fields[1].value = u"Jane"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -1305,7 +1309,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NotSavedLocally) { test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -1315,7 +1319,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NotSavedLocally) { // Edit the data, and submit. const char* const card_number = "4111111111111111"; - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = ASCIIToUTF16(card_number); credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1336,7 +1340,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FeatureNotEnabled) { FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -1345,7 +1349,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FeatureNotEnabled) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1373,7 +1377,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcUnavailable) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -1383,7 +1387,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcUnavailable) { ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1416,7 +1420,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcInvalidLength) { FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -1425,7 +1429,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcInvalidLength) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1458,7 +1462,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MultipleCvcFields) { FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -1486,7 +1490,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MultipleCvcFields) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1517,7 +1521,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoCvcFieldOnForm) { FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. Note that CVC field is missing. @@ -1541,7 +1545,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoCvcFieldOnForm) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1574,7 +1578,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen({address_form}); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. Note that CVC field is missing. @@ -1600,7 +1604,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen({credit_card_form}); // Enter an invalid cvc in "Random Field" and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1634,7 +1638,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen({address_form}); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. Note that CVC field is missing. @@ -1660,7 +1664,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen({credit_card_form}); // Enter a valid cvc in "Random Field" and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1696,7 +1700,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen({address_form}); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. Note that CVC field is missing. @@ -1722,7 +1726,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen({credit_card_form}); // Enter a valid cvc in "Random Field" and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1758,7 +1762,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoProfileAvailable) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Bob Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1796,7 +1800,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoRecentlyUsedProfile) { test::CreateTestAddressFormData(&address_form); FormsSeen({address_form}); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set the current time to another value. @@ -1808,7 +1812,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoRecentlyUsedProfile) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1847,7 +1851,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1936,7 +1940,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1965,7 +1969,40 @@ TEST_F(CreditCardSaveManagerTest, CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions()); FormsSeen(std::vector<FormData>(1, credit_card_form)); - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; + credit_card_form.fields[1].value = u"4111111111111111"; + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); + credit_card_form.fields[4].value = u"123"; + FormSubmitted(credit_card_form); + + EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); + + std::vector<const char*> active_experiments_in_request = + payments_client_->active_experiments_in_request(); + EXPECT_THAT(active_experiments_in_request, + testing::Not(testing::Contains( + testing::StrEq("AutofillSaveCardUiExperiment")))); +} + +TEST_F( + CreditCardSaveManagerTest, + AttemptToOfferCardUploadSave_SaveCardUiExperimentEnabledWithoutAddedInOutgoingRequest) { + // Setting the flag and params for the save card ui experiment with value 3 as + // we are not getting the updated/experimental TOS for that experiment arm. + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeatureWithParameters( + features::kAutofillSaveCardUiExperiment, + {{"autofill_save_card_ui_experiment_selector_in_number", "3"}}); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions()); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Edit the data, and submit. + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -1975,6 +2012,7 @@ TEST_F(CreditCardSaveManagerTest, EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); + // Confirm that active experiments vector has the correct value. std::vector<const char*> active_experiments_in_request = payments_client_->active_experiments_in_request(); EXPECT_THAT(active_experiments_in_request, @@ -2042,10 +2080,10 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesConflict) { FormsSeen(address_forms); ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); - ManuallyFillAddressForm("Flo", "Master", "77401-8294", "US", &address_form1); + ManuallyFillAddressForm("Jane", "Doe", "77401-8294", "US", &address_form1); FormSubmitted(address_form1); - ManuallyFillAddressForm("Flo", "Master", "77401-1234", "US", &address_form2); + ManuallyFillAddressForm("Jane", "Doe", "77401-1234", "US", &address_form2); FormSubmitted(address_form2); // Set up our credit card form data. @@ -2055,7 +2093,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesConflict) { ExpectFillableFormParsedUkm(3 /* num_fillable_forms_parsed */); // Edit the data and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2089,14 +2127,14 @@ TEST_F(CreditCardSaveManagerTest, // instead of submitting a form, because they're deduped on form submit. AutofillProfile profile1; profile1.set_guid("00000000-0000-0000-0000-000000000001"); - profile1.SetInfo(NAME_FULL, u"Flo Master", "en-US"); + profile1.SetInfo(NAME_FULL, u"Jane Doe", "en-US"); profile1.SetInfo(ADDRESS_HOME_ZIP, u"H3B2Y5", "en-US"); profile1.SetInfo(ADDRESS_HOME_COUNTRY, u"CA", "en-US"); personal_data().AddProfile(profile1); AutofillProfile profile2; profile2.set_guid("00000000-0000-0000-0000-000000000002"); - profile2.SetInfo(NAME_FULL, u"Flo Master", "en-US"); + profile2.SetInfo(NAME_FULL, u"Jane Doe", "en-US"); profile2.SetInfo(ADDRESS_HOME_ZIP, u"h3b 2y5", "en-US"); profile2.SetInfo(ADDRESS_HOME_COUNTRY, u"CA", "en-US"); personal_data().AddProfile(profile2); @@ -2107,7 +2145,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen({credit_card_form}); // Edit the data and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2146,10 +2184,10 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesHavePrefixMatch) { address_forms.push_back(address_form2); FormsSeen(address_forms); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form1); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form1); FormSubmitted(address_form1); - ManuallyFillAddressForm("Flo", "Master", "77401-8294", "US", &address_form2); + ManuallyFillAddressForm("Jane", "Doe", "77401-8294", "US", &address_form2); FormSubmitted(address_form2); // Set up our credit card form data. @@ -2158,7 +2196,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesHavePrefixMatch) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2194,7 +2232,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoZipCodeAvailable) { // other countries which autofill requires a zip code for) would result in no // address being imported at all, and then we never reach the check for // missing zip code in the upload code. - ManuallyFillAddressForm("Flo", "Master", "" /* zip_code */, "Venezuela", + ManuallyFillAddressForm("Jane", "Doe", "" /* zip_code */, "Venezuela", &address_form); FormSubmitted(address_form); @@ -2204,7 +2242,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoZipCodeAvailable) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2239,11 +2277,11 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CCFormHasMiddleInitial) { FormsSeen({address_form1, address_form2}); // Names can be different case. - ManuallyFillAddressForm("flo", "master", "77401", "US", &address_form1); + ManuallyFillAddressForm("jane", "doe", "77401", "US", &address_form1); FormSubmitted(address_form1); // And they can have a middle initial even if the other names don't. - ManuallyFillAddressForm("Flo W", "Master", "77401", "US", &address_form2); + ManuallyFillAddressForm("Jane W", "Doe", "77401", "US", &address_form2); FormSubmitted(address_form2); // Set up our credit card form data. @@ -2253,7 +2291,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CCFormHasMiddleInitial) { // Edit the data, but use the name with a middle initial *and* period, and // submit. - credit_card_form.fields[0].value = u"Flo W. Master"; + credit_card_form.fields[0].value = u"Jane W. Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2285,9 +2323,9 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoMiddleInitialInCCForm) { FormsSeen({address_form1, address_form2}); // Names can have different variations of middle initials. - ManuallyFillAddressForm("flo w.", "master", "77401", "US", &address_form1); + ManuallyFillAddressForm("jane w.", "doe", "77401", "US", &address_form1); FormSubmitted(address_form1); - ManuallyFillAddressForm("Flo W", "Master", "77401", "US", &address_form2); + ManuallyFillAddressForm("Jane W", "Doe", "77401", "US", &address_form2); FormSubmitted(address_form2); // Set up our credit card form data. @@ -2296,7 +2334,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoMiddleInitialInCCForm) { FormsSeen({credit_card_form}); // Edit the data, but do not use middle initial. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2424,10 +2462,10 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NamesCanMismatch) { address_forms.push_back(address_form2); FormsSeen(address_forms); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form1); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form1); FormSubmitted(address_form1); - ManuallyFillAddressForm("Master", "Blaster", "77401", "US", &address_form2); + ManuallyFillAddressForm("John", "Smith", "77401", "US", &address_form2); FormSubmitted(address_form2); // Set up our credit card form data. @@ -2436,7 +2474,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NamesCanMismatch) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, but use yet another name, and submit. - credit_card_form.fields[0].value = u"Bob Master"; + credit_card_form.fields[0].value = u"Different Person"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2479,14 +2517,14 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_IgnoreOldProfiles) { test::CreateTestAddressFormData(&address_form2); FormsSeen({address_form1, address_form2}); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form1); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form1); FormSubmitted(address_form1); // Advance the current time. Since |address_form1| will not be a recently // used address profile, we will not include it in the candidate profiles. test_clock.SetNow(kMuchLaterTime); - ManuallyFillAddressForm("Master", "Blaster", "77401", "US", &address_form2); + ManuallyFillAddressForm("John", "Smith", "77401", "US", &address_form2); FormSubmitted(address_form2); // Set up our credit card form data. @@ -2495,7 +2533,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_IgnoreOldProfiles) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, but use yet another name, and submit. - credit_card_form.fields[0].value = u"Master Blaster"; + credit_card_form.fields[0].value = u"John Smith"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2601,7 +2639,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. FormData credit_card_form; @@ -2610,7 +2648,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2636,7 +2674,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. FormData credit_card_form; @@ -2646,7 +2684,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2671,7 +2709,7 @@ TEST_F( FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -2680,7 +2718,7 @@ TEST_F( FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -2927,7 +2965,7 @@ TEST_F( FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -2936,7 +2974,7 @@ TEST_F( FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, but don't include a expiration date, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = u""; credit_card_form.fields[3].value = u""; @@ -2969,7 +3007,7 @@ TEST_F( FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -2978,7 +3016,7 @@ TEST_F( FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, but don't include a expiration date, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = u""; credit_card_form.fields[3].value = u""; @@ -3042,7 +3080,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -3340,7 +3378,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadDetailsFails) { FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -3349,7 +3387,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadDetailsFails) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -3378,13 +3416,13 @@ TEST_F(CreditCardSaveManagerTest, DuplicateMaskedCreditCard_NoUpload) { FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Add a masked credit card whose |TypeAndLastFourDigits| matches what we will // enter below. CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123"); - test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", + test::SetCreditCardInfo(&credit_card, "Jane Doe", "1111", test::NextMonth().c_str(), test::NextYear().c_str(), "1"); credit_card.SetNetworkForMaskedCard(kVisaCard); @@ -3396,7 +3434,7 @@ TEST_F(CreditCardSaveManagerTest, DuplicateMaskedCreditCard_NoUpload) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4091,7 +4129,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4100,7 +4138,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4189,7 +4227,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4238,7 +4276,7 @@ TEST_F(CreditCardSaveManagerTest, // Set up a new address profile without a postal code. AutofillProfile profile; profile.set_guid("00000000-0000-0000-0000-000000000200"); - profile.SetInfo(NAME_FULL, u"Flo Master", "en-US"); + profile.SetInfo(NAME_FULL, u"Jane Doe", "en-US"); profile.SetInfo(ADDRESS_HOME_LINE1, u"123 Testing St.", "en-US"); profile.SetInfo(ADDRESS_HOME_CITY, u"Mountain View", "en-US"); profile.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US"); @@ -4251,7 +4289,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4286,7 +4324,7 @@ TEST_F(CreditCardSaveManagerTest, // Set up two new address profiles with conflicting postal codes. AutofillProfile profile1; profile1.set_guid("00000000-0000-0000-0000-000000000200"); - profile1.SetInfo(NAME_FULL, u"Flo Master", "en-US"); + profile1.SetInfo(NAME_FULL, u"Jane Doe", "en-US"); profile1.SetInfo(ADDRESS_HOME_LINE1, u"123 Testing St.", "en-US"); profile1.SetInfo(ADDRESS_HOME_CITY, u"Mountain View", "en-US"); profile1.SetInfo(ADDRESS_HOME_STATE, u"California", "en-US"); @@ -4295,7 +4333,7 @@ TEST_F(CreditCardSaveManagerTest, personal_data().AddProfile(profile1); AutofillProfile profile2; profile2.set_guid("00000000-0000-0000-0000-000000000201"); - profile2.SetInfo(NAME_FULL, u"Flo Master", "en-US"); + profile2.SetInfo(NAME_FULL, u"Jane Doe", "en-US"); profile2.SetInfo(ADDRESS_HOME_LINE1, u"234 Other Place", "en-US"); profile2.SetInfo(ADDRESS_HOME_CITY, u"Fake City", "en-US"); profile2.SetInfo(ADDRESS_HOME_STATE, u"Stateland", "en-US"); @@ -4309,7 +4347,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4397,7 +4435,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) { // Add a local credit card whose |TypeAndLastFourDigits| matches what we will // enter below. CreditCard local_card; - test::SetCreditCardInfo(&local_card, "Flo Master", "4111111111111111", + test::SetCreditCardInfo(&local_card, "Jane Doe", "4111111111111111", test::NextMonth().c_str(), test::NextYear().c_str(), "1"); local_card.set_record_type(CreditCard::LOCAL_CARD); @@ -4410,7 +4448,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4420,7 +4458,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) { ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4450,7 +4488,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfNewCard) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4460,7 +4498,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfNewCard) { ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4493,7 +4531,7 @@ TEST_F(CreditCardSaveManagerTest, // Add a local credit card whose |TypeAndLastFourDigits| matches what we will // enter below. CreditCard local_card; - test::SetCreditCardInfo(&local_card, "Flo Master", "4111111111111111", + test::SetCreditCardInfo(&local_card, "Jane Doe", "4111111111111111", test::NextMonth().c_str(), test::NextYear().c_str(), "1"); local_card.set_record_type(CreditCard::LOCAL_CARD); @@ -4506,7 +4544,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4516,7 +4554,7 @@ TEST_F(CreditCardSaveManagerTest, ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4542,7 +4580,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4551,7 +4589,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4572,7 +4610,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4581,7 +4619,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4606,7 +4644,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4615,7 +4653,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4634,7 +4672,7 @@ TEST_F(CreditCardSaveManagerTest, FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4643,7 +4681,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4674,7 +4712,7 @@ TEST_F(CreditCardSaveManagerTest, ExpectFillableFormParsedUkm(1 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4713,7 +4751,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4723,7 +4761,7 @@ TEST_F(CreditCardSaveManagerTest, ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4765,7 +4803,7 @@ TEST_F(CreditCardSaveManagerTest, ExpectFillableFormParsedUkm(1 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4805,7 +4843,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MaxStrikesDisallowsSave) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4815,7 +4853,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MaxStrikesDisallowsSave) { ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4863,7 +4901,7 @@ TEST_F(CreditCardSaveManagerTest, ExpectFillableFormParsedUkm(1 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4904,7 +4942,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MaxStrikesStillAllowsSave) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -4914,7 +4952,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MaxStrikesStillAllowsSave) { ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -4956,7 +4994,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data with credit card first and last name @@ -4967,8 +5005,8 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -5004,7 +5042,7 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data with credit card first and last name @@ -5015,8 +5053,8 @@ TEST_F(CreditCardSaveManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo"; - credit_card_form.fields[1].value = u"Master"; + credit_card_form.fields[0].value = u"Jane"; + credit_card_form.fields[1].value = u"Doe"; credit_card_form.fields[2].value = u"4111111111111111"; credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); @@ -5051,7 +5089,7 @@ TEST_F(CreditCardSaveManagerTest, LocallySaveCreditCard_ClearStrikesOnAdd) { ExpectFillableFormParsedUkm(1 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -5082,7 +5120,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ClearStrikesOnAdd) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -5092,7 +5130,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ClearStrikesOnAdd) { ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -5125,7 +5163,7 @@ TEST_F(CreditCardSaveManagerTest, LocallySaveCreditCard_NumStrikesLoggedOnAdd) { ExpectFillableFormParsedUkm(1 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -5160,7 +5198,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NumStrikesLoggedOnAdd) { FormsSeen(std::vector<FormData>(1, address_form)); ExpectUniqueFillableFormParsedUkm(); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -5170,7 +5208,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NumStrikesLoggedOnAdd) { ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -5234,7 +5272,7 @@ TEST_F(CreditCardSaveManagerTest, UploadSaveNotOfferedForUnsupportedCard) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"5454545454545454"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -5262,14 +5300,14 @@ TEST_F(CreditCardSaveManagerTest, LocalSaveNotOfferedForSavedUnsupportedCard) { // Add a local credit card whose number matches what we will // enter below. CreditCard local_card; - test::SetCreditCardInfo(&local_card, "Flo Master", "5454545454545454", + test::SetCreditCardInfo(&local_card, "Jane Doe", "5454545454545454", test::NextMonth().c_str(), test::NextYear().c_str(), "1"); local_card.set_record_type(CreditCard::LOCAL_CARD); personal_data().AddCreditCard(local_card); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"5454545454545454"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -5294,7 +5332,7 @@ TEST_F(CreditCardSaveManagerTest, UploadSaveOfferedForSupportedCard) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = u"4111111111111111"; credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -5318,7 +5356,7 @@ TEST_F(CreditCardSaveManagerTest, InvalidLegalMessageInOnDidGetUploadDetails) { test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -5328,7 +5366,7 @@ TEST_F(CreditCardSaveManagerTest, InvalidLegalMessageInOnDidGetUploadDetails) { // Edit the data, and submit. const char* const card_number = "4111111111111111"; - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = ASCIIToUTF16(card_number); credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); @@ -5356,7 +5394,7 @@ TEST_F(CreditCardSaveManagerTest, LegalMessageInOnDidGetUploadDetails) { test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); - ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + ManuallyFillAddressForm("Jane", "Doe", "77401", "US", &address_form); FormSubmitted(address_form); // Set up our credit card form data. @@ -5366,7 +5404,7 @@ TEST_F(CreditCardSaveManagerTest, LegalMessageInOnDidGetUploadDetails) { // Edit the data, and submit. const char* const card_number = "4111111111111111"; - credit_card_form.fields[0].value = u"Flo Master"; + credit_card_form.fields[0].value = u"Jane Doe"; credit_card_form.fields[1].value = ASCIIToUTF16(card_number); credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc index 4909a677287..9566ce385cf 100644 --- a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc +++ b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc @@ -19,6 +19,7 @@ #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/form_data_importer.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" +#include "components/autofill/core/browser/metrics/payments/local_card_migration_metrics.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/payments/payments_util.h" #include "components/autofill/core/browser/personal_data_manager.h" @@ -59,22 +60,22 @@ bool LocalCardMigrationManager::ShouldOfferLocalCardMigration( switch (imported_credit_card_record_type_) { case FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD: local_card_migration_origin_ = - AutofillMetrics::LocalCardMigrationOrigin::UseOfLocalCard; + autofill_metrics::LocalCardMigrationOrigin::UseOfLocalCard; break; case FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD: local_card_migration_origin_ = - AutofillMetrics::LocalCardMigrationOrigin::UseOfServerCard; + autofill_metrics::LocalCardMigrationOrigin::UseOfServerCard; break; default: - AutofillMetrics::LogLocalCardMigrationDecisionMetric( - AutofillMetrics::LocalCardMigrationDecisionMetric:: + autofill_metrics::LogLocalCardMigrationDecisionMetric( + autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_USE_NEW_CARD); return false; } if (!IsCreditCardMigrationEnabled()) { - AutofillMetrics::LogLocalCardMigrationDecisionMetric( - AutofillMetrics::LocalCardMigrationDecisionMetric:: + autofill_metrics::LogLocalCardMigrationDecisionMetric( + autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_FAILED_PREREQUISITES); return false; } @@ -83,16 +84,16 @@ bool LocalCardMigrationManager::ShouldOfferLocalCardMigration( if (GetLocalCardMigrationStrikeDatabase()->ShouldBlockFeature()) { switch (imported_credit_card_record_type_) { case FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD: - AutofillMetrics::LogLocalCardMigrationNotOfferedDueToMaxStrikesMetric( + autofill_metrics::LogLocalCardMigrationNotOfferedDueToMaxStrikesMetric( AutofillMetrics::SaveTypeMetric::LOCAL); break; case FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD: - AutofillMetrics::LogLocalCardMigrationNotOfferedDueToMaxStrikesMetric( + autofill_metrics::LogLocalCardMigrationNotOfferedDueToMaxStrikesMetric( AutofillMetrics::SaveTypeMetric::SERVER); break; } - AutofillMetrics::LogLocalCardMigrationDecisionMetric( - AutofillMetrics::LocalCardMigrationDecisionMetric:: + autofill_metrics::LogLocalCardMigrationDecisionMetric( + autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_REACHED_MAX_STRIKE_COUNT); return false; } @@ -114,13 +115,13 @@ bool LocalCardMigrationManager::ShouldOfferLocalCardMigration( } else if (imported_credit_card_record_type_ == FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD && migratable_credit_cards_.size() == 1) { - AutofillMetrics::LogLocalCardMigrationDecisionMetric( - AutofillMetrics::LocalCardMigrationDecisionMetric:: + autofill_metrics::LogLocalCardMigrationDecisionMetric( + autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_SINGLE_LOCAL_CARD); return false; } else { - AutofillMetrics::LogLocalCardMigrationDecisionMetric( - AutofillMetrics::LocalCardMigrationDecisionMetric:: + autofill_metrics::LogLocalCardMigrationDecisionMetric( + autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_NO_MIGRATABLE_CARDS); return false; } @@ -153,9 +154,9 @@ void LocalCardMigrationManager::AttemptToOfferLocalCardMigration( // Call ShowMainMigrationDialog() to pop up a larger, modal dialog showing the // local cards to be uploaded. void LocalCardMigrationManager::OnUserAcceptedIntermediateMigrationDialog() { - AutofillMetrics::LogLocalCardMigrationPromptMetric( + autofill_metrics::LogLocalCardMigrationPromptMetric( local_card_migration_origin_, - AutofillMetrics::INTERMEDIATE_BUBBLE_ACCEPTED); + autofill_metrics::INTERMEDIATE_BUBBLE_ACCEPTED); ShowMainMigrationDialog(); } @@ -163,8 +164,8 @@ void LocalCardMigrationManager::OnUserAcceptedIntermediateMigrationDialog() { void LocalCardMigrationManager::OnUserAcceptedMainMigrationDialog( const std::vector<std::string>& selected_card_guids) { user_accepted_main_migration_dialog_ = true; - AutofillMetrics::LogLocalCardMigrationPromptMetric( - local_card_migration_origin_, AutofillMetrics::MAIN_DIALOG_ACCEPTED); + autofill_metrics::LogLocalCardMigrationPromptMetric( + local_card_migration_origin_, autofill_metrics::MAIN_DIALOG_ACCEPTED); // Log number of LocalCardMigration strikes when migration was accepted. base::UmaHistogramCounts1000( @@ -211,8 +212,8 @@ void LocalCardMigrationManager::OnDidGetUploadDetails( /*escape_apostrophes=*/true); if (legal_message_lines_.empty()) { - AutofillMetrics::LogLocalCardMigrationDecisionMetric( - AutofillMetrics::LocalCardMigrationDecisionMetric:: + autofill_metrics::LogLocalCardMigrationDecisionMetric( + autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_INVALID_LEGAL_MESSAGE); return; } @@ -226,7 +227,7 @@ void LocalCardMigrationManager::OnDidGetUploadDetails( if (is_from_settings_page) { // Set the origin to SettingsPage. local_card_migration_origin_ = - AutofillMetrics::LocalCardMigrationOrigin::SettingsPage; + autofill_metrics::LocalCardMigrationOrigin::SettingsPage; // Pops up a larger, modal dialog showing the local cards to be uploaded. ShowMainMigrationDialog(); } else { @@ -240,8 +241,8 @@ void LocalCardMigrationManager::OnDidGetUploadDetails( !payments::IsCreditCardNumberSupported( imported_credit_card_number_.value(), supported_card_bin_ranges)) { - AutofillMetrics::LogLocalCardMigrationDecisionMetric( - AutofillMetrics::LocalCardMigrationDecisionMetric:: + autofill_metrics::LogLocalCardMigrationDecisionMetric( + autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_USE_UNSUPPORTED_LOCAL_CARD); return; } @@ -249,17 +250,17 @@ void LocalCardMigrationManager::OnDidGetUploadDetails( FilterOutUnsupportedLocalCards(supported_card_bin_ranges); // Abandon the migration if no supported card left. if (migratable_credit_cards_.empty()) { - AutofillMetrics::LogLocalCardMigrationDecisionMetric( - AutofillMetrics::LocalCardMigrationDecisionMetric:: + autofill_metrics::LogLocalCardMigrationDecisionMetric( + autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_NO_SUPPORTED_CARDS); return; } client_->ShowLocalCardMigrationDialog(base::BindOnce( &LocalCardMigrationManager::OnUserAcceptedIntermediateMigrationDialog, weak_ptr_factory_.GetWeakPtr())); - AutofillMetrics::LogLocalCardMigrationPromptMetric( + autofill_metrics::LogLocalCardMigrationPromptMetric( local_card_migration_origin_, - AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN); + autofill_metrics::INTERMEDIATE_BUBBLE_SHOWN); } // TODO(crbug.com/876895): Clean up the LoadRiskData Bind/BindRepeating @@ -267,11 +268,11 @@ void LocalCardMigrationManager::OnDidGetUploadDetails( client_->LoadRiskData(base::BindRepeating( &LocalCardMigrationManager::OnDidGetMigrationRiskData, weak_ptr_factory_.GetWeakPtr())); - AutofillMetrics::LogLocalCardMigrationDecisionMetric( - AutofillMetrics::LocalCardMigrationDecisionMetric::OFFERED); + autofill_metrics::LogLocalCardMigrationDecisionMetric( + autofill_metrics::LocalCardMigrationDecisionMetric::OFFERED); } else { - AutofillMetrics::LogLocalCardMigrationDecisionMetric( - AutofillMetrics::LocalCardMigrationDecisionMetric:: + autofill_metrics::LogLocalCardMigrationDecisionMetric( + autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED); } } @@ -378,8 +379,8 @@ LocalCardMigrationManager::GetLocalCardMigrationStrikeDatabase() { // OnUserAcceptedMainMigrationDialog(). Can be called when user agrees to // migration on the intermediate dialog or directly from settings page. void LocalCardMigrationManager::ShowMainMigrationDialog() { - AutofillMetrics::LogLocalCardMigrationPromptMetric( - local_card_migration_origin_, AutofillMetrics::MAIN_DIALOG_SHOWN); + autofill_metrics::LogLocalCardMigrationPromptMetric( + local_card_migration_origin_, autofill_metrics::MAIN_DIALOG_SHOWN); // Pops up a larger, modal dialog showing the local cards to be uploaded. client_->ConfirmMigrateLocalCardToCloud( legal_message_lines_, diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.h b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.h index 24d6855e256..defdf46fb25 100644 --- a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.h +++ b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.h @@ -15,6 +15,7 @@ #include "base/memory/raw_ptr.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" +#include "components/autofill/core/browser/metrics/payments/local_card_migration_metrics.h" #include "components/autofill/core/browser/payments/legal_message_line.h" #include "components/autofill/core/browser/payments/local_card_migration_strike_database.h" #include "components/autofill/core/browser/payments/payments_client.h" @@ -249,7 +250,7 @@ class LocalCardMigrationManager { bool user_accepted_main_migration_dialog_ = false; // Record the triggering source of the local card migration. - AutofillMetrics::LocalCardMigrationOrigin local_card_migration_origin_; + autofill_metrics::LocalCardMigrationOrigin local_card_migration_origin_; // Initialized only during tests. raw_ptr<ObserverForTest> observer_for_testing_ = nullptr; 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 3182b70031e..612b8ff95d1 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/metrics/autofill_metrics.h" +#include "components/autofill/core/browser/metrics/payments/local_card_migration_metrics.h" #include "components/autofill/core/browser/payments/payments_customer_data.h" #include "components/autofill/core/browser/payments/payments_util.h" #include "components/autofill/core/browser/payments/test_credit_card_save_manager.h" @@ -162,7 +163,7 @@ class LocalCardMigrationManagerTest : public testing::Test { // Verify that the correct histogram entry (and only that) was logged. void ExpectUniqueLocalCardMigrationDecision( const base::HistogramTester& histogram_tester, - AutofillMetrics::LocalCardMigrationDecisionMetric metric) { + autofill_metrics::LocalCardMigrationDecisionMetric metric) { histogram_tester.ExpectUniqueSample("Autofill.LocalCardMigrationDecision", metric, 1); } @@ -174,12 +175,12 @@ class LocalCardMigrationManagerTest : public testing::Test { std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456")); // Add a local credit card (but it will not match what we will enter below). - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card (but it will not match what we will enter // below). - AddLocalCreditCard(personal_data(), "Flo Master", "4444333322221111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4444333322221111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -189,7 +190,7 @@ class LocalCardMigrationManagerTest : public testing::Test { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "5555555555554444", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); } @@ -202,11 +203,11 @@ class LocalCardMigrationManagerTest : public testing::Test { // Add a local credit card whose |TypeAndLastFourDigits| matches what we // will enter below. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card. - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -216,7 +217,7 @@ class LocalCardMigrationManagerTest : public testing::Test { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); } @@ -229,15 +230,15 @@ class LocalCardMigrationManagerTest : public testing::Test { // Add a local credit card whose |TypeAndLastFourDigits| matches what we // will enter below. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add other invalid local credit cards (invalid card number or expired), so // it will not trigger migration. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111112", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111112", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::LastYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -247,7 +248,7 @@ class LocalCardMigrationManagerTest : public testing::Test { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); } @@ -262,12 +263,12 @@ class LocalCardMigrationManagerTest : public testing::Test { // what we will enter below. CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, /*server_id=*/"a123"); - test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", + test::SetCreditCardInfo(&credit_card, "Jane Doe", "1111", "11", test::NextYear().c_str(), "1"); credit_card.SetNetworkForMaskedCard(kVisaCard); personal_data().AddServerCreditCard(credit_card); // Add one valid local credit card, so it will trigger migration - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -277,7 +278,7 @@ class LocalCardMigrationManagerTest : public testing::Test { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); } @@ -292,16 +293,16 @@ class LocalCardMigrationManagerTest : public testing::Test { // will enter below. CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, /*server_id=*/"a123"); - test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", + test::SetCreditCardInfo(&credit_card, "Jane Doe", "1111", "11", test::NextYear().c_str(), "1"); credit_card.SetNetworkForMaskedCard(kVisaCard); personal_data().AddServerCreditCard(credit_card); // Add other invalid local credit cards (invalid card number or expired), so // it will not trigger migration. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111112", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111112", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::LastYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -311,7 +312,7 @@ class LocalCardMigrationManagerTest : public testing::Test { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); } @@ -347,7 +348,7 @@ TEST_F(LocalCardMigrationManagerTest, // Add a local credit card whose |TypeAndLastFourDigits| matches what we will // enter below. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -357,7 +358,7 @@ TEST_F(LocalCardMigrationManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered()); @@ -430,11 +431,11 @@ TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_SignInOnly) { TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_NoPaymentsAccount) { // Add a local credit card whose |TypeAndLastFourDigits| matches what we will // enter below. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card. - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -444,7 +445,7 @@ TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_NoPaymentsAccount) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered()); @@ -462,17 +463,17 @@ TEST_F(LocalCardMigrationManagerTest, // Add a masked server card whose |TypeAndLastFourDigits| matches a local // card. CreditCard server_card(CreditCard::MASKED_SERVER_CARD, "a123"); - test::SetCreditCardInfo(&server_card, "Flo Master", "1111", "11", + test::SetCreditCardInfo(&server_card, "Jane Doe", "1111", "11", test::NextYear().c_str(), "1"); server_card.SetNetworkForMaskedCard(kVisaCard); personal_data().AddServerCreditCard(server_card); // Add a local card whose |TypeAndLastFourDigits| matches a masked server // card. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -482,7 +483,7 @@ TEST_F(LocalCardMigrationManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "5555555555554444", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered()); @@ -499,15 +500,15 @@ TEST_F(LocalCardMigrationManagerTest, // Add a full server card whose number matches a local card. CreditCard server_card(CreditCard::FULL_SERVER_CARD, "a123"); - test::SetCreditCardInfo(&server_card, "Flo Master", "4111111111111111", "11", + test::SetCreditCardInfo(&server_card, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1"); personal_data().AddServerCreditCard(server_card); // Add a local credit card whose number matches a full server card. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -517,7 +518,7 @@ TEST_F(LocalCardMigrationManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "5555555555554444", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered()); @@ -532,7 +533,7 @@ TEST_F(LocalCardMigrationManagerTest, GetDetectedValues_AllWithCardHolderName) { // Add a local credit card whose |TypeAndLastFourDigits| matches what we will // enter below. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card with a different cardholder name. @@ -546,7 +547,7 @@ TEST_F(LocalCardMigrationManagerTest, GetDetectedValues_AllWithCardHolderName) { FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered()); @@ -565,7 +566,7 @@ TEST_F(LocalCardMigrationManagerTest, // Add a local credit card whose |TypeAndLastFourDigits| matches what we will // enter below. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card without card holder name. @@ -578,7 +579,7 @@ TEST_F(LocalCardMigrationManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered()); @@ -644,7 +645,7 @@ TEST_F(LocalCardMigrationManagerTest, // Add a local credit card. One migratable credit card will still trigger // migration on settings page. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -672,7 +673,7 @@ TEST_F(LocalCardMigrationManagerTest, // Add a local credit card. One migratable credit card will still trigger // migration on settings page. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -704,7 +705,7 @@ TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_MigrationSuccess) { std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456")); // Add a local credit card for migration. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -746,7 +747,7 @@ TEST_F(LocalCardMigrationManagerTest, // Add a local credit card. One migratable credit card will still trigger // migration on settings page. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -789,7 +790,7 @@ TEST_F(LocalCardMigrationManagerTest, // Add a local credit card. One migratable credit card will still trigger // migration on settings page. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -824,11 +825,11 @@ TEST_F(LocalCardMigrationManagerTest, // Verify selected cards are correctly passed to manager. TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_ToggleIsChosen) { const base::GUID guid1 = base::GUID::GenerateRandomV4(); - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", guid1); const base::GUID guid2 = base::GUID::GenerateRandomV4(); - AddLocalCreditCard(personal_data(), "Flo Master", "5454545454545454", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5454545454545454", "11", test::NextYear().c_str(), "1", guid2); // Set the billing_customer_number to designate existence of a Payments @@ -853,7 +854,7 @@ TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_ToggleIsChosen) { TEST_F(LocalCardMigrationManagerTest, DeleteLocalCardViaMigrationDialog) { const base::GUID guid = base::GUID::GenerateRandomV4(); - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", guid); const std::string guid_str = guid.AsLowercaseString(); @@ -919,11 +920,11 @@ TEST_F(LocalCardMigrationManagerTest, // strike count is logged. TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_StrikeCountUMALogged) { const base::GUID guid1 = base::GUID::GenerateRandomV4(); - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", guid1); const base::GUID guid2 = base::GUID::GenerateRandomV4(); - AddLocalCreditCard(personal_data(), "Flo Master", "5454545454545454", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5454545454545454", "11", test::NextYear().c_str(), "1", guid2); // Set the billing_customer_number to designate existence of a Payments @@ -962,11 +963,11 @@ TEST_F(LocalCardMigrationManagerTest, // Add a local credit card whose |TypeAndLastFourDigits| matches what we will // enter below. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card. - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -982,7 +983,7 @@ TEST_F(LocalCardMigrationManagerTest, payments_client_->SetSupportedBINRanges(supported_card_bin_ranges); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); EXPECT_FALSE(local_card_migration_manager_->IntermediatePromptWasShown()); @@ -999,11 +1000,11 @@ TEST_F(LocalCardMigrationManagerTest, // Add a local credit card whose |TypeAndLastFourDigits| matches what we will // enter below. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card. - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -1019,7 +1020,7 @@ TEST_F(LocalCardMigrationManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); @@ -1046,12 +1047,12 @@ TEST_F( // Add a masked server credit card whose |TypeAndLastFourDigits| matches what // we will enter below. CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123"); - test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", + test::SetCreditCardInfo(&credit_card, "Jane Doe", "1111", "11", test::NextYear().c_str(), "1"); credit_card.SetNetworkForMaskedCard(kVisaCard); personal_data().AddServerCreditCard(credit_card); // Add one valid local credit card, so it will trigger migration - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -1067,7 +1068,7 @@ TEST_F( FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); @@ -1087,12 +1088,12 @@ TEST_F( // Add a masked server credit card whose |TypeAndLastFourDigits| matches what // we will enter below. CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123"); - test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", + test::SetCreditCardInfo(&credit_card, "Jane Doe", "1111", "11", test::NextYear().c_str(), "1"); credit_card.SetNetworkForMaskedCard(kVisaCard); personal_data().AddServerCreditCard(credit_card); // Add one valid local credit card, so it will trigger migration - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -1108,7 +1109,7 @@ TEST_F( FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); @@ -1139,16 +1140,16 @@ TEST_F(LocalCardMigrationManagerTest, // sub-histogram. histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.UseOfLocalCard", - AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 1); + autofill_metrics::INTERMEDIATE_BUBBLE_SHOWN, 1); histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.UseOfLocalCard", - AutofillMetrics::INTERMEDIATE_BUBBLE_ACCEPTED, 1); + autofill_metrics::INTERMEDIATE_BUBBLE_ACCEPTED, 1); histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.UseOfLocalCard", - AutofillMetrics::MAIN_DIALOG_SHOWN, 1); + autofill_metrics::MAIN_DIALOG_SHOWN, 1); histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.UseOfLocalCard", - AutofillMetrics::MAIN_DIALOG_ACCEPTED, 1); + autofill_metrics::MAIN_DIALOG_ACCEPTED, 1); } // Using a server card when any number of local cards are eligible for migration @@ -1163,16 +1164,16 @@ TEST_F(LocalCardMigrationManagerTest, // sub-histogram. histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.UseOfServerCard", - AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 1); + autofill_metrics::INTERMEDIATE_BUBBLE_SHOWN, 1); histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.UseOfServerCard", - AutofillMetrics::INTERMEDIATE_BUBBLE_ACCEPTED, 1); + autofill_metrics::INTERMEDIATE_BUBBLE_ACCEPTED, 1); histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.UseOfServerCard", - AutofillMetrics::MAIN_DIALOG_SHOWN, 1); + autofill_metrics::MAIN_DIALOG_SHOWN, 1); histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.UseOfServerCard", - AutofillMetrics::MAIN_DIALOG_ACCEPTED, 1); + autofill_metrics::MAIN_DIALOG_ACCEPTED, 1); } // Using a server card will not trigger migration even if there are other local @@ -1200,7 +1201,7 @@ TEST_F(LocalCardMigrationManagerTest, // Add a local credit card. One migratable credit card will still trigger // migration on settings page. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -1213,16 +1214,16 @@ TEST_F(LocalCardMigrationManagerTest, // Triggering from settings page won't show intermediate bubble. histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.SettingsPage", - AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN, 0); + autofill_metrics::INTERMEDIATE_BUBBLE_SHOWN, 0); histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.SettingsPage", - AutofillMetrics::INTERMEDIATE_BUBBLE_ACCEPTED, 0); + autofill_metrics::INTERMEDIATE_BUBBLE_ACCEPTED, 0); histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.SettingsPage", - AutofillMetrics::MAIN_DIALOG_SHOWN, 1); + autofill_metrics::MAIN_DIALOG_SHOWN, 1); histogram_tester.ExpectBucketCount( "Autofill.LocalCardMigrationOrigin.SettingsPage", - AutofillMetrics::MAIN_DIALOG_ACCEPTED, 1); + autofill_metrics::MAIN_DIALOG_ACCEPTED, 1); } // Use new card when submit so migration was not offered. Verify the migration @@ -1232,7 +1233,7 @@ TEST_F(LocalCardMigrationManagerTest, LogMigrationDecisionMetric_UseNewCard) { UseNewCardWithLocalCardsOnFile(); ExpectUniqueLocalCardMigrationDecision( - histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + histogram_tester, autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_USE_NEW_CARD); } @@ -1244,11 +1245,11 @@ TEST_F(LocalCardMigrationManagerTest, base::HistogramTester histogram_tester; // Add a local credit card whose |TypeAndLastFourDigits| matches what we will // enter below. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card. - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -1258,12 +1259,12 @@ TEST_F(LocalCardMigrationManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); ExpectUniqueLocalCardMigrationDecision( - histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + histogram_tester, autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_FAILED_PREREQUISITES); } @@ -1283,7 +1284,7 @@ TEST_F(LocalCardMigrationManagerTest, UseLocalCardWithOtherLocalCardsOnFile(); ExpectUniqueLocalCardMigrationDecision( - histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + histogram_tester, autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_REACHED_MAX_STRIKE_COUNT); } @@ -1296,7 +1297,7 @@ TEST_F(LocalCardMigrationManagerTest, UseLocalCardWithInvalidLocalCardsOnFile(); ExpectUniqueLocalCardMigrationDecision( - histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + histogram_tester, autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_SINGLE_LOCAL_CARD); } @@ -1308,7 +1309,7 @@ TEST_F(LocalCardMigrationManagerTest, UseServerCardWithInvalidLocalCardsOnFile(); ExpectUniqueLocalCardMigrationDecision( - histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + histogram_tester, autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_NO_MIGRATABLE_CARDS); } @@ -1324,7 +1325,7 @@ TEST_F(LocalCardMigrationManagerTest, UseLocalCardWithOtherLocalCardsOnFile(); ExpectUniqueLocalCardMigrationDecision( - histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + histogram_tester, autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED); } @@ -1340,11 +1341,11 @@ TEST_F(LocalCardMigrationManagerTest, // Add a local credit card whose |TypeAndLastFourDigits| matches what we will // enter below. - AddLocalCreditCard(personal_data(), "Flo Master", "4111111111111111", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); // Add another local credit card. - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -1361,12 +1362,12 @@ TEST_F(LocalCardMigrationManagerTest, payments_client_->SetSupportedBINRanges(supported_card_bin_ranges); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); ExpectUniqueLocalCardMigrationDecision( - histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + histogram_tester, autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_USE_UNSUPPORTED_LOCAL_CARD); } @@ -1383,12 +1384,12 @@ TEST_F(LocalCardMigrationManagerTest, // Add a masked server credit card whose |TypeAndLastFourDigits| matches what // we will enter below. CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123"); - test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", + test::SetCreditCardInfo(&credit_card, "Jane Doe", "1111", "11", test::NextYear().c_str(), "1"); credit_card.SetNetworkForMaskedCard(kVisaCard); personal_data().AddServerCreditCard(credit_card); // Add one valid local credit card, so it will trigger migration - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -1405,12 +1406,12 @@ TEST_F(LocalCardMigrationManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); ExpectUniqueLocalCardMigrationDecision( - histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + histogram_tester, autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_NO_SUPPORTED_CARDS); } @@ -1427,12 +1428,12 @@ TEST_F(LocalCardMigrationManagerTest, // Add a masked server credit card whose |TypeAndLastFourDigits| matches what // we will enter below. CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123"); - test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", + test::SetCreditCardInfo(&credit_card, "Jane Doe", "1111", "11", test::NextYear().c_str(), "1"); credit_card.SetNetworkForMaskedCard(kVisaCard); personal_data().AddServerCreditCard(credit_card); // Add one valid local credit card, so it will trigger migration - AddLocalCreditCard(personal_data(), "Flo Master", "5555555555554444", "11", + AddLocalCreditCard(personal_data(), "Jane Doe", "5555555555554444", "11", test::NextYear().c_str(), "1", base::GUID::GenerateRandomV4()); @@ -1449,12 +1450,12 @@ TEST_F(LocalCardMigrationManagerTest, FormsSeen(std::vector<FormData>(1, credit_card_form)); // Edit the data, and submit. - EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11", + EditCreditCardFrom(credit_card_form, "Jane Doe", "4111111111111111", "11", test::NextYear().c_str(), "123"); FormSubmitted(credit_card_form); ExpectUniqueLocalCardMigrationDecision( - histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + histogram_tester, autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_NO_SUPPORTED_CARDS); } @@ -1467,7 +1468,7 @@ TEST_F(LocalCardMigrationManagerTest, ExpectUniqueLocalCardMigrationDecision( histogram_tester, - AutofillMetrics::LocalCardMigrationDecisionMetric::OFFERED); + autofill_metrics::LocalCardMigrationDecisionMetric::OFFERED); } // Tests that if payment client returns an invalid legal message migration @@ -1481,7 +1482,7 @@ TEST_F(LocalCardMigrationManagerTest, // Verify that the correct histogram entries were logged. ExpectUniqueLocalCardMigrationDecision( - histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + histogram_tester, autofill_metrics::LocalCardMigrationDecisionMetric:: NOT_OFFERED_INVALID_LEGAL_MESSAGE); } diff --git a/chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc b/chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc index 646142d1311..5d353d366fb 100644 --- a/chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc +++ b/chromium/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc @@ -163,10 +163,6 @@ void UploadCardRequest::ParseResponse(const base::Value& response) { if (base::FeatureList::IsEnabled( features:: kAutofillEnableGetDetailsForEnrollParsingInUploadCardResponse) && - !base::FeatureList::IsEnabled( - features::kAutofillEnableToolbarStatusChip) && - !base::FeatureList::IsEnabled( - features::kAutofillCreditCardUploadFeedback) && upload_card_response_details_.virtual_card_enrollment_state == CreditCard::VirtualCardEnrollmentState::UNENROLLED_AND_ELIGIBLE) { const auto* virtual_card_enrollment_data = diff --git a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc index 4a208e67f59..57afdaeca90 100644 --- a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc +++ b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc @@ -77,6 +77,7 @@ void VirtualCardEnrollmentManager::InitVirtualCardEnroll( ShouldBlockVirtualCardEnrollment( base::NumberToString(credit_card.instrument_id()), virtual_card_enrollment_source)) { + Reset(); return; } @@ -120,7 +121,9 @@ void VirtualCardEnrollmentManager::OnCardSavedAnimationComplete() { } } -void VirtualCardEnrollmentManager::Enroll() { +void VirtualCardEnrollmentManager::Enroll( + absl::optional<VirtualCardEnrollmentUpdateResponseCallback> + virtual_card_enrollment_update_response_callback) { LogUpdateVirtualCardEnrollmentRequestAttempt( state_.virtual_card_enrollment_fields.virtual_card_enrollment_source, VirtualCardEnrollmentRequestType::kEnroll); @@ -136,6 +139,9 @@ void VirtualCardEnrollmentManager::Enroll() { state_.virtual_card_enrollment_fields.credit_card.instrument_id(); request_details.vcn_context_token = state_.vcn_context_token; + virtual_card_enrollment_update_response_callback_ = + std::move(virtual_card_enrollment_update_response_callback); + payments_client_->UpdateVirtualCardEnrollment( request_details, base::BindOnce(&VirtualCardEnrollmentManager:: @@ -149,7 +155,10 @@ void VirtualCardEnrollmentManager::Enroll() { } } -void VirtualCardEnrollmentManager::Unenroll(int64_t instrument_id) { +void VirtualCardEnrollmentManager::Unenroll( + int64_t instrument_id, + absl::optional<VirtualCardEnrollmentUpdateResponseCallback> + virtual_card_enrollment_update_response_callback) { LogUpdateVirtualCardEnrollmentRequestAttempt( VirtualCardEnrollmentSource::kSettingsPage, VirtualCardEnrollmentRequestType::kUnenroll); @@ -169,6 +178,9 @@ void VirtualCardEnrollmentManager::Unenroll(int64_t instrument_id) { payments::GetBillingCustomerId(personal_data_manager_); request_details.instrument_id = instrument_id; + virtual_card_enrollment_update_response_callback_ = + std::move(virtual_card_enrollment_update_response_callback); + payments_client_->UpdateVirtualCardEnrollment( request_details, base::BindOnce(&VirtualCardEnrollmentManager:: @@ -181,8 +193,9 @@ bool VirtualCardEnrollmentManager::ShouldBlockVirtualCardEnrollment( const std::string& instrument_id, VirtualCardEnrollmentSource virtual_card_enrollment_source) const { if (virtual_card_enrollment_source == - VirtualCardEnrollmentSource::kSettingsPage) + VirtualCardEnrollmentSource::kSettingsPage) { return false; + } if (!GetVirtualCardEnrollmentStrikeDatabase()) return false; @@ -262,6 +275,13 @@ void VirtualCardEnrollmentManager::OnDidGetUpdateVirtualCardEnrollmentResponse( state_.virtual_card_enrollment_fields.virtual_card_enrollment_source, type, result == AutofillClient::PaymentsRpcResult::kSuccess); Reset(); + // Relay the response to the server card editor page. This also destroys the + // payments delegate if the editor was already closed. + if (virtual_card_enrollment_update_response_callback_.has_value()) { + std::move(virtual_card_enrollment_update_response_callback_.value()) + .Run(result == AutofillClient::PaymentsRpcResult::kSuccess); + } + virtual_card_enrollment_update_response_callback_.reset(); } void VirtualCardEnrollmentManager::Reset() { @@ -322,8 +342,9 @@ void VirtualCardEnrollmentManager::ShowVirtualCardEnrollBubble() { autofill_client_->ShowVirtualCardEnrollDialog( state_.virtual_card_enrollment_fields, - base::BindOnce(&VirtualCardEnrollmentManager::Enroll, - weak_ptr_factory_.GetWeakPtr()), + base::BindOnce( + &VirtualCardEnrollmentManager::Enroll, weak_ptr_factory_.GetWeakPtr(), + /*virtual_card_enrollment_update_response_callback=*/absl::nullopt), base::BindOnce( &VirtualCardEnrollmentManager::OnVirtualCardEnrollmentBubbleCancelled, weak_ptr_factory_.GetWeakPtr())); @@ -337,24 +358,10 @@ void VirtualCardEnrollmentManager::OnRiskDataLoadedForVirtualCard( // GetDetailsForEnrollmentResponseDetails were already received, then we // received it from the UploadCardResponseDetails. Thus, we can skip making // another GetDetailsForEnrollmentRequest and go straight to showing the - // bubble if the avatar animation is complete. + // bubble. if (state_.virtual_card_enrollment_fields.virtual_card_enrollment_source == VirtualCardEnrollmentSource::kUpstream && enroll_response_details_received_) { -#if !BUILDFLAG(IS_ANDROID) - if (base::FeatureList::IsEnabled( - features::kAutofillEnableToolbarStatusChip) && - base::FeatureList::IsEnabled( - features::kAutofillCreditCardUploadFeedback) && - !avatar_animation_complete_) { - // If status chip and upload feedback is enabled, we need to make sure - // we wait for the upload card animation to complete before showing the - // virtual card enroll bubble, or else we will have a conflict and the - // virtual card enroll bubble will not show. - return; - } -#endif - // We are about to show the virtual card enroll bubble, so make sure the // card art image is set to then display in the bubble. EnsureCardArtImageIsSetBeforeShowingUI(); @@ -426,22 +433,6 @@ void VirtualCardEnrollmentManager::OnDidGetDetailsForEnrollResponse( DCHECK(IsValidGetDetailsForEnrollmentResponseDetails(response)); SetGetDetailsForEnrollmentResponseDetails(response); -#if !BUILDFLAG(IS_ANDROID) - if (base::FeatureList::IsEnabled( - features::kAutofillEnableToolbarStatusChip) && - base::FeatureList::IsEnabled( - features::kAutofillCreditCardUploadFeedback) && - state_.virtual_card_enrollment_fields.virtual_card_enrollment_source == - VirtualCardEnrollmentSource::kUpstream && - !avatar_animation_complete_) { - // If status chip and upload feedback is enabled, we need to make sure - // we wait for the upload card animation to complete before showing the - // virtual card enroll bubble, or else we will have a conflict and the - // virtual card enroll bubble will not show. - return; - } -#endif - // We are about to show the UI for virtual card enrollment, so make sure the // card art image is set to then display in the bubble. EnsureCardArtImageIsSetBeforeShowingUI(); @@ -509,6 +500,7 @@ void VirtualCardEnrollmentManager::EnsureCardArtImageIsSetBeforeShowingUI() { void VirtualCardEnrollmentManager::SetInitialVirtualCardEnrollFields( const CreditCard& credit_card, VirtualCardEnrollmentSource virtual_card_enrollment_source) { + // Reset here to override currently pending enrollment. Reset(); DCHECK_NE(virtual_card_enrollment_source, VirtualCardEnrollmentSource::kNone); diff --git a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h index 26af151fea6..ddc4e4515b8 100644 --- a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h +++ b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h @@ -108,6 +108,9 @@ class VirtualCardEnrollmentManager { using VirtualCardEnrollmentFieldsLoadedCallback = base::OnceCallback<void( VirtualCardEnrollmentFields* virtual_card_enrollment_fields)>; + using VirtualCardEnrollmentUpdateResponseCallback = + base::OnceCallback<void(bool)>; + // Starting point for the VCN enroll flow. The fields in |credit_card| will // be used throughout the flow, such as for request fields as well as credit // card specific fields for the bubble to display. @@ -149,10 +152,16 @@ class VirtualCardEnrollmentManager { // |vcn_context_token_|, which should be set when we receive the // GetDetailsForEnrollResponse, is used in the // UpdateVirtualCardEnrollmentRequest to enroll the correct card. - void Enroll(); + void Enroll( + // The callback lets the Android Settings page know whether + // (un)enrollment was successful. + absl::optional<VirtualCardEnrollmentUpdateResponseCallback> + virtual_card_enrollment_update_response_callback); // Unenrolls the card mapped to the given |instrument_id|. - void Unenroll(int64_t instrument_id); + void Unenroll(int64_t instrument_id, + absl::optional<VirtualCardEnrollmentUpdateResponseCallback> + virtual_card_enrollment_update_response_callback); // Returns true if a credit card identified by its |instrument_id| should be // blocked for virtual card enrollment and is not attempting to enroll from @@ -236,6 +245,11 @@ class VirtualCardEnrollmentManager { VirtualCardEnrollmentFieldsLoadedCallback virtual_card_enrollment_fields_loaded_callback_; + // Callback triggered after getting server response about the success of + // virtual card (un)enrollment. + absl::optional<VirtualCardEnrollmentUpdateResponseCallback> + virtual_card_enrollment_update_response_callback_; + private: friend class VirtualCardEnrollmentManagerTest; FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest, diff --git a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc index 9630ced0549..d64283ce34b 100644 --- a/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc @@ -477,7 +477,8 @@ TEST_F(VirtualCardEnrollmentManagerTest, Enroll) { payments_client_->set_update_virtual_card_enrollment_result( AutofillClient::PaymentsRpcResult::kSuccess); - virtual_card_enrollment_manager_->Enroll(); + virtual_card_enrollment_manager_->Enroll( + /*virtual_card_enrollment_update_response_callback=*/absl::nullopt); payments::PaymentsClient::UpdateVirtualCardEnrollmentRequestDetails request_details = @@ -518,7 +519,8 @@ TEST_F(VirtualCardEnrollmentManagerTest, Enroll) { // Starts another request and makes sure it fails. payments_client_->set_update_virtual_card_enrollment_result( AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure); - virtual_card_enrollment_manager_->Enroll(); + virtual_card_enrollment_manager_->Enroll( + /*virtual_card_enrollment_update_response_callback=*/absl::nullopt); // Verifies the logging. histogram_tester.ExpectUniqueSample( @@ -538,7 +540,8 @@ TEST_F(VirtualCardEnrollmentManagerTest, Unenroll) { AutofillClient::PaymentsRpcResult::kNone); virtual_card_enrollment_manager_->Unenroll( - /*instrument_id=*/9223372036854775807); + /*instrument_id=*/9223372036854775807, + /*virtual_card_enrollment_update_response_callback=*/absl::nullopt); payments::PaymentsClient::UpdateVirtualCardEnrollmentRequestDetails request_details = @@ -567,7 +570,8 @@ TEST_F(VirtualCardEnrollmentManagerTest, Unenroll) { payments_client_->set_update_virtual_card_enrollment_result( AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure); virtual_card_enrollment_manager_->Unenroll( - /*instrument_id=*/9223372036854775807); + /*instrument_id=*/9223372036854775807, + /*virtual_card_enrollment_update_response_callback=*/absl::nullopt); // Verifies the logging. histogram_tester.ExpectUniqueSample( @@ -578,110 +582,6 @@ TEST_F(VirtualCardEnrollmentManagerTest, Unenroll) { /*sample=*/false, 1); } -#if !BUILDFLAG(IS_ANDROID) -TEST_F(VirtualCardEnrollmentManagerTest, UpstreamAnimationSync_AnimationFirst) { - for (bool optimized_upstream : {true, false}) { - virtual_card_enrollment_manager_->SetBubbleShown(false); - virtual_card_enrollment_manager_->SetAvatarAnimationComplete(false); - virtual_card_enrollment_manager_->SetEnrollResponseDetailsReceived(false); - - personal_data_manager_->ClearCreditCardArtImages(); - SetUpCard(); - SetValidCardArtImageForCard(*card_); - - VirtualCardEnrollmentProcessState* state = - virtual_card_enrollment_manager_ - ->GetVirtualCardEnrollmentProcessState(); - state->virtual_card_enrollment_fields.credit_card = *card_; - state->vcn_context_token = kTestVcnContextToken; - state->virtual_card_enrollment_fields.virtual_card_enrollment_source = - VirtualCardEnrollmentSource::kUpstream; - - payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails response; - response.vcn_context_token = kTestVcnContextToken; - response.issuer_legal_message = { - TestLegalMessageLine("issuer_test_legal_message_line")}; - response.google_legal_message = { - TestLegalMessageLine("google_test_legal_message_line")}; - - absl::optional< - payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails> - response_optional = response; - // Update avatar animation complete boolean. - virtual_card_enrollment_manager_->OnCardSavedAnimationComplete(); - EXPECT_TRUE(virtual_card_enrollment_manager_->GetAvatarAnimationComplete()); - - // Ensure bubble was not shown yet. - EXPECT_FALSE(virtual_card_enrollment_manager_->GetBubbleShown()); - - if (optimized_upstream) { - virtual_card_enrollment_manager_->InitVirtualCardEnroll( - *card_, VirtualCardEnrollmentSource::kUpstream, response_optional); - } else { - virtual_card_enrollment_manager_->OnDidGetDetailsForEnrollResponse( - AutofillClient::PaymentsRpcResult::kSuccess, response); - } - - EXPECT_TRUE( - virtual_card_enrollment_manager_->GetEnrollResponseDetailsReceived()); - - // Ensure bubble was shown. - EXPECT_TRUE(virtual_card_enrollment_manager_->GetBubbleShown()); - } -} - -TEST_F(VirtualCardEnrollmentManagerTest, UpstreamAnimationSync_ResponseFirst) { - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures({features::kAutofillEnableToolbarStatusChip, - features::kAutofillCreditCardUploadFeedback}, - {}); - for (bool optimized_upstream : {true, false}) { - virtual_card_enrollment_manager_->Reset(); - virtual_card_enrollment_manager_->SetBubbleShown(false); - virtual_card_enrollment_manager_->SetAvatarAnimationComplete(false); - personal_data_manager_->ClearCreditCardArtImages(); - SetUpCard(); - SetValidCardArtImageForCard(*card_); - - VirtualCardEnrollmentProcessState* state = - virtual_card_enrollment_manager_ - ->GetVirtualCardEnrollmentProcessState(); - state->virtual_card_enrollment_fields.credit_card = *card_; - state->vcn_context_token = kTestVcnContextToken; - state->virtual_card_enrollment_fields.virtual_card_enrollment_source = - VirtualCardEnrollmentSource::kUpstream; - - payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails response; - response.vcn_context_token = kTestVcnContextToken; - response.issuer_legal_message = { - TestLegalMessageLine("issuer_test_legal_message_line")}; - response.google_legal_message = { - TestLegalMessageLine("google_test_legal_message_line")}; - - absl::optional< - payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails> - response_optional = response; - if (optimized_upstream) { - virtual_card_enrollment_manager_->InitVirtualCardEnroll( - *card_, VirtualCardEnrollmentSource::kUpstream, response_optional); - } else { - virtual_card_enrollment_manager_->OnDidGetDetailsForEnrollResponse( - AutofillClient::PaymentsRpcResult::kSuccess, response); - } - - EXPECT_TRUE( - virtual_card_enrollment_manager_->GetEnrollResponseDetailsReceived()); - - // Ensure bubble was not shown yet. - EXPECT_FALSE(virtual_card_enrollment_manager_->GetBubbleShown()); - - // Update avatar animation complete boolean. - virtual_card_enrollment_manager_->OnCardSavedAnimationComplete(); - EXPECT_TRUE(virtual_card_enrollment_manager_->GetAvatarAnimationComplete()); - } -} -#endif // !BUILDFLAG(IS_ANDROID) - #if !BUILDFLAG(IS_IOS) TEST_F(VirtualCardEnrollmentManagerTest, StrikeDatabase_BubbleAccepted) { base::HistogramTester histogram_tester; @@ -703,7 +603,8 @@ TEST_F(VirtualCardEnrollmentManagerTest, StrikeDatabase_BubbleAccepted) { 1); // Ensure a strike has been removed after enrollment accepted. - virtual_card_enrollment_manager_->Enroll(); + virtual_card_enrollment_manager_->Enroll( + /*virtual_card_enrollment_update_response_callback=*/absl::nullopt); EXPECT_EQ( virtual_card_enrollment_manager_->GetVirtualCardEnrollmentStrikeDatabase() ->GetStrikes( diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc index 594176e85cd..5e5ffbb6431 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager.cc @@ -432,21 +432,23 @@ void PersonalDataManager::OnWebDataServiceRequestDone( DCHECK(pending_profiles_query_ || pending_server_profiles_query_ || pending_creditcards_query_ || pending_server_creditcards_query_ || pending_server_creditcard_cloud_token_data_query_ || - pending_customer_data_query_ || pending_upi_ids_query_ || - pending_offer_data_query_); + pending_ibans_query_ || pending_customer_data_query_ || + pending_upi_ids_query_ || pending_offer_data_query_); if (!result) { // Error from the web database. - if (h == pending_creditcards_query_) - pending_creditcards_query_ = 0; - else if (h == pending_profiles_query_) + if (h == pending_profiles_query_) pending_profiles_query_ = 0; - else if (h == pending_server_creditcards_query_) - pending_server_creditcards_query_ = 0; else if (h == pending_server_profiles_query_) pending_server_profiles_query_ = 0; + else if (h == pending_creditcards_query_) + pending_creditcards_query_ = 0; + else if (h == pending_server_creditcards_query_) + pending_server_creditcards_query_ = 0; else if (h == pending_server_creditcard_cloud_token_data_query_) pending_server_creditcard_cloud_token_data_query_ = 0; + else if (h == pending_ibans_query_) + pending_ibans_query_ = 0; else if (h == pending_customer_data_query_) pending_customer_data_query_ = 0; else if (h == pending_upi_ids_query_) @@ -487,6 +489,12 @@ void PersonalDataManager::OnWebDataServiceRequestDone( h, result.get(), &pending_server_creditcard_cloud_token_data_query_, &server_credit_card_cloud_token_data_); break; + case AUTOFILL_IBANS_RESULT: + DCHECK_EQ(h, pending_ibans_query_) + << "received ibans from invalid request."; + ReceiveLoadedDbValues(h, result.get(), &pending_ibans_query_, + &local_ibans_); + break; case AUTOFILL_CUSTOMERDATA_RESULT: DCHECK_EQ(h, pending_customer_data_query_) << "received customer data from invalid request."; @@ -574,23 +582,12 @@ void PersonalDataManager::OnSyncShutdown(syncer::SyncService* sync_service) { CoreAccountInfo PersonalDataManager::GetAccountInfoForPaymentsServer() const { // Return the account of the active signed-in user irrespective of whether // they enabled sync or not. - // However if there is no |sync_service_| (e.g. in incognito), return the - // latest cached AccountInfo of the user's primary account, which is empty if - // the user has disabled sync. - // In both cases, the AccountInfo will be empty if the user is not signed in. - return sync_service_ ? sync_service_->GetAccountInfo() - : identity_manager_->GetPrimaryAccountInfo( - signin::ConsentLevel::kSync); + return identity_manager_->GetPrimaryAccountInfo( + signin::ConsentLevel::kSignin); } -// TODO(crbug.com/903914): Clean up this function so that it's more clear what -// it's checking. It should not check the database helper. bool PersonalDataManager::IsSyncFeatureEnabled() const { - if (!sync_service_) - return false; - - return !sync_service_->GetAccountInfo().IsEmpty() && - !database_helper_->IsUsingAccountStorageForServerData(); + return sync_service_ && sync_service_->IsSyncFeatureEnabled(); } void PersonalDataManager::OnAccountsCookieDeletedByUserAction() { @@ -777,6 +774,51 @@ AutofillProfile* PersonalDataManager::GetProfileFromProfilesByGUID( return iter != profiles.end() ? *iter : nullptr; } +void PersonalDataManager::AddIBAN(const IBAN& iban) { + if (!IsAutofillIBANEnabled()) + return; + + if (is_off_the_record_ || FindByGUID(local_ibans_, iban.guid()) || + !database_helper_->GetLocalDatabase() || + FindByContents(local_ibans_, iban)) { + return; + } + + // Add the new iban to the web database. + database_helper_->GetLocalDatabase()->AddIBAN(iban); + + // Refresh our local cache and send notifications to observers. + Refresh(); +} + +void PersonalDataManager::UpdateIBAN(const IBAN& iban) { + DCHECK_EQ(IBAN::LOCAL_IBAN, iban.record_type()); + if (is_off_the_record_) { + return; + } + IBAN* existing_iban = GetIBANByGUID(iban.guid()); + if (!existing_iban) { + return; + } + + // Do not overwrite iban if it's existed already. + if (existing_iban->Compare(iban) == 0) { + return; + } + + // Update the cached version. + *existing_iban = iban; + if (!database_helper_->GetLocalDatabase()) { + return; + } + + // Make the update. + database_helper_->GetLocalDatabase()->UpdateIBAN(iban); + + // Refresh our local cache and send notifications to observers. + Refresh(); +} + void PersonalDataManager::AddCreditCard(const CreditCard& credit_card) { if (!IsAutofillCreditCardEnabled()) return; @@ -1018,16 +1060,25 @@ void PersonalDataManager::RemoveByGUID(const std::string& guid) { if (!database_helper_->GetLocalDatabase()) return; - bool is_credit_card = FindByGUID(local_credit_cards_, guid); - if (is_credit_card) { + if (FindByGUID(local_credit_cards_, guid)) { database_helper_->GetLocalDatabase()->RemoveCreditCard(guid); // Refresh our local cache and send notifications to observers. Refresh(); + } else if (FindByGUID(local_ibans_, guid)) { + database_helper_->GetLocalDatabase()->RemoveIBAN(guid); + // Refresh our local cache and send notifications to observers. + Refresh(); } else { RemoveAutofillProfileByGUIDAndBlankCreditCardReference(guid); } } +IBAN* PersonalDataManager::GetIBANByGUID(const std::string& guid) { + const std::vector<IBAN*>& ibans = GetIBANs(); + auto iter = FindElementByGUID(ibans, guid); + return iter != ibans.end() ? *iter : nullptr; +} + CreditCard* PersonalDataManager::GetCreditCardByGUID(const std::string& guid) { const std::vector<CreditCard*>& credit_cards = GetCreditCards(); auto iter = FindElementByGUID(credit_cards, guid); @@ -1118,7 +1169,6 @@ std::vector<CreditCard*> PersonalDataManager::GetServerCreditCards() const { std::vector<CreditCard*> PersonalDataManager::GetCreditCards() const { std::vector<CreditCard*> result; - result.reserve(local_credit_cards_.size() + server_credit_cards_.size()); for (const auto& card : local_credit_cards_) result.push_back(card.get()); @@ -1130,6 +1180,15 @@ std::vector<CreditCard*> PersonalDataManager::GetCreditCards() const { return result; } +std::vector<IBAN*> PersonalDataManager::GetIBANs() const { + std::vector<IBAN*> result; + result.reserve(local_ibans_.size()); + for (const auto& iban : local_ibans_) { + result.push_back(iban.get()); + } + return result; +} + PaymentsCustomerData* PersonalDataManager::GetPaymentsCustomerData() const { return payments_customer_data_ ? payments_customer_data_.get() : nullptr; } @@ -1147,7 +1206,7 @@ PersonalDataManager::GetCreditCardCloudTokenData() const { } std::vector<AutofillOfferData*> PersonalDataManager::GetAutofillOffers() const { - if (!IsAutofillWalletImportEnabled()) + if (!IsAutofillWalletImportEnabled() || !IsAutofillCreditCardEnabled()) return {}; std::vector<AutofillOfferData*> result; @@ -1160,7 +1219,7 @@ std::vector<AutofillOfferData*> PersonalDataManager::GetAutofillOffers() const { std::vector<const AutofillOfferData*> PersonalDataManager::GetActiveAutofillPromoCodeOffersForOrigin( GURL origin) const { - if (!IsAutofillWalletImportEnabled()) + if (!IsAutofillWalletImportEnabled() || !IsAutofillCreditCardEnabled()) return {}; std::vector<const AutofillOfferData*> promo_code_offers_for_origin; @@ -1210,6 +1269,7 @@ void PersonalDataManager::Refresh() { LoadProfiles(); LoadCreditCards(); LoadCreditCardCloudTokenData(); + LoadIBANs(); LoadPaymentsCustomerData(); LoadUpiIds(); LoadAutofillOffers(); @@ -1367,7 +1427,8 @@ const std::vector<CreditCard*> PersonalDataManager::GetCreditCardsToSuggest( } bool PersonalDataManager::IsAutofillEnabled() const { - return IsAutofillProfileEnabled() || IsAutofillCreditCardEnabled(); + return IsAutofillProfileEnabled() || IsAutofillCreditCardEnabled() || + IsAutofillIBANEnabled(); } bool PersonalDataManager::IsAutofillProfileEnabled() const { @@ -1378,6 +1439,10 @@ bool PersonalDataManager::IsAutofillCreditCardEnabled() const { return prefs::IsAutofillCreditCardEnabled(pref_service_); } +bool PersonalDataManager::IsAutofillIBANEnabled() const { + return prefs::IsAutofillIBANEnabled(pref_service_); +} + bool PersonalDataManager::IsAutofillWalletImportEnabled() const { return prefs::IsPaymentsIntegrationEnabled(pref_service_); } @@ -1726,9 +1791,22 @@ void PersonalDataManager::LoadCreditCardCloudTokenData() { database_helper_->GetServerDatabase()->GetCreditCardCloudTokenData(this); } +void PersonalDataManager::LoadIBANs() { + if (!database_helper_->GetLocalDatabase()) { + NOTREACHED(); + return; + } + + CancelPendingLocalQuery(&pending_ibans_query_); + + pending_ibans_query_ = database_helper_->GetLocalDatabase()->GetIBANs(this); +} + void PersonalDataManager::LoadUpiIds() { - if (!database_helper_->GetLocalDatabase()) + if (!database_helper_->GetLocalDatabase()) { + NOTREACHED(); return; + } CancelPendingLocalQuery(&pending_upi_ids_query_); @@ -1778,10 +1856,6 @@ void PersonalDataManager::CancelPendingServerQueries() { CancelPendingServerQuery(&pending_offer_data_query_); } -bool PersonalDataManager::HasPendingQueriesForTesting() { - return HasPendingQueries(); -} - void PersonalDataManager::LoadPaymentsCustomerData() { if (!database_helper_->GetServerDatabase()) return; @@ -2094,15 +2168,7 @@ void PersonalDataManager::NotifyPersonalDataObserver() { } } -void PersonalDataManager::OnCreditCardSaved(bool is_local_card) { - if (!base::FeatureList::IsEnabled( - features::kAutofillCreditCardUploadFeedback)) { - return; - } - for (PersonalDataManagerObserver& observer : observers_) - observer.OnCreditCardSaved( - /*should_show_sign_in_promo_if_applicable=*/is_local_card); -} +void PersonalDataManager::OnCreditCardSaved(bool is_local_card) {} void PersonalDataManager::ConvertWalletAddressesAndUpdateWalletCards() { // If the full Sync feature isn't enabled, then do NOT convert any Wallet diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h index c11ab0ed653..510ca157a3d 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.h +++ b/chromium/components/autofill/core/browser/personal_data_manager.h @@ -25,6 +25,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/data_model/credit_card_cloud_token_data.h" +#include "components/autofill/core/browser/data_model/iban.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/geo/alternative_state_name_map_updater.h" #include "components/autofill/core/browser/payments/account_info_getter.h" @@ -200,6 +201,13 @@ class PersonalDataManager : public KeyedService, const std::string& guid, const std::vector<AutofillProfile*>& profiles); + // Adds |iban| to the web database as a local Iban. + virtual void AddIBAN(const IBAN& iban); + + // Updates |iban| which already exists in the web database. This + // can only be used on local ibans. + virtual void UpdateIBAN(const IBAN& iban); + // Adds |credit_card| to the web database as a local card. virtual void AddCreditCard(const CreditCard& credit_card); @@ -237,6 +245,12 @@ class PersonalDataManager : public KeyedService, // Sets a server credit card for test. void AddServerCreditCardForTest(std::unique_ptr<CreditCard> credit_card); +#if defined(UNIT_TEST) + void AddIBANForTest(std::unique_ptr<IBAN> iban) { + local_ibans_.push_back(std::move(iban)); + } +#endif + // Returns whether server credit cards are stored in account (i.e. ephemeral) // storage. bool IsUsingAccountStorageForServerDataForTest() const; @@ -245,6 +259,10 @@ class PersonalDataManager : public KeyedService, // the real database. void AddOfferDataForTest(std::unique_ptr<AutofillOfferData> offer_data); + // Returns the iban with the specified |guid|, or nullptr if there is no iban + // with the specified |guid|. + virtual IBAN* GetIBANByGUID(const std::string& guid); + // Returns the credit card with the specified |guid|, or nullptr if there is // no credit card with the specified |guid|. virtual CreditCard* GetCreditCardByGUID(const std::string& guid); @@ -280,6 +298,9 @@ class PersonalDataManager : public KeyedService, // Returns all credit cards, server and local. virtual std::vector<CreditCard*> GetCreditCards() const; + // Returns local Ibans. + virtual std::vector<IBAN*> GetIBANs() const; + // Returns the Payments customer data. Returns nullptr if no data is present. virtual PaymentsCustomerData* GetPaymentsCustomerData() const; @@ -330,12 +351,12 @@ class PersonalDataManager : public KeyedService, const std::vector<CreditCard*> GetCreditCardsToSuggest( bool include_server_cards) const; - // Re-loads profiles and credit cards from the WebDatabase asynchronously. - // In the general case, this is a no-op and will re-create the same - // in-memory model as existed prior to the call. If any change occurred to - // profiles in the WebDatabase directly, as is the case if the browser sync - // engine processed a change from the cloud, we will learn of these as a - // result of this call. + // Re-loads profiles, credit cards, and IBANs from the WebDatabase + // asynchronously. In the general case, this is a no-op and will re-create + // the same in-memory model as existed prior to the call. If any change + // occurred to profiles in the WebDatabase directly, as is the case if the + // browser sync engine processed a change from the cloud, we will learn of + // these as a result of this call. // // Also see SetProfile for more details. virtual void Refresh(); @@ -355,6 +376,7 @@ class PersonalDataManager : public KeyedService, variations_country_code_ = country_code; } +#if BUILDFLAG(IS_IOS) // Returns the raw pointer to PersonalDataManagerCleaner used for testing // purposes. PersonalDataManagerCleaner* personal_data_manager_cleaner_for_testing() @@ -362,7 +384,8 @@ class PersonalDataManager : public KeyedService, DCHECK(personal_data_manager_cleaner_); return personal_data_manager_cleaner_.get(); } -#endif +#endif // IOS +#endif // UNIT_TEST // Returns our best guess for the country a user is likely to use when // inputting a new address. The value is calculated once and cached, so it @@ -383,8 +406,10 @@ class PersonalDataManager : public KeyedService, // Cancels any pending queries to the server web database. void CancelPendingServerQueries(); +#if defined(UNIT_TEST) // Returns if there are any pending queries to the web database. - bool HasPendingQueriesForTesting(); + bool HasPendingQueriesForTesting() { return HasPendingQueries(); } +#endif // This function assumes |credit_card| contains the full PAN. Returns |true| // if the card number of |credit_card| is equal to any local card or any @@ -418,6 +443,8 @@ class PersonalDataManager : public KeyedService, // Notifies observers that the waiting should be stopped. void NotifyPersonalDataObserver(); + // TODO(crbug.com/1337392): Revisit the function when card upload feedback is + // to be added again. In the new proposal, we may not need to go through PDM. // Called when at least one (can be multiple) card was saved. |is_local_card| // indicates if the card is saved to local storage. void OnCreditCardSaved(bool is_local_card); @@ -431,6 +458,9 @@ class PersonalDataManager : public KeyedService, // Returns the value of the AutofillCreditCardEnabled pref. virtual bool IsAutofillCreditCardEnabled() const; + // Returns the value of the AutofillIBANEnabled pref. + virtual bool IsAutofillIBANEnabled() const; + // Returns the value of the AutofillWalletImportEnabled pref. virtual bool IsAutofillWalletImportEnabled() const; @@ -490,32 +520,6 @@ class PersonalDataManager : public KeyedService, FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, GetCreditCardByServerId); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, AddAndGetCreditCardArtImage); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - DedupeProfiles_ProfilesToDelete); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - DedupeProfiles_GuidsMergeMap); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - UpdateCardsBillingAddressReference); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ApplyDedupingRoutine_CardsBillingAddressIdUpdated); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ApplyDedupingRoutine_MergedProfileValues); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ApplyDedupingRoutine_VerifiedProfileFirst); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ApplyDedupingRoutine_VerifiedProfileLast); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ApplyDedupingRoutine_MultipleVerifiedProfiles); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ApplyDedupingRoutine_FeatureDisabled); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ApplyDedupingRoutine_NopIfZeroProfiles); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ApplyDedupingRoutine_NopIfOneProfile); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ApplyDedupingRoutine_OncePerVersion); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - ApplyDedupingRoutine_MultipleDedupes); FRIEND_TEST_ALL_PREFIXES( PersonalDataManagerTest, ConvertWalletAddressesAndUpdateWalletCards_NewProfile); @@ -535,9 +539,6 @@ class PersonalDataManager : public KeyedService, DoNotConvertWalletAddressesInEphemeralStorage); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, DeleteDisusedCreditCards_DoNothingWhenDisabled); - FRIEND_TEST_ALL_PREFIXES( - PersonalDataManagerTest, - DeleteDisusedCreditCards_OnlyDeleteExpiredDisusedLocalCards); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, GetProfileSuggestions_ProfileAutofillDisabled); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, @@ -552,6 +553,8 @@ class PersonalDataManager : public KeyedService, PersonalDataManagerTest, GetCreditCardsToSuggest_NoCreditCardsAddedIfDisabled); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, LogStoredCreditCardMetrics); + FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerCleanerTest, + UpdateCardsBillingAddressReference); friend class autofill::AutofillInteractiveTest; friend class autofill::PersonalDataManagerCleaner; @@ -600,6 +603,9 @@ class PersonalDataManager : public KeyedService, // Loads the saved credit card cloud token data from the web database. virtual void LoadCreditCardCloudTokenData(); + // Loads the saved IBANs from the web database. + virtual void LoadIBANs(); + // Loads the payments customer data from the web database. virtual void LoadPaymentsCustomerData(); @@ -662,6 +668,9 @@ class PersonalDataManager : public KeyedService, std::vector<std::unique_ptr<CreditCard>> local_credit_cards_; std::vector<std::unique_ptr<CreditCard>> server_credit_cards_; + // Cached versions of the local Ibans. + std::vector<std::unique_ptr<IBAN>> local_ibans_; + // Cached UPI IDs. std::vector<std::string> upi_ids_; @@ -686,6 +695,7 @@ class PersonalDataManager : public KeyedService, WebDataServiceBase::Handle pending_server_creditcards_query_ = 0; WebDataServiceBase::Handle pending_server_creditcard_cloud_token_data_query_ = 0; + WebDataServiceBase::Handle pending_ibans_query_ = 0; WebDataServiceBase::Handle pending_customer_data_query_ = 0; WebDataServiceBase::Handle pending_upi_ids_query_ = 0; WebDataServiceBase::Handle pending_offer_data_query_ = 0; diff --git a/chromium/components/autofill/core/browser/personal_data_manager_cleaner_unittest.cc b/chromium/components/autofill/core/browser/personal_data_manager_cleaner_unittest.cc new file mode 100644 index 00000000000..4ad0a096b71 --- /dev/null +++ b/chromium/components/autofill/core/browser/personal_data_manager_cleaner_unittest.cc @@ -0,0 +1,1290 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/personal_data_manager_cleaner.h" + +#include "base/guid.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/browser/personal_data_manager_test_base.h" +#include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_constants.h" +#include "components/autofill/core/common/autofill_features.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::Matcher; +using ::testing::Truly; + +namespace autofill { + +namespace { + +const base::Time kArbitraryTime = base::Time::FromDoubleT(25); +const base::Time kSomeLaterTime = base::Time::FromDoubleT(1000); +const base::Time kMuchLaterTime = base::Time::FromDoubleT(5000); + +ACTION_P(QuitMessageLoop, loop) { + loop->Quit(); +} + +template <typename T> +auto HasSameElements(const std::vector<T*>& expectations) { + std::vector<Matcher<T*>> matchers; + for (const auto& e : expectations) + matchers.push_back(Truly([e](T* a) { return a->Compare(*e) == 0; })); + return UnorderedElementsAreArray(matchers); +} + +} // anonymous namespace + +class PersonalDataManagerCleanerTest : public PersonalDataManagerTestBase, + public testing::Test { + public: + PersonalDataManagerCleanerTest() = default; + ~PersonalDataManagerCleanerTest() override = default; + + void SetUp() override { + SetUpTest(); + personal_data_ = std::make_unique<PersonalDataManager>("EN", "US"); + ResetPersonalDataManager(/*is_incognito=*/false, + /*use_sync_transport_mode=*/false, + personal_data_.get()); + personal_data_manager_cleaner_ = + std::make_unique<PersonalDataManagerCleaner>(personal_data_.get(), + nullptr, prefs_.get()); + } + + void TearDown() override { + if (personal_data_) + personal_data_->Shutdown(); + personal_data_.reset(); + TearDownTest(); + } + + protected: + // Verifies that the web database has been updated and the notification sent. + void WaitForOnPersonalDataChanged( + absl::optional<AutofillProfile> profile = absl::nullopt) { + base::RunLoop run_loop; + EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) + .WillOnce(QuitMessageLoop(&run_loop)); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .Times(testing::AnyNumber()); + if (profile) + personal_data_->AddProfile(profile.value()); + run_loop.Run(); + } + + void AddProfileToPersonalDataManager(const AutofillProfile& profile) { + WaitForOnPersonalDataChanged(profile); + } + + void SetServerCards(const std::vector<CreditCard>& server_cards) { + test::SetServerCreditCards(personal_data_->IsSyncFeatureEnabled() + ? profile_autofill_table_.get() + : account_autofill_table_.get(), + server_cards); + } + + std::unique_ptr<PersonalDataManager> personal_data_; + std::unique_ptr<PersonalDataManagerCleaner> personal_data_manager_cleaner_; +}; + +// Tests that DedupeProfiles sets the correct profile guids to +// delete after merging similar profiles. +TEST_F(PersonalDataManagerCleanerTest, DedupeProfiles_ProfilesToDelete) { + // Create the profile for which to find duplicates. It has the highest + // ranking score. + AutofillProfile* profile1 = + new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(profile1, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", "12345678910"); + profile1->set_use_count(9); + + // Create a different profile that should not be deduped (different address). + AutofillProfile* profile2 = + new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(profile2, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "Fox", "1234 Other Street", "", + "Springfield", "IL", "91601", "US", "12345678910"); + profile2->set_use_count(7); + + // Create a profile similar to profile1 which should be deduped. + AutofillProfile* profile3 = + new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(profile3, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", + "Springfield", "IL", "91601", "US", "12345678910"); + profile3->set_use_count(5); + + // Create another different profile that should not be deduped (different + // name). + AutofillProfile* profile4 = + new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(profile4, "Marjorie", "Jacqueline", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", "12345678910"); + profile4->set_use_count(3); + + // Create another profile similar to profile1. Since that one has the lowest + // ranking score, the result of the merge should be in this profile at the end + // of the test. + AutofillProfile* profile5 = + new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(profile5, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "US", "12345678910"); + profile5->set_use_count(1); + + // Add the profiles. + std::vector<std::unique_ptr<AutofillProfile>> existing_profiles; + existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile1)); + existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile2)); + existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile3)); + existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile4)); + existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile5)); + + base::HistogramTester histogram_tester; + std::unordered_map<std::string, std::string> guids_merge_map; + std::unordered_set<std::string> profiles_to_delete; + personal_data_manager_cleaner_->DedupeProfilesForTesting( + &existing_profiles, &profiles_to_delete, &guids_merge_map); + // 5 profiles were considered for dedupe. + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesConsideredForDedupe", 5, 1); + // 2 profiles were removed (profiles 1 and 3). + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1); + + // Profile1 should be deleted because it was sent as the profile to merge and + // thus was merged into profile3 and then into profile5. + EXPECT_TRUE(profiles_to_delete.count(profile1->guid())); + + // Profile3 should be deleted because profile1 was merged into it and the + // resulting profile was then merged into profile5. + EXPECT_TRUE(profiles_to_delete.count(profile3->guid())); + + // Only these two profiles should be deleted. + EXPECT_EQ(2U, profiles_to_delete.size()); + + // All profiles should still be present in |existing_profiles|. + EXPECT_EQ(5U, existing_profiles.size()); +} + +// Tests that DedupeProfiles sets the correct merge mapping for billing address +// id references. +TEST_F(PersonalDataManagerCleanerTest, DedupeProfiles_GuidsMergeMap) { + // Create the profile for which to find duplicates. It has the highest + // ranking score. + AutofillProfile* profile1 = + new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(profile1, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", "12345678910"); + profile1->set_use_count(9); + + // Create a different profile that should not be deduped (different address). + AutofillProfile* profile2 = + new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(profile2, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "Fox", "1234 Other Street", "", + "Springfield", "IL", "91601", "US", "12345678910"); + profile2->set_use_count(7); + + // Create a profile similar to profile1 which should be deduped. + AutofillProfile* profile3 = + new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(profile3, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", + "Springfield", "IL", "91601", "US", "12345678910"); + profile3->set_use_count(5); + + // Create another different profile that should not be deduped (different + // name). + AutofillProfile* profile4 = + new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(profile4, "Marjorie", "Jacqueline", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", "12345678910"); + profile4->set_use_count(3); + + // Create another profile similar to profile1. Since that one has the lowest + // ranking score, the result of the merge should be in this profile at the end + // of the test. + AutofillProfile* profile5 = + new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(profile5, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "US", "12345678910"); + profile5->set_use_count(1); + + // Add the profiles. + std::vector<std::unique_ptr<AutofillProfile>> existing_profiles; + existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile1)); + existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile2)); + existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile3)); + existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile4)); + existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile5)); + + std::unordered_map<std::string, std::string> guids_merge_map; + std::unordered_set<std::string> profiles_to_delete; + + personal_data_manager_cleaner_->DedupeProfilesForTesting( + &existing_profiles, &profiles_to_delete, &guids_merge_map); + + // The two profile merges should be recorded in the map. + EXPECT_EQ(2U, guids_merge_map.size()); + + // Profile 1 was merged into profile 3. + ASSERT_TRUE(guids_merge_map.count(profile1->guid())); + EXPECT_TRUE(guids_merge_map.at(profile1->guid()) == profile3->guid()); + + // Profile 3 was merged into profile 5. + ASSERT_TRUE(guids_merge_map.count(profile3->guid())); + EXPECT_TRUE(guids_merge_map.at(profile3->guid()) == profile5->guid()); +} + +// Tests that UpdateCardsBillingAddressReference sets the correct billing +// address id as specified in the map. +TEST_F(PersonalDataManagerCleanerTest, UpdateCardsBillingAddressReference) { + /* The merges will be as follow: + + A -> B F (not merged) + \ + -> E + / + C -> D + */ + + std::unordered_map<std::string, std::string> guids_merge_map; + guids_merge_map.insert(std::pair<std::string, std::string>("A", "B")); + guids_merge_map.insert(std::pair<std::string, std::string>("C", "D")); + guids_merge_map.insert(std::pair<std::string, std::string>("B", "E")); + guids_merge_map.insert(std::pair<std::string, std::string>("D", "E")); + + // Create a credit card without a billing address id + CreditCard* credit_card0 = + new CreditCard(base::GenerateGUID(), test::kEmptyOrigin); + + // Create cards that use A, D, E and F as their billing address id. + CreditCard* credit_card1 = + new CreditCard(base::GenerateGUID(), test::kEmptyOrigin); + credit_card1->set_billing_address_id("A"); + CreditCard* credit_card2 = + new CreditCard(base::GenerateGUID(), test::kEmptyOrigin); + credit_card2->set_billing_address_id("D"); + CreditCard* credit_card3 = + new CreditCard(base::GenerateGUID(), test::kEmptyOrigin); + credit_card3->set_billing_address_id("E"); + CreditCard* credit_card4 = + new CreditCard(base::GenerateGUID(), test::kEmptyOrigin); + credit_card4->set_billing_address_id("F"); + + // Add the credit cards to the database. + personal_data_->local_credit_cards_.push_back( + std::unique_ptr<CreditCard>(credit_card0)); + personal_data_->local_credit_cards_.push_back( + std::unique_ptr<CreditCard>(credit_card1)); + personal_data_->server_credit_cards_.push_back( + std::unique_ptr<CreditCard>(credit_card2)); + personal_data_->local_credit_cards_.push_back( + std::unique_ptr<CreditCard>(credit_card3)); + personal_data_->server_credit_cards_.push_back( + std::unique_ptr<CreditCard>(credit_card4)); + + personal_data_manager_cleaner_->UpdateCardsBillingAddressReferenceForTesting( + guids_merge_map); + + // The first card's billing address should now be E. + EXPECT_EQ("E", credit_card1->billing_address_id()); + // The second card's billing address should now be E. + EXPECT_EQ("E", credit_card2->billing_address_id()); + // The third card's billing address should still be E. + EXPECT_EQ("E", credit_card3->billing_address_id()); + // The fourth card's billing address should still be F. + EXPECT_EQ("F", credit_card4->billing_address_id()); +} + +// Tests that ApplyDedupingRoutine updates the credit cards' billing address id +// based on the deduped profiles. +TEST_F(PersonalDataManagerCleanerTest, + ApplyDedupingRoutine_CardsBillingAddressIdUpdated) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + + // A set of 6 profiles will be created. They should merge in this way: + // 1 -> 2 -> 3 + // 4 -> 5 + // 6 + // Set their frencency score so that profile 3 has a higher score than 5, and + // 5 has a higher score than 6. This will ensure a deterministic order when + // verifying results. + + // Create a set of 3 profiles to be merged together. + // Create a profile with a higher ranking score. + AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + profile1.set_use_count(12); + profile1.set_use_date(AutofillClock::Now() - base::Days(1)); + + // Create a profile with a medium ranking score. + AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile2, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", + "Springfield", "IL", "91601", "", "12345678910"); + profile2.set_use_count(5); + profile2.set_use_date(AutofillClock::Now() - base::Days(3)); + + // Create a profile with a lower ranking score. + AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile3, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + profile3.set_use_count(3); + profile3.set_use_date(AutofillClock::Now() - base::Days(5)); + + // Create a set of two profiles to be merged together. + // Create a profile with a higher ranking score. + AutofillProfile profile4(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile4, "Marge", "B", "Simpson", + "marge.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + profile4.set_use_count(11); + profile4.set_use_date(AutofillClock::Now() - base::Days(1)); + + // Create a profile with a lower ranking score. + AutofillProfile profile5(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile5, "Marge", "B", "Simpson", + "marge.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + profile5.set_use_count(5); + profile5.set_use_date(AutofillClock::Now() - base::Days(3)); + + // Create a unique profile. + AutofillProfile profile6(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile6, "Bart", "J", "Simpson", + "bart.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + profile6.set_use_count(10); + profile6.set_use_date(AutofillClock::Now() - base::Days(1)); + + // Add three credit cards. Give them a ranking score so that they are + // suggested in order (1, 2, 3). This will ensure a deterministic order for + // verifying results. + CreditCard credit_card1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetCreditCardInfo(&credit_card1, "Clyde Barrow", + "378282246310005" /* American Express */, "04", + "2999", "1"); + credit_card1.set_use_count(10); + + CreditCard credit_card2(base::GenerateGUID(), test::kEmptyOrigin); + test::SetCreditCardInfo(&credit_card2, "John Dillinger", + "4234567890123456" /* Visa */, "01", "2999", "1"); + credit_card2.set_use_count(5); + + CreditCard credit_card3(base::GenerateGUID(), test::kEmptyOrigin); + test::SetCreditCardInfo(&credit_card3, "Bonnie Parker", + "5105105105105100" /* Mastercard */, "12", "2999", + "1"); + credit_card3.set_use_count(1); + + // Associate the first card with profile1. + credit_card1.set_billing_address_id(profile1.guid()); + // Associate the second card with profile4. + credit_card2.set_billing_address_id(profile4.guid()); + // Associate the third card with profile6. + credit_card3.set_billing_address_id(profile6.guid()); + + AddProfileToPersonalDataManager(profile1); + AddProfileToPersonalDataManager(profile2); + AddProfileToPersonalDataManager(profile3); + AddProfileToPersonalDataManager(profile4); + AddProfileToPersonalDataManager(profile5); + AddProfileToPersonalDataManager(profile6); + personal_data_->AddCreditCard(credit_card1); + personal_data_->AddCreditCard(credit_card2); + personal_data_->AddCreditCard(credit_card3); + + WaitForOnPersonalDataChanged(); + + // Make sure the 6 profiles and 3 credit cards were saved. + EXPECT_EQ(6U, personal_data_->GetProfiles().size()); + EXPECT_EQ(3U, personal_data_->GetCreditCards().size()); + + EXPECT_TRUE(personal_data_manager_cleaner_->ApplyDedupingRoutineForTesting()); + WaitForOnPersonalDataChanged(); + + // Get the profiles and cards sorted by their ranking score to have a + // deterministic order. + std::vector<AutofillProfile*> profiles = + personal_data_->GetProfilesToSuggest(); + std::vector<CreditCard*> credit_cards = + personal_data_->GetCreditCardsToSuggest(/*include_server_cards=*/true); + + // |profile1| should have been merged into |profile2| which should then have + // been merged into |profile3|. |profile4| should have been merged into + // |profile5| and |profile6| should not have merged. Therefore there should be + // 3 profile left. + ASSERT_EQ(3U, profiles.size()); + + // Make sure the remaining profiles are the expected ones. + EXPECT_EQ(profile3.guid(), profiles[0]->guid()); + EXPECT_EQ(profile5.guid(), profiles[1]->guid()); + EXPECT_EQ(profile6.guid(), profiles[2]->guid()); + + // |credit_card1|'s billing address should now be profile 3. + EXPECT_EQ(profile3.guid(), credit_cards[0]->billing_address_id()); + + // |credit_card2|'s billing address should now be profile 5. + EXPECT_EQ(profile5.guid(), credit_cards[1]->billing_address_id()); + + // |credit_card3|'s billing address should still be profile 6. + EXPECT_EQ(profile6.guid(), credit_cards[2]->billing_address_id()); +} + +// Tests that ApplyDedupingRoutine merges the profile values correctly, i.e. +// never lose information and keep the syntax of the profile with the higher +// ranking score. +TEST_F(PersonalDataManagerCleanerTest, + ApplyDedupingRoutine_MergedProfileValues) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + + // Create a profile with a higher ranking score. + AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + profile1.set_use_count(10); + profile1.set_use_date(AutofillClock::Now() - base::Days(1)); + + // Create a profile with a medium ranking score. + AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile2, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", + "Springfield", "IL", "91601", "", "12345678910"); + profile2.set_use_count(5); + profile2.set_use_date(AutofillClock::Now() - base::Days(3)); + + // Create a profile with a lower ranking score. + AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile3, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + profile3.set_use_count(3); + profile3.set_use_date(AutofillClock::Now() - base::Days(5)); + + AddProfileToPersonalDataManager(profile1); + AddProfileToPersonalDataManager(profile2); + AddProfileToPersonalDataManager(profile3); + + // Make sure the 3 profiles were saved; + EXPECT_EQ(3U, personal_data_->GetProfiles().size()); + + base::HistogramTester histogram_tester; + + EXPECT_TRUE(personal_data_manager_cleaner_->ApplyDedupingRoutineForTesting()); + WaitForOnPersonalDataChanged(); + + std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); + + // |profile1| should have been merged into |profile2| which should then have + // been merged into |profile3|. Therefore there should only be 1 saved + // profile. + ASSERT_EQ(1U, profiles.size()); + // 3 profiles were considered for dedupe. + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesConsideredForDedupe", 3, 1); + // 2 profiles were removed (profiles 1 and 2). + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1); + + // Since profiles with higher ranking scores are merged into profiles with + // lower ranking scores, the result of the merge should be contained in + // profile3 since it had a lower ranking score compared to profile1. + EXPECT_EQ(profile3.guid(), profiles[0]->guid()); + // The address syntax that results from the merge should be the one from the + // imported profile (highest ranking). + EXPECT_EQ(u"742. Evergreen Terrace", + profiles[0]->GetRawInfo(ADDRESS_HOME_LINE1)); + // The middle name should be full, even if the profile with the higher + // ranking only had an initial (no loss of information). + EXPECT_EQ(u"Jay", profiles[0]->GetRawInfo(NAME_MIDDLE)); + // The specified phone number from profile1 should be kept (no loss of + // information). + EXPECT_EQ(u"12345678910", profiles[0]->GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); + // The specified company name from profile2 should be kept (no loss of + // information). + EXPECT_EQ(u"Fox", profiles[0]->GetRawInfo(COMPANY_NAME)); + // The specified country from the imported profile should be kept (no loss of + // information). + EXPECT_EQ(u"US", profiles[0]->GetRawInfo(ADDRESS_HOME_COUNTRY)); + // The use count that results from the merge should be the max of all the + // profiles use counts. + EXPECT_EQ(10U, profiles[0]->use_count()); + // The use date that results from the merge should be the one from the + // profile1 since it was the most recently used profile. + EXPECT_LT(profile1.use_date() - base::Seconds(10), profiles[0]->use_date()); +} + +// Tests that ApplyDedupingRoutine only keeps the verified profile with its +// original data when deduping with similar profiles, even if it has a higher +// ranking score. +TEST_F(PersonalDataManagerCleanerTest, + ApplyDedupingRoutine_VerifiedProfileFirst) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + + // Create a verified profile with a higher ranking score. + AutofillProfile profile1(base::GenerateGUID(), kSettingsOrigin); + test::SetProfileInfo( + &profile1, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "", + "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", + "12345678910", /*finalize=*/true, + /*status=*/structured_address::VerificationStatus::kUserVerified); + profile1.set_use_count(7); + profile1.set_use_date(kMuchLaterTime); + + // Create a similar non verified profile with a medium ranking score. + AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile2, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + profile2.set_use_count(5); + profile2.set_use_date(kSomeLaterTime); + + // Create a similar non verified profile with a lower ranking score. + AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile3, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + profile3.set_use_count(3); + profile3.set_use_date(kArbitraryTime); + + AddProfileToPersonalDataManager(profile1); + AddProfileToPersonalDataManager(profile2); + AddProfileToPersonalDataManager(profile3); + + // Make sure the 3 profiles were saved. + EXPECT_EQ(3U, personal_data_->GetProfiles().size()); + + base::HistogramTester histogram_tester; + + EXPECT_TRUE(personal_data_manager_cleaner_->ApplyDedupingRoutineForTesting()); + WaitForOnPersonalDataChanged(); + + std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); + + // |profile2| should have merged with |profile3|. |profile3| + // should then have been discarded because it is similar to the verified + // |profile1|. + ASSERT_EQ(1U, profiles.size()); + // 3 profiles were considered for dedupe. + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesConsideredForDedupe", 3, 1); + // 2 profile were removed (profiles 2 and 3). + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1); + + // Although the profile was verified, the structure of the street address + // still evolved with future observations. In this case, the "." was added + // from a later observation. + profile1.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_NAME, u"Evergreen Terrace", + structured_address::VerificationStatus::kParsed); + // + // Only the verified |profile1| with its original data should have been kept. + EXPECT_EQ(profile1.guid(), profiles[0]->guid()); + EXPECT_TRUE(profile1 == *profiles[0]); + EXPECT_EQ(profile1.use_count(), profiles[0]->use_count()); + EXPECT_EQ(profile1.use_date(), profiles[0]->use_date()); +} + +// Tests that ApplyDedupingRoutine only keeps the verified profile with its +// original data when deduping with similar profiles, even if it has a lower +// ranking score. +TEST_F(PersonalDataManagerCleanerTest, + ApplyDedupingRoutine_VerifiedProfileLast) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + + // Create a profile to dedupe with a higher ranking score. + AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + profile1.set_use_count(5); + profile1.set_use_date(kMuchLaterTime); + + // Create a similar non verified profile with a medium ranking score. + AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile2, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + profile2.set_use_count(5); + profile2.set_use_date(kSomeLaterTime); + + // Create a similar verified profile with a lower ranking score. + AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin); + test::SetProfileInfo( + &profile3, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "", + "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", + "12345678910", /*finalize=*/true, + /*status=*/structured_address::VerificationStatus::kUserVerified); + profile3.set_use_count(3); + profile3.set_use_date(kArbitraryTime); + + AddProfileToPersonalDataManager(profile1); + AddProfileToPersonalDataManager(profile2); + AddProfileToPersonalDataManager(profile3); + + // Make sure the 3 profiles were saved. + EXPECT_EQ(3U, personal_data_->GetProfiles().size()); + + base::HistogramTester histogram_tester; + + EXPECT_TRUE(personal_data_manager_cleaner_->ApplyDedupingRoutineForTesting()); + WaitForOnPersonalDataChanged(); + + std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); + + // |profile1| should have merged with |profile2|. |profile2| + // should then have been discarded because it is similar to the verified + // |profile3|. + ASSERT_EQ(1U, profiles.size()); + // 3 profiles were considered for dedupe. + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesConsideredForDedupe", 3, 1); + // 2 profile were removed (profiles 1 and 2). + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1); + + // Only the verified |profile3| with it's original data should have been kept. + EXPECT_EQ(profile3.guid(), profiles[0]->guid()); + EXPECT_TRUE(profile3 == *profiles[0]); + EXPECT_EQ(profile3.use_count(), profiles[0]->use_count()); + EXPECT_EQ(profile3.use_date(), profiles[0]->use_date()); +} + +// Tests that ApplyDedupingRoutine does not merge unverified data into +// a verified profile. Also tests that two verified profiles don't get merged. +TEST_F(PersonalDataManagerCleanerTest, + ApplyDedupingRoutine_MultipleVerifiedProfiles) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + + // Create a profile to dedupe with a higher ranking score. + AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + profile1.set_use_count(5); + profile1.set_use_date(kMuchLaterTime); + + // Create a similar verified profile with a medium ranking score. + AutofillProfile profile2(base::GenerateGUID(), kSettingsOrigin); + test::SetProfileInfo( + &profile2, "Homer", "J", "Simpson", "homer.simpson@abc.com", "Fox", + "742 Evergreen Terrace.", "", "Springfield", "IL", "91601", "", "", + /*finalize=*/true, + /*status=*/structured_address::VerificationStatus::kUserVerified); + + profile2.set_use_count(5); + profile2.set_use_date(kSomeLaterTime); + + // Create a similar verified profile with a lower ranking score. + AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin); + test::SetProfileInfo( + &profile3, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "", + "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", + "12345678910", /*finalize=*/true, + /*status*/ structured_address::VerificationStatus::kUserVerified); + profile3.set_use_count(3); + profile3.set_use_date(kArbitraryTime); + + AddProfileToPersonalDataManager(profile1); + AddProfileToPersonalDataManager(profile2); + AddProfileToPersonalDataManager(profile3); + + // Make sure the 3 profiles were saved. + EXPECT_EQ(3U, personal_data_->GetProfiles().size()); + + base::HistogramTester histogram_tester; + + EXPECT_TRUE(personal_data_manager_cleaner_->ApplyDedupingRoutineForTesting()); + WaitForOnPersonalDataChanged(); + + // Get the profiles, sorted by ranking to have a deterministic order. + std::vector<AutofillProfile*> profiles = + personal_data_->GetProfilesToSuggest(); + + // Although the profile was verified, the structure of the street address + // still evolved with future observations. In this case, the "." was removed + // from a later observation. + profile2.SetRawInfoWithVerificationStatus( + ADDRESS_HOME_STREET_NAME, u"Evergreen Terrace", + structured_address::VerificationStatus::kParsed); + + // |profile1| should have been discarded because the saved profile with the + // highest ranking score is verified (|profile2|). Therefore, |profile1|'s + // data should not have been merged with |profile2|'s data. Then |profile2| + // should have been compared to |profile3| but they should not have merged + // because both profiles are verified. + ASSERT_EQ(2U, profiles.size()); + // 3 profiles were considered for dedupe. + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesConsideredForDedupe", 3, 1); + // 1 profile was removed (|profile1|). + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesRemovedDuringDedupe", 1, 1); + + EXPECT_EQ(profile2.guid(), profiles[0]->guid()); + EXPECT_EQ(profile3.guid(), profiles[1]->guid()); + // The profiles should have kept their original data. + EXPECT_TRUE(profile2 == *profiles[0]); + EXPECT_TRUE(profile3 == *profiles[1]); + EXPECT_EQ(profile2.use_count(), profiles[0]->use_count()); + EXPECT_EQ(profile3.use_count(), profiles[1]->use_count()); + EXPECT_EQ(profile2.use_date(), profiles[0]->use_date()); + EXPECT_EQ(profile3.use_date(), profiles[1]->use_date()); +} + +// Tests that ApplyDedupingRoutine works as expected in a realistic scenario. +// Tests that it merges the diffent set of similar profiles independently and +// that the resulting profiles have the right values, has no effect on the other +// profiles and that the data of verified profiles is not modified. +TEST_F(PersonalDataManagerCleanerTest, ApplyDedupingRoutine_MultipleDedupes) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + + // Create a Homer home profile with a higher ranking score than other Homer + // profiles. + AutofillProfile Homer1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&Homer1, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + Homer1.set_use_count(10); + Homer1.set_use_date(AutofillClock::Now() - base::Days(1)); + + // Create a Homer home profile with a medium ranking score compared to other + // Homer profiles. + AutofillProfile Homer2(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&Homer2, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", + "Springfield", "IL", "91601", "", "12345678910"); + Homer2.set_use_count(5); + Homer2.set_use_date(AutofillClock::Now() - base::Days(3)); + + // Create a Homer home profile with a lower ranking score than other Homer + // profiles. + AutofillProfile Homer3(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&Homer3, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + Homer3.set_use_count(3); + Homer3.set_use_date(AutofillClock::Now() - base::Days(5)); + + // Create a Homer work profile (different address). + AutofillProfile Homer4(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&Homer4, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "Fox", "12 Nuclear Plant.", "", + "Springfield", "IL", "91601", "US", "9876543"); + Homer4.set_use_count(3); + Homer4.set_use_date(AutofillClock::Now() - base::Days(5)); + + // Create a Marge profile with a lower ranking score that other Marge + // profiles. + AutofillProfile Marge1(base::GenerateGUID(), kSettingsOrigin); + test::SetProfileInfo(&Marge1, "Marjorie", "J", "Simpson", + "marge.simpson@abc.com", "", "742 Evergreen Terrace", "", + "Springfield", "IL", "91601", "", "12345678910"); + Marge1.set_use_count(4); + Marge1.set_use_date(AutofillClock::Now() - base::Days(3)); + + // Create a verified Marge home profile with a lower ranking score that the + // other Marge profile. + AutofillProfile Marge2(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&Marge2, "Marjorie", "Jacqueline", "Simpson", + "marge.simpson@abc.com", "", "742 Evergreen Terrace", "", + "Springfield", "IL", "91601", "", "12345678910"); + Marge2.set_use_count(2); + Marge2.set_use_date(AutofillClock::Now() - base::Days(3)); + + // Create a Barney profile (guest user). + AutofillProfile Barney(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&Barney, "Barney", "", "Gumble", "barney.gumble@abc.com", + "ABC", "123 Other Street", "", "Springfield", "IL", + "91601", "", ""); + Barney.set_use_count(1); + Barney.set_use_date(AutofillClock::Now() - base::Days(180)); + Barney.FinalizeAfterImport(); + + AddProfileToPersonalDataManager(Homer1); + AddProfileToPersonalDataManager(Homer2); + AddProfileToPersonalDataManager(Homer3); + AddProfileToPersonalDataManager(Homer4); + AddProfileToPersonalDataManager(Marge1); + AddProfileToPersonalDataManager(Marge2); + AddProfileToPersonalDataManager(Barney); + + // Make sure the 7 profiles were saved; + EXPECT_EQ(7U, personal_data_->GetProfiles().size()); + + base::HistogramTester histogram_tester; + + // |Homer1| should get merged into |Homer2| which should then be merged into + // |Homer3|. |Marge2| should be discarded in favor of |Marge1| which is + // verified. |Homer4| and |Barney| should not be deduped at all. + EXPECT_TRUE(personal_data_manager_cleaner_->ApplyDedupingRoutineForTesting()); + WaitForOnPersonalDataChanged(); + + // Get the profiles, sorted by ranking score to have a deterministic order. + std::vector<AutofillProfile*> profiles = + personal_data_->GetProfilesToSuggest(); + + // The 2 duplicates Homer home profiles with the higher ranking score and the + // unverified Marge profile should have been deduped. + ASSERT_EQ(4U, profiles.size()); + // 7 profiles were considered for dedupe. + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesConsideredForDedupe", 7, 1); + // 3 profile were removed (|Homer1|, |Homer2| and |Marge2|). + histogram_tester.ExpectUniqueSample( + "Autofill.NumberOfProfilesRemovedDuringDedupe", 3, 1); + + // The remaining profiles should be |Homer3|, |Marge1|, |Homer4| and |Barney| + // in this order of ranking score. + EXPECT_EQ(Homer3.guid(), profiles[0]->guid()); + EXPECT_EQ(Marge1.guid(), profiles[1]->guid()); + EXPECT_EQ(Homer4.guid(), profiles[2]->guid()); + EXPECT_EQ(Barney.guid(), profiles[3]->guid()); + + // |Homer3|'s data: + // The address should be saved with the syntax of |Homer1| since it has the + // highest ranking score. + EXPECT_EQ(u"742. Evergreen Terrace", + profiles[0]->GetRawInfo(ADDRESS_HOME_LINE1)); + // The middle name should be the full version found in |Homer2|, + EXPECT_EQ(u"Jay", profiles[0]->GetRawInfo(NAME_MIDDLE)); + // The phone number from |Homer2| should be kept (no loss of information). + EXPECT_EQ(u"12345678910", profiles[0]->GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); + // The company name from |Homer3| should be kept (no loss of information). + EXPECT_EQ(u"Fox", profiles[0]->GetRawInfo(COMPANY_NAME)); + // The country from |Homer1| profile should be kept (no loss of information). + EXPECT_EQ(u"US", profiles[0]->GetRawInfo(ADDRESS_HOME_COUNTRY)); + // The use count that results from the merge should be the max of Homer 1, 2 + // and 3's respective use counts. + EXPECT_EQ(10U, profiles[0]->use_count()); + // The use date that results from the merge should be the one from the + // |Homer1| since it was the most recently used profile. + EXPECT_LT(Homer1.use_date() - base::Seconds(5), profiles[0]->use_date()); + EXPECT_GT(Homer1.use_date() + base::Seconds(5), profiles[0]->use_date()); + + // The other profiles should not have been modified. + EXPECT_TRUE(Marge1 == *profiles[1]); + EXPECT_TRUE(Homer4 == *profiles[2]); + EXPECT_TRUE(Barney == *profiles[3]); +} + +TEST_F(PersonalDataManagerCleanerTest, ApplyDedupingRoutine_NopIfZeroProfiles) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + EXPECT_TRUE(personal_data_->GetProfiles().empty()); + EXPECT_FALSE( + personal_data_manager_cleaner_->ApplyDedupingRoutineForTesting()); +} + +TEST_F(PersonalDataManagerCleanerTest, ApplyDedupingRoutine_NopIfOneProfile) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + + // Create a profile to dedupe. + AutofillProfile profile(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + + AddProfileToPersonalDataManager(profile); + + EXPECT_EQ(1U, personal_data_->GetProfiles().size()); + EXPECT_FALSE( + personal_data_manager_cleaner_->ApplyDedupingRoutineForTesting()); +} + +// Tests that ApplyDedupingRoutine is not run a second time on the same major +// version. +TEST_F(PersonalDataManagerCleanerTest, ApplyDedupingRoutine_OncePerVersion) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); + + // Create a profile to dedupe. + AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + + // Create a similar profile. + AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile2, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + + AddProfileToPersonalDataManager(profile1); + AddProfileToPersonalDataManager(profile2); + + EXPECT_EQ(2U, personal_data_->GetProfiles().size()); + + // The deduping routine should be run a first time. + EXPECT_TRUE(personal_data_manager_cleaner_->ApplyDedupingRoutineForTesting()); + WaitForOnPersonalDataChanged(); + + std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); + + // The profiles should have been deduped + EXPECT_EQ(1U, profiles.size()); + + // Add another duplicate profile. + AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile3, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + + AddProfileToPersonalDataManager(profile3); + + // Make sure |profile3| was saved. + EXPECT_EQ(2U, personal_data_->GetProfiles().size()); + + // The deduping routine should not be run. + EXPECT_FALSE( + personal_data_manager_cleaner_->ApplyDedupingRoutineForTesting()); + + // The two duplicate profiles should still be present. + EXPECT_EQ(2U, personal_data_->GetProfiles().size()); +} + +// Tests that settings-inaccessible profile values are removed from every stored +// profile on startup. +TEST_F(PersonalDataManagerCleanerTest, + RemoveInaccessibleProfileValuesOnStartup) { + base::test::ScopedFeatureList feature; + feature.InitAndEnableFeatureWithParameters( + features::kAutofillRemoveInaccessibleProfileValues, + {{features::kAutofillRemoveInaccessibleProfileValuesOnStartup.name, + "true"}}); + + // Add a German and a US profile. + AutofillProfile profile0(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile0, "Marion", "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", + "Hollywood", "CA", "91601", "DE", "12345678910"); + AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile1, "Josephine", "Alicia", "Saenz", + "joewayne@me.xyz", "Fox", "903 Apple Ct.", nullptr, + "Orlando", "FL", "32801", "US", "19482937549"); + AddProfileToPersonalDataManager(profile0); + AddProfileToPersonalDataManager(profile1); + + personal_data_manager_cleaner_->RemoveInaccessibleProfileValuesForTesting(); + WaitForOnPersonalDataChanged(); + + // profile0 should have it's state removed, while the US profile should remain + // unchanged. + profile0.SetRawInfo(ADDRESS_HOME_STATE, u""); + std::vector<AutofillProfile*> expected_profiles = {&profile0, &profile1}; + EXPECT_THAT(personal_data_->GetProfiles(), + HasSameElements(expected_profiles)); +} + +// Tests that DeleteDisusedAddresses only deletes the addresses that are +// supposed to be deleted. +TEST_F(PersonalDataManagerCleanerTest, + DeleteDisusedAddresses_DeleteDesiredAddressesOnly) { + auto now = AutofillClock::Now(); + + // Create unverified/disused/not-used-by-valid-credit-card + // address(deletable). + AutofillProfile profile0(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile0, "Alice", "", "Delete", "", "ACME", + "1234 Evergreen Terrace", "Bld. 6", "Springfield", "IL", + "32801", "US", "15151231234"); + profile0.set_use_date(now - base::Days(400)); + AddProfileToPersonalDataManager(profile0); + + // Create unverified/disused/used-by-expired-credit-card address(deletable). + AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile1, "Bob", "", "Delete", "", "ACME", + "1234 Evergreen Terrace", "Bld. 7", "Springfield", "IL", + "32801", "US", "15151231234"); + profile1.set_use_date(now - base::Days(400)); + CreditCard credit_card0(base::GenerateGUID(), test::kEmptyOrigin); + test::SetCreditCardInfo(&credit_card0, "Bob", + "5105105105105100" /* Mastercard */, "04", "1999", + "1"); + credit_card0.set_use_date(now - base::Days(400)); + credit_card0.set_billing_address_id(profile1.guid()); + AddProfileToPersonalDataManager(profile1); + personal_data_->AddCreditCard(credit_card0); + WaitForOnPersonalDataChanged(); + // Create verified/disused/not-used-by-valid-credit-card address(not + // deletable). + AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile2, "Charlie", "", "Keep", "", "ACME", + "1234 Evergreen Terrace", "Bld. 8", "Springfield", "IL", + "32801", "US", "15151231234"); + profile2.set_origin(kSettingsOrigin); + profile2.set_use_date(now - base::Days(400)); + AddProfileToPersonalDataManager(profile2); + + // Create unverified/recently-used/not-used-by-valid-credit-card address(not + // deletable). + AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile3, "Dave", "", "Keep", "", "ACME", + "1234 Evergreen Terrace", "Bld. 9", "Springfield", "IL", + "32801", "US", "15151231234"); + profile3.set_use_date(now - base::Days(4)); + AddProfileToPersonalDataManager(profile3); + + // Create unverified/disused/used-by-valid-credit-card address(not deletable). + AutofillProfile profile4(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile4, "Emma", "", "Keep", "", "ACME", + "1234 Evergreen Terrace", "Bld. 10", "Springfield", "IL", + "32801", "US", "15151231234"); + profile4.set_use_date(now - base::Days(400)); + CreditCard credit_card1(CreditCard::MASKED_SERVER_CARD, "c987"); + test::SetCreditCardInfo(&credit_card1, "Emma", "6543", "01", "2999", "1"); + credit_card1.SetNetworkForMaskedCard(kVisaCard); + credit_card1.set_billing_address_id(profile4.guid()); + credit_card1.set_use_date(now - base::Days(1)); + AddProfileToPersonalDataManager(profile4); + personal_data_->AddCreditCard(credit_card1); + + WaitForOnPersonalDataChanged(); + + EXPECT_EQ(5U, personal_data_->GetProfiles().size()); + EXPECT_EQ(2U, personal_data_->GetCreditCards().size()); + + // DeleteDisusedAddresses should return true. + EXPECT_TRUE( + personal_data_manager_cleaner_->DeleteDisusedAddressesForTesting()); + WaitForOnPersonalDataChanged(); + + EXPECT_EQ(3U, personal_data_->GetProfiles().size()); + EXPECT_EQ(2U, personal_data_->GetCreditCards().size()); + EXPECT_EQ(u"Keep", personal_data_->GetProfiles()[0]->GetRawInfo(NAME_LAST)); + EXPECT_EQ(u"Keep", personal_data_->GetProfiles()[1]->GetRawInfo(NAME_LAST)); + EXPECT_EQ(u"Keep", personal_data_->GetProfiles()[2]->GetRawInfo(NAME_LAST)); +} + +// Tests that DeleteDisusedCreditCards deletes desired credit cards only. +TEST_F(PersonalDataManagerCleanerTest, + DeleteDisusedCreditCards_OnlyDeleteExpiredDisusedLocalCards) { + const char kHistogramName[] = "Autofill.CreditCardsDeletedForDisuse"; + auto now = AutofillClock::Now(); + + // Create a recently used local card, it is expected to remain. + CreditCard credit_card1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetCreditCardInfo(&credit_card1, "Alice", + "378282246310005" /* American Express */, "04", + "2999", "1"); + credit_card1.set_use_date(now - base::Days(4)); + + // Create a local card that was expired 400 days ago, but recently used. + // It is expected to remain. + CreditCard credit_card2(base::GenerateGUID(), test::kEmptyOrigin); + test::SetCreditCardInfo(&credit_card2, "Bob", + "378282246310006" /* American Express */, "04", + "1999", "1"); + credit_card2.set_use_date(now - base::Days(4)); + + // Create a local card expired recently, and last used 400 days ago. + // It is expected to remain. + CreditCard credit_card3(base::GenerateGUID(), test::kEmptyOrigin); + base::Time expiry_date = now - base::Days(32); + base::Time::Exploded exploded; + expiry_date.UTCExplode(&exploded); + test::SetCreditCardInfo(&credit_card3, "Clyde", "4111111111111111" /* Visa */, + base::StringPrintf("%02d", exploded.month).c_str(), + base::StringPrintf("%04d", exploded.year).c_str(), + "1"); + credit_card3.set_use_date(now - base::Days(400)); + + // Create a local card expired 400 days ago, and last used 400 days ago. + // It is expected to be deleted. + CreditCard credit_card4(base::GenerateGUID(), test::kEmptyOrigin); + test::SetCreditCardInfo(&credit_card4, "David", + "5105105105105100" /* Mastercard */, "04", "1999", + "1"); + credit_card4.set_use_date(now - base::Days(400)); + personal_data_->AddCreditCard(credit_card1); + personal_data_->AddCreditCard(credit_card2); + personal_data_->AddCreditCard(credit_card3); + personal_data_->AddCreditCard(credit_card4); + + // Create a unmasked server card expired 400 days ago, and last used 400 + // days ago. + // It is expected to remain because we do not delete server cards. + CreditCard credit_card5(CreditCard::FULL_SERVER_CARD, "c789"); + test::SetCreditCardInfo(&credit_card5, "Emma", "4234567890123456" /* Visa */, + "04", "1999", "1"); + credit_card5.set_use_date(now - base::Days(400)); + + // Create masked server card expired 400 days ago, and last used 400 days ago. + // It is expected to remain because we do not delete server cards. + CreditCard credit_card6(CreditCard::MASKED_SERVER_CARD, "c987"); + test::SetCreditCardInfo(&credit_card6, "Frank", "6543", "01", "1998", "1"); + credit_card6.set_use_date(now - base::Days(400)); + credit_card6.SetNetworkForMaskedCard(kVisaCard); + + // Save the server cards and set used_date to desired dates. + std::vector<CreditCard> server_cards; + server_cards.push_back(credit_card5); + server_cards.push_back(credit_card6); + SetServerCards(server_cards); + personal_data_->UpdateServerCardsMetadata({credit_card5, credit_card6}); + + WaitForOnPersonalDataChanged(); + EXPECT_EQ(6U, personal_data_->GetCreditCards().size()); + + // Setup histograms capturing. + base::HistogramTester histogram_tester; + + // DeleteDisusedCreditCards should return true to indicate it was run. + EXPECT_TRUE( + personal_data_manager_cleaner_->DeleteDisusedCreditCardsForTesting()); + + // Wait for the data to be refreshed. + WaitForOnPersonalDataChanged(); + + EXPECT_EQ(5U, personal_data_->GetCreditCards().size()); + std::unordered_set<std::u16string> expectedToRemain = { + u"Alice", u"Bob", u"Clyde", u"Emma", u"Frank"}; + for (auto* card : personal_data_->GetCreditCards()) { + EXPECT_NE(expectedToRemain.end(), + expectedToRemain.find(card->GetRawInfo(CREDIT_CARD_NAME_FULL))); + } + + // Verify histograms are logged. + histogram_tester.ExpectTotalCount(kHistogramName, 1); + histogram_tester.ExpectBucketCount(kHistogramName, 1, 1); +} + +// Tests that all the non settings origins of autofill profiles are cleared but +// that the settings origins are untouched. +TEST_F(PersonalDataManagerCleanerTest, ClearProfileNonSettingsOrigins) { + // Create three profile with a nonsettings, non-empty origin. + AutofillProfile profile0(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(&profile0, "Marion0", "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", + "123 Zoo St.\nSecond Line\nThird line", "unit 5", + "Hollywood", "CA", "91601", "US", "12345678910"); + profile0.set_use_count(10000); + AddProfileToPersonalDataManager(profile0); + + AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetProfileInfo(&profile1, "Marion1", "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", + "123 Zoo St.\nSecond Line\nThird line", "unit 5", + "Hollywood", "CA", "91601", "US", "12345678910"); + profile1.set_use_count(1000); + AddProfileToPersonalDataManager(profile1); + + AutofillProfile profile2(base::GenerateGUID(), "1234"); + test::SetProfileInfo(&profile2, "Marion2", "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", + "123 Zoo St.\nSecond Line\nThird line", "unit 5", + "Hollywood", "CA", "91601", "US", "12345678910"); + profile2.set_use_count(100); + AddProfileToPersonalDataManager(profile2); + + // Create a profile with a settings origin. + AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin); + test::SetProfileInfo(&profile3, "Marion3", "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", + "123 Zoo St.\nSecond Line\nThird line", "unit 5", + "Hollywood", "CA", "91601", "US", "12345678910"); + profile3.set_use_count(10); + AddProfileToPersonalDataManager(profile3); + + ASSERT_EQ(4U, personal_data_->GetProfiles().size()); + + base::RunLoop run_loop; + EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) + .WillRepeatedly(QuitMessageLoop(&run_loop)); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .Times(2); // The setting of profiles 0 and 2 will be cleared. + + personal_data_manager_cleaner_->ClearProfileNonSettingsOriginsForTesting(); + run_loop.Run(); + + ASSERT_EQ(4U, personal_data_->GetProfiles().size()); + + // The first three profiles' origin should be cleared and the fourth one still + // be the settings origin. + EXPECT_TRUE(personal_data_->GetProfilesToSuggest()[0]->origin().empty()); + EXPECT_TRUE(personal_data_->GetProfilesToSuggest()[1]->origin().empty()); + EXPECT_TRUE(personal_data_->GetProfilesToSuggest()[2]->origin().empty()); + EXPECT_EQ(kSettingsOrigin, + personal_data_->GetProfilesToSuggest()[3]->origin()); +} + +// Tests that all the non settings origins of autofill credit cards are cleared +// but that the settings origins are untouched. +TEST_F(PersonalDataManagerCleanerTest, ClearCreditCardNonSettingsOrigins) { + // Create three cards with a non settings origin. + CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com"); + test::SetCreditCardInfo(&credit_card0, "Bob0", + "5105105105105100" /* Mastercard */, "04", "1999", + "1"); + credit_card0.set_use_count(10000); + personal_data_->AddCreditCard(credit_card0); + + CreditCard credit_card1(base::GenerateGUID(), test::kEmptyOrigin); + test::SetCreditCardInfo(&credit_card1, "Bob1", + "5105105105105101" /* Mastercard */, "04", "1999", + "1"); + credit_card1.set_use_count(1000); + personal_data_->AddCreditCard(credit_card1); + + CreditCard credit_card2(base::GenerateGUID(), "1234"); + test::SetCreditCardInfo(&credit_card2, "Bob2", + "5105105105105102" /* Mastercard */, "04", "1999", + "1"); + credit_card2.set_use_count(100); + personal_data_->AddCreditCard(credit_card2); + + // Create a card with a settings origin. + CreditCard credit_card3(base::GenerateGUID(), kSettingsOrigin); + test::SetCreditCardInfo(&credit_card3, "Bob3", + "5105105105105103" /* Mastercard */, "04", "1999", + "1"); + credit_card3.set_use_count(10); + personal_data_->AddCreditCard(credit_card3); + + WaitForOnPersonalDataChanged(); + ASSERT_EQ(4U, personal_data_->GetCreditCards().size()); + + personal_data_manager_cleaner_->ClearCreditCardNonSettingsOriginsForTesting(); + + WaitForOnPersonalDataChanged(); + ASSERT_EQ(4U, personal_data_->GetCreditCards().size()); + + // The first three profiles' origin should be cleared and the fourth one still + // be the settings origin. + EXPECT_TRUE( + personal_data_->GetCreditCardsToSuggest(false)[0]->origin().empty()); + EXPECT_TRUE( + personal_data_->GetCreditCardsToSuggest(false)[1]->origin().empty()); + EXPECT_TRUE( + personal_data_->GetCreditCardsToSuggest(false)[2]->origin().empty()); + EXPECT_EQ(kSettingsOrigin, + personal_data_->GetCreditCardsToSuggest(false)[3]->origin()); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/personal_data_manager_test_base.cc b/chromium/components/autofill/core/browser/personal_data_manager_test_base.cc new file mode 100644 index 00000000000..e4e784c7e66 --- /dev/null +++ b/chromium/components/autofill/core/browser/personal_data_manager_test_base.cc @@ -0,0 +1,191 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/personal_data_manager_test_base.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_features.h" + +namespace autofill { + +namespace { + +const char kPrimaryAccountEmail[] = "syncuser@example.com"; +const char kSyncTransportAccountEmail[] = "transport@example.com"; + +ACTION_P(QuitMessageLoop, loop) { + loop->Quit(); +} + +} // anonymous namespace + +PersonalDataLoadedObserverMock::PersonalDataLoadedObserverMock() = default; +PersonalDataLoadedObserverMock::~PersonalDataLoadedObserverMock() = default; + +// static +std::vector<base::Feature> +PersonalDataManagerTestBase::GetDefaultEnabledFeatures() { + // Enable account storage by default, some tests will override this to be + // false. + return {features::kAutofillEnableAccountWalletStorage}; +} + +PersonalDataManagerTestBase::PersonalDataManagerTestBase( + const std::vector<base::Feature>& additional_enabled_features) + : identity_test_env_(&test_url_loader_factory_) { + std::vector<base::Feature> all_enabled_features( + PersonalDataManagerTestBase::GetDefaultEnabledFeatures()); + base::ranges::copy(additional_enabled_features, + std::back_inserter(all_enabled_features)); + scoped_features_.InitWithFeatures(all_enabled_features, + /*disabled_features=*/{}); +} + +PersonalDataManagerTestBase::~PersonalDataManagerTestBase() = default; + +void PersonalDataManagerTestBase::SetUpTest() { + OSCryptMocker::SetUp(); + prefs_ = test::PrefServiceForTesting(); + base::FilePath path(WebDatabase::kInMemoryPath); + profile_web_database_ = + new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get()); + + // Hacky: hold onto a pointer but pass ownership. + profile_autofill_table_ = new AutofillTable; + profile_web_database_->AddTable( + std::unique_ptr<WebDatabaseTable>(profile_autofill_table_)); + profile_web_database_->LoadDatabase(); + profile_database_service_ = new AutofillWebDataService( + profile_web_database_, base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get()); + profile_database_service_->Init(base::NullCallback()); + + account_web_database_ = new WebDatabaseService( + base::FilePath(WebDatabase::kInMemoryPath), + base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get()); + account_autofill_table_ = new AutofillTable; + account_web_database_->AddTable( + std::unique_ptr<WebDatabaseTable>(account_autofill_table_)); + account_web_database_->LoadDatabase(); + account_database_service_ = new AutofillWebDataService( + account_web_database_, base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get()); + account_database_service_->Init(base::NullCallback()); + + strike_database_ = std::make_unique<TestInMemoryStrikeDatabase>(); + + test::DisableSystemServices(prefs_.get()); +} + +void PersonalDataManagerTestBase::TearDownTest() { + // Order of destruction is important as BrowserAutofillManager relies on + // PersonalDataManager to be around when it gets destroyed. + test::ReenableSystemServices(); + OSCryptMocker::TearDown(); +} + +void PersonalDataManagerTestBase::ResetPersonalDataManager( + bool is_incognito, + bool use_sync_transport_mode, + PersonalDataManager* personal_data) { + personal_data->Init( + scoped_refptr<AutofillWebDataService>(profile_database_service_), + base::FeatureList::IsEnabled( + features::kAutofillEnableAccountWalletStorage) + ? scoped_refptr<AutofillWebDataService>(account_database_service_) + : nullptr, + prefs_.get(), prefs_.get(), identity_test_env_.identity_manager(), + /*history_service=*/nullptr, strike_database_.get(), + /*image_fetcher=*/nullptr, is_incognito); + + personal_data->AddObserver(&personal_data_observer_); + std::string email = use_sync_transport_mode ? kSyncTransportAccountEmail + : kPrimaryAccountEmail; + // Set the account in both IdentityManager and SyncService. + CoreAccountInfo account_info; + signin::ConsentLevel consent_level = use_sync_transport_mode + ? signin::ConsentLevel::kSignin + : signin::ConsentLevel::kSync; +#if !BUILDFLAG(IS_CHROMEOS_ASH) + identity_test_env_.ClearPrimaryAccount(); + account_info = identity_test_env_.SetPrimaryAccount(email, consent_level); +#else + // In ChromeOS-Ash, clearing/resetting the primary account is not supported. + // So if an account already exists, reuse it (and make sure it matches). + if (identity_test_env_.identity_manager()->HasPrimaryAccount(consent_level)) { + account_info = identity_test_env_.identity_manager()->GetPrimaryAccountInfo( + consent_level); + ASSERT_EQ(account_info.email, email); + } else { + account_info = identity_test_env_.SetPrimaryAccount(email, consent_level); + } +#endif + sync_service_.SetAccountInfo(account_info); + sync_service_.SetHasSyncConsent(!use_sync_transport_mode); + personal_data->OnSyncServiceInitialized(&sync_service_); + personal_data->OnStateChanged(&sync_service_); + + WaitForOnPersonalDataChangedRepeatedly(); +} + +[[nodiscard]] bool PersonalDataManagerTestBase::TurnOnSyncFeature( + PersonalDataManager* personal_data) { + sync_service_.SetHasSyncConsent(true); + if (!sync_service_.IsSyncFeatureEnabled()) + return false; + personal_data->OnStateChanged(&sync_service_); + + return personal_data->IsSyncFeatureEnabled(); +} + +void PersonalDataManagerTestBase::RemoveByGUIDFromPersonalDataManager( + const std::string& guid, + PersonalDataManager* personal_data) { + base::RunLoop run_loop; + EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) + .WillOnce(QuitMessageLoop(&run_loop)); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .Times(testing::AnyNumber()); + + personal_data->RemoveByGUID(guid); + run_loop.Run(); +} + +void PersonalDataManagerTestBase::SetServerCards( + std::vector<CreditCard> server_cards) { + test::SetServerCreditCards(account_autofill_table_, server_cards); +} + +// Verify that the web database has been updated and the notification sent. +void PersonalDataManagerTestBase::WaitOnceForOnPersonalDataChanged() { + base::RunLoop run_loop; + EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) + .WillOnce(QuitMessageLoop(&run_loop)); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1); + run_loop.Run(); +} + +// Verifies that the web database has been updated and the notification sent. +void PersonalDataManagerTestBase::WaitForOnPersonalDataChanged() { + base::RunLoop run_loop; + EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) + .WillOnce(QuitMessageLoop(&run_loop)); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .Times(testing::AnyNumber()); + run_loop.Run(); +} + +// Verifies that the web database has been updated and the notification sent. +void PersonalDataManagerTestBase::WaitForOnPersonalDataChangedRepeatedly() { + base::RunLoop run_loop; + EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) + .WillRepeatedly(QuitMessageLoop(&run_loop)); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .Times(testing::AnyNumber()); + run_loop.Run(); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/personal_data_manager_test_base.h b/chromium/components/autofill/core/browser/personal_data_manager_test_base.h new file mode 100644 index 00000000000..50a0ec2eef4 --- /dev/null +++ b/chromium/components/autofill/core/browser/personal_data_manager_test_base.h @@ -0,0 +1,87 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_TEST_BASE_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_TEST_BASE_H_ + +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "components/autofill/core/browser/personal_data_manager_observer.h" +#include "components/autofill/core/browser/test_inmemory_strike_database.h" +#include "components/autofill/core/browser/webdata/autofill_table.h" +#include "components/autofill/core/browser/webdata/autofill_webdata_service.h" +#include "components/autofill/core/common/autofill_clock.h" +#include "components/os_crypt/os_crypt_mocker.h" +#include "components/prefs/pref_service.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" +#include "components/sync/driver/test_sync_service.h" +#include "components/webdata/common/web_data_service_base.h" +#include "components/webdata/common/web_database_service.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace autofill { + +class PersonalDataManager; + +class PersonalDataLoadedObserverMock : public PersonalDataManagerObserver { + public: + PersonalDataLoadedObserverMock(); + ~PersonalDataLoadedObserverMock() override; + + MOCK_METHOD(void, OnPersonalDataChanged, (), (override)); + MOCK_METHOD(void, OnPersonalDataFinishedProfileTasks, (), (override)); +}; + +class PersonalDataManagerTestBase { + protected: + static std::vector<base::Feature> GetDefaultEnabledFeatures(); + + explicit PersonalDataManagerTestBase( + const std::vector<base::Feature>& additional_enabled_features = {}); + + ~PersonalDataManagerTestBase(); + + void SetUpTest(); + void TearDownTest(); + + void ResetPersonalDataManager(bool is_incognito, + bool use_sync_transport_mode, + PersonalDataManager* personal_data); + + [[nodiscard]] bool TurnOnSyncFeature(PersonalDataManager* personal_data); + + void RemoveByGUIDFromPersonalDataManager(const std::string& guid, + PersonalDataManager* personal_data); + + void SetServerCards(std::vector<CreditCard> server_cards); + + // Verify that the web database has been updated and the notification sent. + void WaitOnceForOnPersonalDataChanged(); + + // Verifies that the web database has been updated and the notification sent. + void WaitForOnPersonalDataChanged(); + + // Verifies that the web database has been updated and the notification sent. + void WaitForOnPersonalDataChangedRepeatedly(); + + base::test::TaskEnvironment task_environment_; + std::unique_ptr<PrefService> prefs_; + base::test::ScopedFeatureList scoped_features_; + network::TestURLLoaderFactory test_url_loader_factory_; + signin::IdentityTestEnvironment identity_test_env_; + syncer::TestSyncService sync_service_; + scoped_refptr<AutofillWebDataService> profile_database_service_; + scoped_refptr<AutofillWebDataService> account_database_service_; + scoped_refptr<WebDatabaseService> profile_web_database_; + scoped_refptr<WebDatabaseService> account_web_database_; + raw_ptr<AutofillTable> profile_autofill_table_; // weak ref + raw_ptr<AutofillTable> account_autofill_table_; // weak ref + std::unique_ptr<StrikeDatabaseBase> strike_database_; + PersonalDataLoadedObserverMock personal_data_observer_; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_TEST_BASE_H_ diff --git a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc index c0a220df582..ba2d3526528 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc @@ -14,21 +14,15 @@ #include <utility> #include <vector> -#include "base/base64.h" #include "base/callback_helpers.h" -#include "base/command_line.h" #include "base/containers/contains.h" #include "base/guid.h" -#include "base/i18n/time_formatting.h" #include "base/memory/raw_ptr.h" -#include "base/rand_util.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/synchronization/waitable_event.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" -#include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -43,15 +37,12 @@ #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" -#include "components/autofill/core/browser/personal_data_manager_observer.h" +#include "components/autofill/core/browser/personal_data_manager_test_base.h" #include "components/autofill/core/browser/sync_utils.h" #include "components/autofill/core/browser/test_autofill_clock.h" -#include "components/autofill/core/browser/test_inmemory_strike_database.h" #include "components/autofill/core/browser/ui/label_formatter_utils.h" #include "components/autofill/core/browser/ui/suggestion.h" #include "components/autofill/core/browser/ui/suggestion_selection.h" -#include "components/autofill/core/browser/webdata/autofill_table.h" -#include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_features.h" @@ -59,24 +50,16 @@ #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/form_data.h" -#include "components/os_crypt/os_crypt_mocker.h" -#include "components/prefs/pref_service.h" #include "components/signin/public/base/signin_switches.h" #include "components/signin/public/identity_manager/identity_test_environment.h" -#include "components/sync/driver/sync_service_utils.h" #include "components/sync/driver/test_sync_service.h" #include "components/version_info/version_info.h" -#include "components/webdata/common/web_data_service_base.h" -#include "components/webdata/common/web_database_service.h" -#include "google_apis/gaia/google_service_auth_error.h" -#include "services/network/test/test_url_loader_factory.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/image/image_unittest_util.h" namespace autofill { -using structured_address::HonorificPrefixEnabled; using structured_address::StructuredAddressesEnabled; using structured_address::StructuredNamesEnabled; @@ -84,7 +67,6 @@ namespace { const char kPrimaryAccountEmail[] = "syncuser@example.com"; const char16_t kPrimaryAccountEmail16[] = u"syncuser@example.com"; -const char kSyncTransportAccountEmail[] = "transport@example.com"; enum UserMode { USER_MODE_NORMAL, USER_MODE_INCOGNITO }; @@ -96,15 +78,6 @@ ACTION_P(QuitMessageLoop, loop) { loop->Quit(); } -class PersonalDataLoadedObserverMock : public PersonalDataManagerObserver { - public: - PersonalDataLoadedObserverMock() = default; - ~PersonalDataLoadedObserverMock() override = default; - - MOCK_METHOD(void, OnPersonalDataChanged, (), (override)); - MOCK_METHOD(void, OnPersonalDataFinishedProfileTasks, (), (override)); -}; - class PersonalDataManagerMock : public PersonalDataManager { public: explicit PersonalDataManagerMock(const std::string& app_locale, @@ -149,195 +122,8 @@ void ExpectSameElements(const std::vector<T*>& expectations, results_copy.end()); } -class ScopedFeatureListWrapper { - public: - explicit ScopedFeatureListWrapper( - const std::vector<base::Feature>& default_enabled_features, - const std::vector<base::Feature>& additional_enabled_features) { - std::vector<base::Feature> all_enabled_features(default_enabled_features); - std::copy(additional_enabled_features.begin(), - additional_enabled_features.end(), - std::back_inserter(all_enabled_features)); - scoped_features_.InitWithFeatures(all_enabled_features, - /*disabled_features=*/{}); - } - ~ScopedFeatureListWrapper() = default; - - private: - base::test::ScopedFeatureList scoped_features_; -}; - } // anonymous namespace -class PersonalDataManagerTestBase { - protected: - static std::vector<base::Feature> GetDefaultEnabledFeatures() { - // Enable account storage by default, some tests will override this to be - // false. - return {features::kAutofillEnableAccountWalletStorage}; - } - - PersonalDataManagerTestBase() - : scoped_features_( - PersonalDataManagerTestBase::GetDefaultEnabledFeatures(), - /*additional_enabled_features=*/{}), - identity_test_env_(&test_url_loader_factory_) {} - - explicit PersonalDataManagerTestBase( - const std::vector<base::Feature>& additioanal_enabled_features) - : scoped_features_( - PersonalDataManagerTestBase::GetDefaultEnabledFeatures(), - additioanal_enabled_features), - identity_test_env_(&test_url_loader_factory_) {} - - void SetUpTest() { - OSCryptMocker::SetUp(); - prefs_ = test::PrefServiceForTesting(); - base::FilePath path(WebDatabase::kInMemoryPath); - profile_web_database_ = - new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get()); - - // Hacky: hold onto a pointer but pass ownership. - profile_autofill_table_ = new AutofillTable; - profile_web_database_->AddTable( - std::unique_ptr<WebDatabaseTable>(profile_autofill_table_)); - profile_web_database_->LoadDatabase(); - profile_database_service_ = new AutofillWebDataService( - profile_web_database_, base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get()); - profile_database_service_->Init(base::NullCallback()); - - account_web_database_ = - new WebDatabaseService(base::FilePath(WebDatabase::kInMemoryPath), - base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get()); - account_autofill_table_ = new AutofillTable; - account_web_database_->AddTable( - std::unique_ptr<WebDatabaseTable>(account_autofill_table_)); - account_web_database_->LoadDatabase(); - account_database_service_ = new AutofillWebDataService( - account_web_database_, base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get()); - account_database_service_->Init(base::NullCallback()); - - strike_database_ = std::make_unique<TestInMemoryStrikeDatabase>(); - - test::DisableSystemServices(prefs_.get()); - } - - void TearDownTest() { - // Order of destruction is important as BrowserAutofillManager relies on - // PersonalDataManager to be around when it gets destroyed. - test::ReenableSystemServices(); - OSCryptMocker::TearDown(); - } - - void ResetPersonalDataManager(UserMode user_mode, - bool use_sync_transport_mode, - PersonalDataManager* personal_data) { - bool is_incognito = (user_mode == USER_MODE_INCOGNITO); - - personal_data->Init( - scoped_refptr<AutofillWebDataService>(profile_database_service_), - base::FeatureList::IsEnabled( - features::kAutofillEnableAccountWalletStorage) - ? scoped_refptr<AutofillWebDataService>(account_database_service_) - : nullptr, - prefs_.get(), prefs_.get(), identity_test_env_.identity_manager(), - /*history_service=*/nullptr, strike_database_.get(), - /*image_fetcher=*/nullptr, is_incognito); - - personal_data->AddObserver(&personal_data_observer_); - AccountInfo account_info; - account_info.email = use_sync_transport_mode ? kSyncTransportAccountEmail - : kPrimaryAccountEmail; - sync_service_.SetAccountInfo(account_info); - sync_service_.SetHasSyncConsent(!use_sync_transport_mode); - personal_data->OnSyncServiceInitialized(&sync_service_); - personal_data->OnStateChanged(&sync_service_); - - WaitForOnPersonalDataChangedRepeatedly(); - } - - [[nodiscard]] bool TurnOnSyncFeature(PersonalDataManager* personal_data) { - sync_service_.SetHasSyncConsent(true); - if (!sync_service_.IsSyncFeatureEnabled()) - return false; - personal_data->OnStateChanged(&sync_service_); - - return personal_data->IsSyncFeatureEnabled(); - } - - void RemoveByGUIDFromPersonalDataManager(const std::string& guid, - PersonalDataManager* personal_data) { - base::RunLoop run_loop; - EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) - .WillOnce(QuitMessageLoop(&run_loop)); - EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) - .Times(testing::AnyNumber()); - - personal_data->RemoveByGUID(guid); - run_loop.Run(); - } - - void SetServerCards(std::vector<CreditCard> server_cards) { - test::SetServerCreditCards(account_autofill_table_, server_cards); - } - - // Verify that the web database has been updated and the notification sent. - void WaitOnceForOnPersonalDataChanged() { - base::RunLoop run_loop; - EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) - .WillOnce(QuitMessageLoop(&run_loop)); - EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1); - run_loop.Run(); - } - - // Verifies that the web database has been updated and the notification sent. - void WaitForOnPersonalDataChanged() { - base::RunLoop run_loop; - EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) - .WillOnce(QuitMessageLoop(&run_loop)); - EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) - .Times(testing::AnyNumber()); - run_loop.Run(); - } - - // Verifies that the web database has been updated and the notification sent. - void WaitForOnPersonalDataChangedRepeatedly() { - base::RunLoop run_loop; - EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) - .WillRepeatedly(QuitMessageLoop(&run_loop)); - EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) - .Times(testing::AnyNumber()); - run_loop.Run(); - } - - AccountInfo SetActiveSecondaryAccount() { - AccountInfo account_info; - account_info.email = kSyncTransportAccountEmail; - account_info.account_id = CoreAccountId("account_id"); - sync_service_.SetAccountInfo(account_info); - sync_service_.SetHasSyncConsent(false); - return account_info; - } - base::test::TaskEnvironment task_environment_; - std::unique_ptr<PrefService> prefs_; - ScopedFeatureListWrapper scoped_features_; - network::TestURLLoaderFactory test_url_loader_factory_; - signin::IdentityTestEnvironment identity_test_env_; - syncer::TestSyncService sync_service_; - scoped_refptr<AutofillWebDataService> profile_database_service_; - scoped_refptr<AutofillWebDataService> account_database_service_; - scoped_refptr<WebDatabaseService> profile_web_database_; - scoped_refptr<WebDatabaseService> account_web_database_; - raw_ptr<AutofillTable> profile_autofill_table_; // weak ref - raw_ptr<AutofillTable> account_autofill_table_; // weak ref - std::unique_ptr<StrikeDatabaseBase> strike_database_; - PersonalDataLoadedObserverMock personal_data_observer_; -}; - class PersonalDataManagerHelper : public PersonalDataManagerTestBase { protected: PersonalDataManagerHelper() = default; @@ -353,12 +139,13 @@ class PersonalDataManagerHelper : public PersonalDataManagerTestBase { } void ResetPersonalDataManager(UserMode user_mode, - bool use_account_server_storage = false) { + bool use_sync_transport_mode = false) { if (personal_data_) personal_data_->Shutdown(); personal_data_ = std::make_unique<PersonalDataManager>("EN", "US"); PersonalDataManagerTestBase::ResetPersonalDataManager( - user_mode, use_account_server_storage, personal_data_.get()); + user_mode == USER_MODE_INCOGNITO, use_sync_transport_mode, + personal_data_.get()); } void ResetProfiles() { @@ -576,6 +363,18 @@ class PersonalDataManagerTest : public PersonalDataManagerHelper, void TearDown() override { TearDownTest(); } }; +class PersonalDataManagerSyncTransportModeTest + : public PersonalDataManagerHelper, + public testing::Test { + protected: + void SetUp() override { + SetUpTest(); + ResetPersonalDataManager(USER_MODE_NORMAL, + /*use_sync_transport_mode=*/true); + } + void TearDown() override { TearDownTest(); } +}; + #if BUILDFLAG(IS_CHROMEOS_ASH) class PersonalDataManagerMigrationTest : public PersonalDataManagerHelper, public testing::Test { @@ -612,7 +411,8 @@ class PersonalDataManagerMockTest : public PersonalDataManagerTestBase, personal_data_ = std::make_unique<PersonalDataManagerMock>("en", std::string()); PersonalDataManagerTestBase::ResetPersonalDataManager( - user_mode, /*use_sync_transport_mode=*/true, personal_data_.get()); + user_mode == USER_MODE_INCOGNITO, /*use_sync_transport_mode=*/true, + personal_data_.get()); } bool TurnOnSyncFeature() { @@ -985,6 +785,80 @@ TEST_F(PersonalDataManagerTest, AddUpdateRemoveProfiles) { ExpectSameElements(profiles, personal_data_->GetProfiles()); } +TEST_F(PersonalDataManagerTest, NoIBANsAddedIfDisabled) { + prefs::SetAutofillIBANEnabled(prefs_.get(), false); + IBAN iban0(base::GenerateGUID()); + iban0.set_value(u"IE12 BOFI 9000 0112 3456 78"); + iban0.set_nickname(u"Nickname 0"); + + IBAN iban1(base::GenerateGUID()); + iban1.set_value(u"DE91 1000 0000 0123 4567 89"); + iban1.set_nickname(u"Nickname 1"); + + personal_data_->AddIBAN(iban0); + personal_data_->AddIBAN(iban1); + + EXPECT_EQ(0U, personal_data_->GetIBANs().size()); +} + +TEST_F(PersonalDataManagerTest, AddUpdateRemoveIbans) { + prefs::SetAutofillIBANEnabled(prefs_.get(), true); + IBAN iban0(base::GenerateGUID()); + iban0.set_value(u"IE12 BOFI 9000 0112 3456 78"); + iban0.set_nickname(u"Nickname 0"); + + IBAN iban1(base::GenerateGUID()); + iban1.set_value(u"DE91 1000 0000 0123 4567 89"); + iban1.set_nickname(u"Nickname 1"); + + IBAN iban2(base::GenerateGUID()); + iban2.set_value(u"ES79 2100 0813 6101 2345 6789"); + iban2.set_nickname(u"Nickname 2"); + + // Add two test IBANs to the database. + personal_data_->AddIBAN(iban0); + personal_data_->AddIBAN(iban1); + + WaitForOnPersonalDataChanged(); + + std::vector<IBAN*> ibans; + ibans.push_back(&iban0); + ibans.push_back(&iban1); + ExpectSameElements(ibans, personal_data_->GetIBANs()); + + // Update IBAN0, remove IBAN1, and add IBAN2. + iban0.set_nickname(u"Nickname new 0"); + iban0.SetRawInfo(IBAN_VALUE, u"GB98 MIDL 0700 9312 3456 78"); + personal_data_->UpdateIBAN(iban0); + RemoveByGUIDFromPersonalDataManager(iban1.guid()); + personal_data_->AddIBAN(iban2); + + WaitForOnPersonalDataChanged(); + + ibans.clear(); + ibans.push_back(&iban0); + ibans.push_back(&iban2); + ExpectSameElements(ibans, personal_data_->GetIBANs()); + + // Verify that a duplicate IBAN should not be added. + IBAN iban0_dup = iban0; + personal_data_->AddIBAN(iban0_dup); + ibans.clear(); + ibans.push_back(&iban0); + ibans.push_back(&iban2); + ExpectSameElements(ibans, personal_data_->GetIBANs()); + + // Reset the PersonalDataManager. This tests that the personal data was saved + // to the web database, and that we can load the IBANs from the web database. + ResetPersonalDataManager(USER_MODE_NORMAL); + + // Verify that we've reloaded the IBANs from the web database. + ibans.clear(); + ibans.push_back(&iban0); + ibans.push_back(&iban2); + ExpectSameElements(ibans, personal_data_->GetIBANs()); +} + TEST_F(PersonalDataManagerTest, AddUpdateRemoveCreditCards) { CreditCard credit_card0(base::GenerateGUID(), test::kEmptyOrigin); test::SetCreditCardInfo(&credit_card0, "John Dillinger", @@ -1331,6 +1205,8 @@ TEST_F(PersonalDataManagerTest, UpdateVerifiedProfilesOrigin) { } // Test that ensure local data is not lost on sign-in. +// Clearing/changing the primary account is not supported on CrOS. +#if !BUILDFLAG(IS_CHROMEOS_ASH) TEST_F(PersonalDataManagerTest, KeepExistingLocalDataOnSignIn) { // Set up the experiment flags. base::test::ScopedFeatureList scoped_features; @@ -1338,13 +1214,10 @@ TEST_F(PersonalDataManagerTest, KeepExistingLocalDataOnSignIn) { /*enabled_features=*/{features::kAutofillEnableAccountWalletStorage}, /*disabled_features=*/{}); -// ClearPrimaryAccount is not supported on CrOS. -#if !BUILDFLAG(IS_CHROMEOS_ASH) // Sign out. identity_test_env_.ClearPrimaryAccount(); EXPECT_EQ(AutofillSyncSigninState::kSignedOut, personal_data_->GetSyncSigninState()); -#endif EXPECT_EQ(0U, personal_data_->GetCreditCards().size()); // Add local card. @@ -1373,6 +1246,7 @@ TEST_F(PersonalDataManagerTest, KeepExistingLocalDataOnSignIn) { EXPECT_EQ(1U, personal_data_->GetCreditCards().size()); EXPECT_EQ(0, local_card.Compare(*personal_data_->GetCreditCards()[0])); } +#endif TEST_F(PersonalDataManagerTest, AddProfilesAndCreditCards) { AutofillProfile profile0(base::GenerateGUID(), test::kEmptyOrigin); @@ -1668,43 +1542,36 @@ TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) { // Make sure everything is set up correctly. EXPECT_EQ(1U, personal_data_->GetProfiles().size()); - personal_data_->GetNonEmptyTypes(&non_empty_types); + std::vector<ServerFieldType> expected_types{NAME_FIRST, + NAME_LAST, + NAME_FULL, + EMAIL_ADDRESS, + ADDRESS_HOME_LINE1, + ADDRESS_HOME_STREET_ADDRESS, + ADDRESS_HOME_CITY, + ADDRESS_HOME_STATE, + ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY, + PHONE_HOME_NUMBER, + PHONE_HOME_NUMBER_PREFIX, + PHONE_HOME_NUMBER_SUFFIX, + PHONE_HOME_COUNTRY_CODE, + PHONE_HOME_CITY_CODE, + PHONE_HOME_CITY_AND_NUMBER, + PHONE_HOME_WHOLE_NUMBER}; // For structured names and addresses, there are more non-empty types. // TODO(crbug.com/1103421): Clean once launched. - unsigned int non_empty_types_expectation = 15; if (StructuredNamesEnabled()) - non_empty_types_expectation += 1; - // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddressesEnabled()) - non_empty_types_expectation += 2; - if (HonorificPrefixEnabled()) - non_empty_types_expectation += 1; - - EXPECT_EQ(non_empty_types_expectation, non_empty_types.size()); - - EXPECT_TRUE(non_empty_types.count(NAME_FIRST)); - EXPECT_TRUE(non_empty_types.count(NAME_LAST)); - // TODO(crbug.com/1103421): Clean once launched. - if (StructuredNamesEnabled()) - EXPECT_TRUE(non_empty_types.count(NAME_LAST_SECOND)); - EXPECT_TRUE(non_empty_types.count(NAME_FULL)); - EXPECT_TRUE(non_empty_types.count(EMAIL_ADDRESS)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE1)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_ADDRESS)); + expected_types.push_back(NAME_LAST_SECOND); // TODO(crbug.com/1130194): Clean once launched. if (StructuredAddressesEnabled()) { - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_NAME)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_HOUSE_NUMBER)); + expected_types.insert(expected_types.end(), {ADDRESS_HOME_STREET_NAME, + ADDRESS_HOME_HOUSE_NUMBER}); } - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_CITY)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STATE)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_ZIP)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_COUNTRY)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_NUMBER)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_COUNTRY_CODE)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_CODE)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_AND_NUMBER)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_WHOLE_NUMBER)); + + personal_data_->GetNonEmptyTypes(&non_empty_types); + EXPECT_THAT(non_empty_types, + testing::UnorderedElementsAreArray(expected_types)); // Test with multiple profiles stored. AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); @@ -1722,44 +1589,13 @@ TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) { EXPECT_EQ(3U, personal_data_->GetProfiles().size()); + expected_types.insert( + expected_types.end(), + {NAME_MIDDLE, NAME_MIDDLE_INITIAL, ADDRESS_HOME_LINE2, COMPANY_NAME}); + personal_data_->GetNonEmptyTypes(&non_empty_types); - non_empty_types_expectation = 19; - // For structured names, there is one more non-empty type. - // TODO(crbug.com/1103421): Clean once launched. - if (StructuredNamesEnabled()) - non_empty_types_expectation += 1; - if (HonorificPrefixEnabled()) - non_empty_types_expectation += 1; - // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddressesEnabled()) - non_empty_types_expectation += 2; - EXPECT_EQ(non_empty_types_expectation, non_empty_types.size()); - EXPECT_TRUE(non_empty_types.count(NAME_FIRST)); - EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE)); - EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE_INITIAL)); - // TODO(crbug.com/1103421): Clean once launched. - if (StructuredNamesEnabled()) - EXPECT_TRUE(non_empty_types.count(NAME_LAST)); - EXPECT_TRUE(non_empty_types.count(NAME_FULL)); - EXPECT_TRUE(non_empty_types.count(EMAIL_ADDRESS)); - EXPECT_TRUE(non_empty_types.count(COMPANY_NAME)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE1)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE2)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_ADDRESS)); - // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddressesEnabled()) { - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_NAME)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_HOUSE_NUMBER)); - } - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_CITY)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STATE)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_ZIP)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_COUNTRY)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_NUMBER)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_CODE)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_COUNTRY_CODE)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_AND_NUMBER)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_WHOLE_NUMBER)); + EXPECT_THAT(non_empty_types, + testing::UnorderedElementsAreArray(expected_types)); // Test with credit card information also stored. CreditCard credit_card(base::GenerateGUID(), test::kEmptyOrigin); @@ -1770,54 +1606,16 @@ TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) { WaitForOnPersonalDataChanged(); EXPECT_EQ(1U, personal_data_->GetCreditCards().size()); + expected_types.insert( + expected_types.end(), + {CREDIT_CARD_NAME_FULL, CREDIT_CARD_NAME_FIRST, CREDIT_CARD_NAME_LAST, + CREDIT_CARD_NUMBER, CREDIT_CARD_TYPE, CREDIT_CARD_EXP_MONTH, + CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_EXP_4_DIGIT_YEAR, + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}); + personal_data_->GetNonEmptyTypes(&non_empty_types); - // For structured names, there is one more non-empty type. - // TODO(crbug.com/1103421): Clean once launched. - non_empty_types_expectation = 29; - if (StructuredNamesEnabled()) - non_empty_types_expectation += 1; - if (HonorificPrefixEnabled()) - non_empty_types_expectation += 1; - // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddressesEnabled()) - non_empty_types_expectation += 2; - EXPECT_EQ(non_empty_types_expectation, non_empty_types.size()); - EXPECT_TRUE(non_empty_types.count(NAME_FIRST)); - EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE)); - EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE_INITIAL)); - EXPECT_TRUE(non_empty_types.count(NAME_LAST)); - EXPECT_TRUE(non_empty_types.count(NAME_FULL)); - if (StructuredNamesEnabled()) - EXPECT_TRUE(non_empty_types.count(NAME_LAST)); - EXPECT_TRUE(non_empty_types.count(EMAIL_ADDRESS)); - EXPECT_TRUE(non_empty_types.count(COMPANY_NAME)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE1)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE2)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_ADDRESS)); - // TODO(crbug.com/1130194): Clean once launched. - if (StructuredAddressesEnabled()) { - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STREET_NAME)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_HOUSE_NUMBER)); - } - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_CITY)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STATE)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_ZIP)); - EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_COUNTRY)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_NUMBER)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_CODE)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_COUNTRY_CODE)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_AND_NUMBER)); - EXPECT_TRUE(non_empty_types.count(PHONE_HOME_WHOLE_NUMBER)); - EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_NAME_FULL)); - EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_NAME_FIRST)); - EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_NAME_LAST)); - EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_NUMBER)); - EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_TYPE)); - EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_EXP_MONTH)); - EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_EXP_2_DIGIT_YEAR)); - EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_EXP_4_DIGIT_YEAR)); - EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR)); - EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR)); + EXPECT_THAT(non_empty_types, + testing::UnorderedElementsAreArray(expected_types)); } TEST_F(PersonalDataManagerTest, IncognitoReadOnly) { @@ -1920,6 +1718,21 @@ TEST_F(PersonalDataManagerMockTest, GetAutofillOffers_WalletImportDisabled) { EXPECT_EQ(0U, personal_data_->GetAutofillOffers().size()); } +// Tests that GetAutofillOffers does not return any offers if +// |IsAutofillCreditCardEnabled()| returns |false|. +TEST_F(PersonalDataManagerMockTest, + GetAutofillOffers_AutofillCreditCardDisabled) { + // Add a card-linked offer and a promo code offer. + AddOfferDataForTest(test::GetCardLinkedOfferData1()); + AddOfferDataForTest(test::GetPromoCodeOfferData()); + + prefs::SetAutofillCreditCardEnabled(prefs_.get(), false); + + // Should return neither of the offers as the autofill credit card import pref + // is disabled. + EXPECT_EQ(0U, personal_data_->GetAutofillOffers().size()); +} + // Tests that GetActiveAutofillPromoCodeOffersForOrigin returns only active and // site-relevant promo code offers. TEST_F(PersonalDataManagerTest, GetActiveAutofillPromoCodeOffersForOrigin) { @@ -1963,6 +1776,23 @@ TEST_F(PersonalDataManagerMockTest, .size()); } +// Tests that GetActiveAutofillPromoCodeOffersForOrigin does not return any +// promo code offers if |IsAutofillCreditCardEnabled()| returns |false|. +TEST_F(PersonalDataManagerMockTest, + GetActiveAutofillPromoCodeOffersForOrigin_AutofillCreditCardDisabled) { + // Add an active promo code offer. + AddOfferDataForTest(test::GetPromoCodeOfferData( + /*origin=*/GURL("http://www.example.com"))); + + prefs::SetAutofillCreditCardEnabled(prefs_.get(), false); + + // Should not return the offer as the autofill credit card pref is disabled. + EXPECT_EQ(0U, personal_data_ + ->GetActiveAutofillPromoCodeOffersForOrigin( + GURL("http://www.example.com")) + .size()); +} + TEST_F(PersonalDataManagerTest, DefaultCountryCodeIsCached) { // The return value should always be some country code, no matter what. std::string default_country = @@ -1986,6 +1816,7 @@ TEST_F(PersonalDataManagerTest, DefaultCountryCodeIsCached) { // profiles. prefs::SetAutofillProfileEnabled(prefs_.get(), false); prefs::SetAutofillCreditCardEnabled(prefs_.get(), false); + prefs::SetAutofillIBANEnabled(prefs_.get(), false); WaitForOnPersonalDataChanged(); EXPECT_EQ(default_country, personal_data_->GetDefaultCountryCodeForNewAddress()); @@ -3196,8 +3027,8 @@ TEST_F(PersonalDataManagerTest, ASSERT_EQ(0U, card_to_suggest.size()); } -// Test that local profiles are not added if |kAutofillProfileEnabled| is set to -// |false|. +// Test that local credit cards are not added if |kAutofillCreditCardEnabled| is +// set to |false|. TEST_F(PersonalDataManagerTest, GetCreditCardsToSuggest_NoCreditCardsAddedIfDisabled) { // Disable Profile autofill. @@ -3211,7 +3042,7 @@ TEST_F(PersonalDataManagerTest, "1"); personal_data_->AddCreditCard(credit_card); - // Expect no profile values or suggestions were added. + // Expect no credit card values or suggestions were added. EXPECT_EQ(0U, personal_data_->GetCreditCards().size()); } @@ -4054,1090 +3885,6 @@ TEST_F(PersonalDataManagerTest, MAYBE_MergeProfile_UsageStats) { EXPECT_EQ(kMuchLaterTime, profiles[0].modification_date()); } -// Tests that DedupeProfiles sets the correct profile guids to -// delete after merging similar profiles. -TEST_F(PersonalDataManagerTest, DedupeProfiles_ProfilesToDelete) { - // Create the profile for which to find duplicates. It has the highest - // ranking score. - AutofillProfile* profile1 = - new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(profile1, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", "12345678910"); - profile1->set_use_count(9); - - // Create a different profile that should not be deduped (different address). - AutofillProfile* profile2 = - new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(profile2, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "Fox", "1234 Other Street", "", - "Springfield", "IL", "91601", "US", "12345678910"); - profile2->set_use_count(7); - - // Create a profile similar to profile1 which should be deduped. - AutofillProfile* profile3 = - new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(profile3, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "US", "12345678910"); - profile3->set_use_count(5); - - // Create another different profile that should not be deduped (different - // name). - AutofillProfile* profile4 = - new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(profile4, "Marjorie", "Jacqueline", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", "12345678910"); - profile4->set_use_count(3); - - // Create another profile similar to profile1. Since that one has the lowest - // ranking score, the result of the merge should be in this profile at the end - // of the test. - AutofillProfile* profile5 = - new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(profile5, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "US", "12345678910"); - profile5->set_use_count(1); - - // Add the profiles. - std::vector<std::unique_ptr<AutofillProfile>> existing_profiles; - existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile1)); - existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile2)); - existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile3)); - existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile4)); - existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile5)); - - base::HistogramTester histogram_tester; - std::unordered_map<std::string, std::string> guids_merge_map; - std::unordered_set<std::string> profiles_to_delete; - personal_data_->personal_data_manager_cleaner_for_testing() - ->DedupeProfilesForTesting(&existing_profiles, &profiles_to_delete, - &guids_merge_map); - // 5 profiles were considered for dedupe. - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesConsideredForDedupe", 5, 1); - // 2 profiles were removed (profiles 1 and 3). - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1); - - // Profile1 should be deleted because it was sent as the profile to merge and - // thus was merged into profile3 and then into profile5. - EXPECT_TRUE(profiles_to_delete.count(profile1->guid())); - - // Profile3 should be deleted because profile1 was merged into it and the - // resulting profile was then merged into profile5. - EXPECT_TRUE(profiles_to_delete.count(profile3->guid())); - - // Only these two profiles should be deleted. - EXPECT_EQ(2U, profiles_to_delete.size()); - - // All profiles should still be present in |existing_profiles|. - EXPECT_EQ(5U, existing_profiles.size()); -} - -// Tests that DedupeProfiles sets the correct merge mapping for billing address -// id references. -TEST_F(PersonalDataManagerTest, DedupeProfiles_GuidsMergeMap) { - // Create the profile for which to find duplicates. It has the highest - // ranking score. - AutofillProfile* profile1 = - new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(profile1, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", "12345678910"); - profile1->set_use_count(9); - - // Create a different profile that should not be deduped (different address). - AutofillProfile* profile2 = - new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(profile2, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "Fox", "1234 Other Street", "", - "Springfield", "IL", "91601", "US", "12345678910"); - profile2->set_use_count(7); - - // Create a profile similar to profile1 which should be deduped. - AutofillProfile* profile3 = - new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(profile3, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "US", "12345678910"); - profile3->set_use_count(5); - - // Create another different profile that should not be deduped (different - // name). - AutofillProfile* profile4 = - new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(profile4, "Marjorie", "Jacqueline", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", "12345678910"); - profile4->set_use_count(3); - - // Create another profile similar to profile1. Since that one has the lowest - // ranking score, the result of the merge should be in this profile at the end - // of the test. - AutofillProfile* profile5 = - new AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(profile5, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "US", "12345678910"); - profile5->set_use_count(1); - - // Add the profiles. - std::vector<std::unique_ptr<AutofillProfile>> existing_profiles; - existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile1)); - existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile2)); - existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile3)); - existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile4)); - existing_profiles.push_back(std::unique_ptr<AutofillProfile>(profile5)); - - std::unordered_map<std::string, std::string> guids_merge_map; - std::unordered_set<std::string> profiles_to_delete; - - personal_data_->personal_data_manager_cleaner_for_testing() - ->DedupeProfilesForTesting(&existing_profiles, &profiles_to_delete, - &guids_merge_map); - - // The two profile merges should be recorded in the map. - EXPECT_EQ(2U, guids_merge_map.size()); - - // Profile 1 was merged into profile 3. - ASSERT_TRUE(guids_merge_map.count(profile1->guid())); - EXPECT_TRUE(guids_merge_map.at(profile1->guid()) == profile3->guid()); - - // Profile 3 was merged into profile 5. - ASSERT_TRUE(guids_merge_map.count(profile3->guid())); - EXPECT_TRUE(guids_merge_map.at(profile3->guid()) == profile5->guid()); -} - -// Tests that UpdateCardsBillingAddressReference sets the correct billing -// address id as specified in the map. -TEST_F(PersonalDataManagerTest, UpdateCardsBillingAddressReference) { - /* The merges will be as follow: - - A -> B F (not merged) - \ - -> E - / - C -> D - */ - - std::unordered_map<std::string, std::string> guids_merge_map; - guids_merge_map.insert(std::pair<std::string, std::string>("A", "B")); - guids_merge_map.insert(std::pair<std::string, std::string>("C", "D")); - guids_merge_map.insert(std::pair<std::string, std::string>("B", "E")); - guids_merge_map.insert(std::pair<std::string, std::string>("D", "E")); - - // Create a credit card without a billing address id - CreditCard* credit_card0 = - new CreditCard(base::GenerateGUID(), test::kEmptyOrigin); - - // Create cards that use A, D, E and F as their billing address id. - CreditCard* credit_card1 = - new CreditCard(base::GenerateGUID(), test::kEmptyOrigin); - credit_card1->set_billing_address_id("A"); - CreditCard* credit_card2 = - new CreditCard(base::GenerateGUID(), test::kEmptyOrigin); - credit_card2->set_billing_address_id("D"); - CreditCard* credit_card3 = - new CreditCard(base::GenerateGUID(), test::kEmptyOrigin); - credit_card3->set_billing_address_id("E"); - CreditCard* credit_card4 = - new CreditCard(base::GenerateGUID(), test::kEmptyOrigin); - credit_card4->set_billing_address_id("F"); - - // Add the credit cards to the database. - personal_data_->local_credit_cards_.push_back( - std::unique_ptr<CreditCard>(credit_card0)); - personal_data_->local_credit_cards_.push_back( - std::unique_ptr<CreditCard>(credit_card1)); - personal_data_->server_credit_cards_.push_back( - std::unique_ptr<CreditCard>(credit_card2)); - personal_data_->local_credit_cards_.push_back( - std::unique_ptr<CreditCard>(credit_card3)); - personal_data_->server_credit_cards_.push_back( - std::unique_ptr<CreditCard>(credit_card4)); - - personal_data_->personal_data_manager_cleaner_for_testing() - ->UpdateCardsBillingAddressReferenceForTesting(guids_merge_map); - - // The first card's billing address should now be E. - EXPECT_EQ("E", credit_card1->billing_address_id()); - // The second card's billing address should now be E. - EXPECT_EQ("E", credit_card2->billing_address_id()); - // The third card's billing address should still be E. - EXPECT_EQ("E", credit_card3->billing_address_id()); - // The fourth card's billing address should still be F. - EXPECT_EQ("F", credit_card4->billing_address_id()); -} - -// Tests that ApplyDedupingRoutine updates the credit cards' billing address id -// based on the deduped profiles. -TEST_F(PersonalDataManagerTest, - ApplyDedupingRoutine_CardsBillingAddressIdUpdated) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); - - // A set of 6 profiles will be created. They should merge in this way: - // 1 -> 2 -> 3 - // 4 -> 5 - // 6 - // Set their frencency score so that profile 3 has a higher score than 5, and - // 5 has a higher score than 6. This will ensure a deterministic order when - // verifying results. - - // Create a set of 3 profiles to be merged together. - // Create a profile with a higher ranking score. - AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - profile1.set_use_count(12); - profile1.set_use_date(AutofillClock::Now() - base::Days(1)); - - // Create a profile with a medium ranking score. - AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile2, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "", "12345678910"); - profile2.set_use_count(5); - profile2.set_use_date(AutofillClock::Now() - base::Days(3)); - - // Create a profile with a lower ranking score. - AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile3, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); - profile3.set_use_count(3); - profile3.set_use_date(AutofillClock::Now() - base::Days(5)); - - // Create a set of two profiles to be merged together. - // Create a profile with a higher ranking score. - AutofillProfile profile4(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile4, "Marge", "B", "Simpson", - "marge.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - profile4.set_use_count(11); - profile4.set_use_date(AutofillClock::Now() - base::Days(1)); - - // Create a profile with a lower ranking score. - AutofillProfile profile5(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile5, "Marge", "B", "Simpson", - "marge.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); - profile5.set_use_count(5); - profile5.set_use_date(AutofillClock::Now() - base::Days(3)); - - // Create a unique profile. - AutofillProfile profile6(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile6, "Bart", "J", "Simpson", - "bart.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); - profile6.set_use_count(10); - profile6.set_use_date(AutofillClock::Now() - base::Days(1)); - - // Add three credit cards. Give them a ranking score so that they are - // suggested in order (1, 2, 3). This will ensure a deterministic order for - // verifying results. - CreditCard credit_card1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetCreditCardInfo(&credit_card1, "Clyde Barrow", - "378282246310005" /* American Express */, "04", - "2999", "1"); - credit_card1.set_use_count(10); - - CreditCard credit_card2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetCreditCardInfo(&credit_card2, "John Dillinger", - "4234567890123456" /* Visa */, "01", "2999", "1"); - credit_card2.set_use_count(5); - - CreditCard credit_card3(base::GenerateGUID(), test::kEmptyOrigin); - test::SetCreditCardInfo(&credit_card3, "Bonnie Parker", - "5105105105105100" /* Mastercard */, "12", "2999", - "1"); - credit_card3.set_use_count(1); - - // Associate the first card with profile1. - credit_card1.set_billing_address_id(profile1.guid()); - // Associate the second card with profile4. - credit_card2.set_billing_address_id(profile4.guid()); - // Associate the third card with profile6. - credit_card3.set_billing_address_id(profile6.guid()); - - AddProfileToPersonalDataManager(profile1); - AddProfileToPersonalDataManager(profile2); - AddProfileToPersonalDataManager(profile3); - AddProfileToPersonalDataManager(profile4); - AddProfileToPersonalDataManager(profile5); - AddProfileToPersonalDataManager(profile6); - personal_data_->AddCreditCard(credit_card1); - personal_data_->AddCreditCard(credit_card2); - personal_data_->AddCreditCard(credit_card3); - - WaitForOnPersonalDataChanged(); - - // Make sure the 6 profiles and 3 credit cards were saved. - EXPECT_EQ(6U, personal_data_->GetProfiles().size()); - EXPECT_EQ(3U, personal_data_->GetCreditCards().size()); - - EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() - ->ApplyDedupingRoutineForTesting()); - WaitForOnPersonalDataChanged(); - - // Get the profiles and cards sorted by their ranking score to have a - // deterministic order. - std::vector<AutofillProfile*> profiles = - personal_data_->GetProfilesToSuggest(); - std::vector<CreditCard*> credit_cards = - personal_data_->GetCreditCardsToSuggest(/*include_server_cards=*/true); - - // |profile1| should have been merged into |profile2| which should then have - // been merged into |profile3|. |profile4| should have been merged into - // |profile5| and |profile6| should not have merged. Therefore there should be - // 3 profile left. - ASSERT_EQ(3U, profiles.size()); - - // Make sure the remaining profiles are the expected ones. - EXPECT_EQ(profile3.guid(), profiles[0]->guid()); - EXPECT_EQ(profile5.guid(), profiles[1]->guid()); - EXPECT_EQ(profile6.guid(), profiles[2]->guid()); - - // |credit_card1|'s billing address should now be profile 3. - EXPECT_EQ(profile3.guid(), credit_cards[0]->billing_address_id()); - - // |credit_card2|'s billing address should now be profile 5. - EXPECT_EQ(profile5.guid(), credit_cards[1]->billing_address_id()); - - // |credit_card3|'s billing address should still be profile 6. - EXPECT_EQ(profile6.guid(), credit_cards[2]->billing_address_id()); -} - -// Tests that ApplyDedupingRoutine merges the profile values correctly, i.e. -// never lose information and keep the syntax of the profile with the higher -// ranking score. -TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); - - // Create a profile with a higher ranking score. - AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - profile1.set_use_count(10); - profile1.set_use_date(AutofillClock::Now() - base::Days(1)); - - // Create a profile with a medium ranking score. - AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile2, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "", "12345678910"); - profile2.set_use_count(5); - profile2.set_use_date(AutofillClock::Now() - base::Days(3)); - - // Create a profile with a lower ranking score. - AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile3, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); - profile3.set_use_count(3); - profile3.set_use_date(AutofillClock::Now() - base::Days(5)); - - AddProfileToPersonalDataManager(profile1); - AddProfileToPersonalDataManager(profile2); - AddProfileToPersonalDataManager(profile3); - - // Make sure the 3 profiles were saved; - EXPECT_EQ(3U, personal_data_->GetProfiles().size()); - - base::HistogramTester histogram_tester; - - EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() - ->ApplyDedupingRoutineForTesting()); - WaitForOnPersonalDataChanged(); - - std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); - - // |profile1| should have been merged into |profile2| which should then have - // been merged into |profile3|. Therefore there should only be 1 saved - // profile. - ASSERT_EQ(1U, profiles.size()); - // 3 profiles were considered for dedupe. - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesConsideredForDedupe", 3, 1); - // 2 profiles were removed (profiles 1 and 2). - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1); - - // Since profiles with higher ranking scores are merged into profiles with - // lower ranking scores, the result of the merge should be contained in - // profile3 since it had a lower ranking score compared to profile1. - EXPECT_EQ(profile3.guid(), profiles[0]->guid()); - // The address syntax that results from the merge should be the one from the - // imported profile (highest ranking). - EXPECT_EQ(u"742. Evergreen Terrace", - profiles[0]->GetRawInfo(ADDRESS_HOME_LINE1)); - // The middle name should be full, even if the profile with the higher - // ranking only had an initial (no loss of information). - EXPECT_EQ(u"Jay", profiles[0]->GetRawInfo(NAME_MIDDLE)); - // The specified phone number from profile1 should be kept (no loss of - // information). - EXPECT_EQ(u"12345678910", profiles[0]->GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); - // The specified company name from profile2 should be kept (no loss of - // information). - EXPECT_EQ(u"Fox", profiles[0]->GetRawInfo(COMPANY_NAME)); - // The specified country from the imported profile shoudl be kept (no loss of - // information). - EXPECT_EQ(u"US", profiles[0]->GetRawInfo(ADDRESS_HOME_COUNTRY)); - // The use count that results from the merge should be the max of all the - // profiles use counts. - EXPECT_EQ(10U, profiles[0]->use_count()); - // The use date that results from the merge should be the one from the - // profile1 since it was the most recently used profile. - EXPECT_LT(profile1.use_date() - base::Seconds(10), profiles[0]->use_date()); -} - -// Tests that ApplyDedupingRoutine only keeps the verified profile with its -// original data when deduping with similar profiles, even if it has a higher -// ranking score. -TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); - - // Create a verified profile with a higher ranking score. - AutofillProfile profile1(base::GenerateGUID(), kSettingsOrigin); - test::SetProfileInfo( - &profile1, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "", - "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", - "12345678910", /*finalize=*/true, - /*status=*/structured_address::VerificationStatus::kUserVerified); - profile1.set_use_count(7); - profile1.set_use_date(kMuchLaterTime); - - // Create a similar non verified profile with a medium ranking score. - AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile2, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - profile2.set_use_count(5); - profile2.set_use_date(kSomeLaterTime); - - // Create a similar non verified profile with a lower ranking score. - AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile3, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); - profile3.set_use_count(3); - profile3.set_use_date(kArbitraryTime); - - AddProfileToPersonalDataManager(profile1); - AddProfileToPersonalDataManager(profile2); - AddProfileToPersonalDataManager(profile3); - - // Make sure the 3 profiles were saved. - EXPECT_EQ(3U, personal_data_->GetProfiles().size()); - - base::HistogramTester histogram_tester; - - EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() - ->ApplyDedupingRoutineForTesting()); - WaitForOnPersonalDataChanged(); - - std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); - - // |profile2| should have merged with |profile3|. |profile3| - // should then have been discarded because it is similar to the verified - // |profile1|. - ASSERT_EQ(1U, profiles.size()); - // 3 profiles were considered for dedupe. - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesConsideredForDedupe", 3, 1); - // 2 profile were removed (profiles 2 and 3). - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1); - - // Although the profile was verified, the structure of the street address - // still evolved with future observations. In this case, the "." was added - // from a later observation. - profile1.SetRawInfoWithVerificationStatus( - ADDRESS_HOME_STREET_NAME, u"Evergreen Terrace", - structured_address::VerificationStatus::kParsed); - // - // Only the verified |profile1| with its original data should have been kept. - EXPECT_EQ(profile1.guid(), profiles[0]->guid()); - EXPECT_TRUE(profile1 == *profiles[0]); - EXPECT_EQ(profile1.use_count(), profiles[0]->use_count()); - EXPECT_EQ(profile1.use_date(), profiles[0]->use_date()); -} - -// Tests that ApplyDedupingRoutine only keeps the verified profile with its -// original data when deduping with similar profiles, even if it has a lower -// ranking score. -TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); - - // Create a profile to dedupe with a higher ranking score. - AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - profile1.set_use_count(5); - profile1.set_use_date(kMuchLaterTime); - - // Create a similar non verified profile with a medium ranking score. - AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile2, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); - profile2.set_use_count(5); - profile2.set_use_date(kSomeLaterTime); - - // Create a similar verified profile with a lower ranking score. - AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin); - test::SetProfileInfo( - &profile3, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "", - "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", - "12345678910", /*finalize=*/true, - /*status=*/structured_address::VerificationStatus::kUserVerified); - profile3.set_use_count(3); - profile3.set_use_date(kArbitraryTime); - - AddProfileToPersonalDataManager(profile1); - AddProfileToPersonalDataManager(profile2); - AddProfileToPersonalDataManager(profile3); - - // Make sure the 3 profiles were saved. - EXPECT_EQ(3U, personal_data_->GetProfiles().size()); - - base::HistogramTester histogram_tester; - - EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() - ->ApplyDedupingRoutineForTesting()); - WaitForOnPersonalDataChanged(); - - std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); - - // |profile1| should have merged with |profile2|. |profile2| - // should then have been discarded because it is similar to the verified - // |profile3|. - ASSERT_EQ(1U, profiles.size()); - // 3 profiles were considered for dedupe. - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesConsideredForDedupe", 3, 1); - // 2 profile were removed (profiles 1 and 2). - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1); - - // Only the verified |profile3| with it's original data should have been kept. - EXPECT_EQ(profile3.guid(), profiles[0]->guid()); - EXPECT_TRUE(profile3 == *profiles[0]); - EXPECT_EQ(profile3.use_count(), profiles[0]->use_count()); - EXPECT_EQ(profile3.use_date(), profiles[0]->use_date()); -} - -// Tests that ApplyDedupingRoutine does not merge unverified data into -// a verified profile. Also tests that two verified profiles don't get merged. -TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); - - // Create a profile to dedupe with a higher ranking score. - AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - profile1.set_use_count(5); - profile1.set_use_date(kMuchLaterTime); - - // Create a similar verified profile with a medium ranking score. - AutofillProfile profile2(base::GenerateGUID(), kSettingsOrigin); - test::SetProfileInfo( - &profile2, "Homer", "J", "Simpson", "homer.simpson@abc.com", "Fox", - "742 Evergreen Terrace.", "", "Springfield", "IL", "91601", "", "", - /*finalize=*/true, - /*status=*/structured_address::VerificationStatus::kUserVerified); - - profile2.set_use_count(5); - profile2.set_use_date(kSomeLaterTime); - - // Create a similar verified profile with a lower ranking score. - AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin); - test::SetProfileInfo( - &profile3, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "", - "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", - "12345678910", /*finalize=*/true, - /*status*/ structured_address::VerificationStatus::kUserVerified); - profile3.set_use_count(3); - profile3.set_use_date(kArbitraryTime); - - AddProfileToPersonalDataManager(profile1); - AddProfileToPersonalDataManager(profile2); - AddProfileToPersonalDataManager(profile3); - - // Make sure the 3 profiles were saved. - EXPECT_EQ(3U, personal_data_->GetProfiles().size()); - - base::HistogramTester histogram_tester; - - EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() - ->ApplyDedupingRoutineForTesting()); - WaitForOnPersonalDataChanged(); - - // Get the profiles, sorted by ranking to have a deterministic order. - std::vector<AutofillProfile*> profiles = - personal_data_->GetProfilesToSuggest(); - - // Although the profile was verified, the structure of the street address - // still evolved with future observations. In this case, the "." was removed - // from a later observation. - profile2.SetRawInfoWithVerificationStatus( - ADDRESS_HOME_STREET_NAME, u"Evergreen Terrace", - structured_address::VerificationStatus::kParsed); - - // |profile1| should have been discarded because the saved profile with the - // highest ranking score is verified (|profile2|). Therefore, |profile1|'s - // data should not have been merged with |profile2|'s data. Then |profile2| - // should have been compared to |profile3| but they should not have merged - // because both profiles are verified. - ASSERT_EQ(2U, profiles.size()); - // 3 profiles were considered for dedupe. - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesConsideredForDedupe", 3, 1); - // 1 profile was removed (|profile1|). - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesRemovedDuringDedupe", 1, 1); - - EXPECT_EQ(profile2.guid(), profiles[0]->guid()); - EXPECT_EQ(profile3.guid(), profiles[1]->guid()); - // The profiles should have kept their original data. - EXPECT_TRUE(profile2 == *profiles[0]); - EXPECT_TRUE(profile3 == *profiles[1]); - EXPECT_EQ(profile2.use_count(), profiles[0]->use_count()); - EXPECT_EQ(profile3.use_count(), profiles[1]->use_count()); - EXPECT_EQ(profile2.use_date(), profiles[0]->use_date()); - EXPECT_EQ(profile3.use_date(), profiles[1]->use_date()); -} - -// Tests that ApplyDedupingRoutine works as expected in a realistic scenario. -// Tests that it merges the diffent set of similar profiles independently and -// that the resulting profiles have the right values, has no effect on the other -// profiles and that the data of verified profiles is not modified. -TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); - - // Create a Homer home profile with a higher ranking score than other Homer - // profiles. - AutofillProfile Homer1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&Homer1, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - Homer1.set_use_count(10); - Homer1.set_use_date(AutofillClock::Now() - base::Days(1)); - - // Create a Homer home profile with a medium ranking score compared to other - // Homer profiles. - AutofillProfile Homer2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&Homer2, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "", "12345678910"); - Homer2.set_use_count(5); - Homer2.set_use_date(AutofillClock::Now() - base::Days(3)); - - // Create a Homer home profile with a lower ranking score than other Homer - // profiles. - AutofillProfile Homer3(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&Homer3, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); - Homer3.set_use_count(3); - Homer3.set_use_date(AutofillClock::Now() - base::Days(5)); - - // Create a Homer work profile (different address). - AutofillProfile Homer4(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&Homer4, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "Fox", "12 Nuclear Plant.", "", - "Springfield", "IL", "91601", "US", "9876543"); - Homer4.set_use_count(3); - Homer4.set_use_date(AutofillClock::Now() - base::Days(5)); - - // Create a Marge profile with a lower ranking score that other Marge - // profiles. - AutofillProfile Marge1(base::GenerateGUID(), kSettingsOrigin); - test::SetProfileInfo(&Marge1, "Marjorie", "J", "Simpson", - "marge.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "", "12345678910"); - Marge1.set_use_count(4); - Marge1.set_use_date(AutofillClock::Now() - base::Days(3)); - - // Create a verified Marge home profile with a lower ranking score that the - // other Marge profile. - AutofillProfile Marge2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&Marge2, "Marjorie", "Jacqueline", "Simpson", - "marge.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "", "12345678910"); - Marge2.set_use_count(2); - Marge2.set_use_date(AutofillClock::Now() - base::Days(3)); - - // Create a Barney profile (guest user). - AutofillProfile Barney(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&Barney, "Barney", "", "Gumble", "barney.gumble@abc.com", - "ABC", "123 Other Street", "", "Springfield", "IL", - "91601", "", ""); - Barney.set_use_count(1); - Barney.set_use_date(AutofillClock::Now() - base::Days(180)); - Barney.FinalizeAfterImport(); - - AddProfileToPersonalDataManager(Homer1); - AddProfileToPersonalDataManager(Homer2); - AddProfileToPersonalDataManager(Homer3); - AddProfileToPersonalDataManager(Homer4); - AddProfileToPersonalDataManager(Marge1); - AddProfileToPersonalDataManager(Marge2); - AddProfileToPersonalDataManager(Barney); - - // Make sure the 7 profiles were saved; - EXPECT_EQ(7U, personal_data_->GetProfiles().size()); - - base::HistogramTester histogram_tester; - - // |Homer1| should get merged into |Homer2| which should then be merged into - // |Homer3|. |Marge2| should be discarded in favor of |Marge1| which is - // verified. |Homer4| and |Barney| should not be deduped at all. - EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() - ->ApplyDedupingRoutineForTesting()); - WaitForOnPersonalDataChanged(); - - // Get the profiles, sorted by ranking score to have a deterministic order. - std::vector<AutofillProfile*> profiles = - personal_data_->GetProfilesToSuggest(); - - // The 2 duplicates Homer home profiles with the higher ranking score and the - // unverified Marge profile should have been deduped. - ASSERT_EQ(4U, profiles.size()); - // 7 profiles were considered for dedupe. - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesConsideredForDedupe", 7, 1); - // 3 profile were removed (|Homer1|, |Homer2| and |Marge2|). - histogram_tester.ExpectUniqueSample( - "Autofill.NumberOfProfilesRemovedDuringDedupe", 3, 1); - - // The remaining profiles should be |Homer3|, |Marge1|, |Homer4| and |Barney| - // in this order of ranking score. - EXPECT_EQ(Homer3.guid(), profiles[0]->guid()); - EXPECT_EQ(Marge1.guid(), profiles[1]->guid()); - EXPECT_EQ(Homer4.guid(), profiles[2]->guid()); - EXPECT_EQ(Barney.guid(), profiles[3]->guid()); - - // |Homer3|'s data: - // The address should be saved with the syntax of |Homer1| since it has the - // highest ranking score. - EXPECT_EQ(u"742. Evergreen Terrace", - profiles[0]->GetRawInfo(ADDRESS_HOME_LINE1)); - // The middle name should be the full version found in |Homer2|, - EXPECT_EQ(u"Jay", profiles[0]->GetRawInfo(NAME_MIDDLE)); - // The phone number from |Homer2| should be kept (no loss of information). - EXPECT_EQ(u"12345678910", profiles[0]->GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); - // The company name from |Homer3| should be kept (no loss of information). - EXPECT_EQ(u"Fox", profiles[0]->GetRawInfo(COMPANY_NAME)); - // The country from |Homer1| profile should be kept (no loss of information). - EXPECT_EQ(u"US", profiles[0]->GetRawInfo(ADDRESS_HOME_COUNTRY)); - // The use count that results from the merge should be the max of Homer 1, 2 - // and 3's respective use counts. - EXPECT_EQ(10U, profiles[0]->use_count()); - // The use date that results from the merge should be the one from the - // |Homer1| since it was the most recently used profile. - EXPECT_LT(Homer1.use_date() - base::Seconds(5), profiles[0]->use_date()); - EXPECT_GT(Homer1.use_date() + base::Seconds(5), profiles[0]->use_date()); - - // The other profiles should not have been modified. - EXPECT_TRUE(Marge1 == *profiles[1]); - EXPECT_TRUE(Homer4 == *profiles[2]); - EXPECT_TRUE(Barney == *profiles[3]); -} - -TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_NopIfZeroProfiles) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); - EXPECT_TRUE(personal_data_->GetProfiles().empty()); - EXPECT_FALSE(personal_data_->personal_data_manager_cleaner_for_testing() - ->ApplyDedupingRoutineForTesting()); -} - -TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_NopIfOneProfile) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); - - // Create a profile to dedupe. - AutofillProfile profile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - - AddProfileToPersonalDataManager(profile); - - EXPECT_EQ(1U, personal_data_->GetProfiles().size()); - EXPECT_FALSE(personal_data_->personal_data_manager_cleaner_for_testing() - ->ApplyDedupingRoutineForTesting()); -} - -// Tests that ApplyDedupingRoutine is not run a second time on the same major -// version. -TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_OncePerVersion) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeature(features::kAutofillEnableProfileDeduplication); - - // Create a profile to dedupe. - AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - - // Create a similar profile. - AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile2, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); - - AddProfileToPersonalDataManager(profile1); - AddProfileToPersonalDataManager(profile2); - - EXPECT_EQ(2U, personal_data_->GetProfiles().size()); - - // The deduping routine should be run a first time. - EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() - ->ApplyDedupingRoutineForTesting()); - WaitForOnPersonalDataChanged(); - - std::vector<AutofillProfile*> profiles = personal_data_->GetProfiles(); - - // The profiles should have been deduped - EXPECT_EQ(1U, profiles.size()); - - // Add another duplicate profile. - AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile3, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", - "", "Springfield", "IL", "91601", "", ""); - - AddProfileToPersonalDataManager(profile3); - - // Make sure |profile3| was saved. - EXPECT_EQ(2U, personal_data_->GetProfiles().size()); - - // The deduping routine should not be run. - EXPECT_FALSE(personal_data_->personal_data_manager_cleaner_for_testing() - ->ApplyDedupingRoutineForTesting()); - - // The two duplicate profiles should still be present. - EXPECT_EQ(2U, personal_data_->GetProfiles().size()); -} - -// Tests that settings-inaccessible profile values are removed from every stored -// profile on startup. -TEST_F(PersonalDataManagerTest, RemoveInaccessibleProfileValuesOnStartup) { - base::test::ScopedFeatureList feature; - feature.InitAndEnableFeatureWithParameters( - features::kAutofillRemoveInaccessibleProfileValues, - {{features::kAutofillRemoveInaccessibleProfileValuesOnStartup.name, - "true"}}); - - // Add a German and a US profile. - AutofillProfile profile0(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile0, "Marion", "Mitchell", "Morrison", - "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", - "Hollywood", "CA", "91601", "DE", "12345678910"); - AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Josephine", "Alicia", "Saenz", - "joewayne@me.xyz", "Fox", "903 Apple Ct.", nullptr, - "Orlando", "FL", "32801", "US", "19482937549"); - AddProfileToPersonalDataManager(profile0); - AddProfileToPersonalDataManager(profile1); - - personal_data_->personal_data_manager_cleaner_for_testing() - ->RemoveInaccessibleProfileValuesForTesting(); - WaitForOnPersonalDataChanged(); - - // profile0 should have it's state removed, while the US profile should remain - // unchanged. - profile0.SetRawInfo(ADDRESS_HOME_STATE, u""); - ExpectSameElements({&profile0, &profile1}, personal_data_->GetProfiles()); -} - -// Tests that DeleteDisusedAddresses only deletes the addresses that are -// supposed to be deleted. -TEST_F(PersonalDataManagerTest, - DeleteDisusedAddresses_DeleteDesiredAddressesOnly) { - auto now = AutofillClock::Now(); - - // Create unverified/disused/not-used-by-valid-credit-card - // address(deletable). - AutofillProfile profile0(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile0, "Alice", "", "Delete", "", "ACME", - "1234 Evergreen Terrace", "Bld. 6", "Springfield", "IL", - "32801", "US", "15151231234"); - profile0.set_use_date(now - base::Days(400)); - AddProfileToPersonalDataManager(profile0); - - // Create unverified/disused/used-by-expired-credit-card address(deletable). - AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Bob", "", "Delete", "", "ACME", - "1234 Evergreen Terrace", "Bld. 7", "Springfield", "IL", - "32801", "US", "15151231234"); - profile1.set_use_date(now - base::Days(400)); - CreditCard credit_card0(base::GenerateGUID(), test::kEmptyOrigin); - test::SetCreditCardInfo(&credit_card0, "Bob", - "5105105105105100" /* Mastercard */, "04", "1999", - "1"); - credit_card0.set_use_date(now - base::Days(400)); - credit_card0.set_billing_address_id(profile1.guid()); - AddProfileToPersonalDataManager(profile1); - personal_data_->AddCreditCard(credit_card0); - WaitForOnPersonalDataChanged(); - // Create verified/disused/not-used-by-valid-credit-card address(not - // deletable). - AutofillProfile profile2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile2, "Charlie", "", "Keep", "", "ACME", - "1234 Evergreen Terrace", "Bld. 8", "Springfield", "IL", - "32801", "US", "15151231234"); - profile2.set_origin(kSettingsOrigin); - profile2.set_use_date(now - base::Days(400)); - AddProfileToPersonalDataManager(profile2); - - // Create unverified/recently-used/not-used-by-valid-credit-card address(not - // deletable). - AutofillProfile profile3(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile3, "Dave", "", "Keep", "", "ACME", - "1234 Evergreen Terrace", "Bld. 9", "Springfield", "IL", - "32801", "US", "15151231234"); - profile3.set_use_date(now - base::Days(4)); - AddProfileToPersonalDataManager(profile3); - - // Create unverified/disused/used-by-valid-credit-card address(not deletable). - AutofillProfile profile4(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile4, "Emma", "", "Keep", "", "ACME", - "1234 Evergreen Terrace", "Bld. 10", "Springfield", "IL", - "32801", "US", "15151231234"); - profile4.set_use_date(now - base::Days(400)); - CreditCard credit_card1(CreditCard::MASKED_SERVER_CARD, "c987"); - test::SetCreditCardInfo(&credit_card1, "Emma", "6543", "01", "2999", "1"); - credit_card1.SetNetworkForMaskedCard(kVisaCard); - credit_card1.set_billing_address_id(profile4.guid()); - credit_card1.set_use_date(now - base::Days(1)); - AddProfileToPersonalDataManager(profile4); - personal_data_->AddCreditCard(credit_card1); - - WaitForOnPersonalDataChanged(); - - EXPECT_EQ(5U, personal_data_->GetProfiles().size()); - EXPECT_EQ(2U, personal_data_->GetCreditCards().size()); - - // DeleteDisusedAddresses should return true. - EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() - ->DeleteDisusedAddressesForTesting()); - WaitForOnPersonalDataChanged(); - - EXPECT_EQ(3U, personal_data_->GetProfiles().size()); - EXPECT_EQ(2U, personal_data_->GetCreditCards().size()); - EXPECT_EQ(u"Keep", personal_data_->GetProfiles()[0]->GetRawInfo(NAME_LAST)); - EXPECT_EQ(u"Keep", personal_data_->GetProfiles()[1]->GetRawInfo(NAME_LAST)); - EXPECT_EQ(u"Keep", personal_data_->GetProfiles()[2]->GetRawInfo(NAME_LAST)); -} - -// Tests that DeleteDisusedCreditCards deletes desired credit cards only. -TEST_F(PersonalDataManagerTest, - DeleteDisusedCreditCards_OnlyDeleteExpiredDisusedLocalCards) { - const char kHistogramName[] = "Autofill.CreditCardsDeletedForDisuse"; - auto now = AutofillClock::Now(); - - // Create a recently used local card, it is expected to remain. - CreditCard credit_card1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetCreditCardInfo(&credit_card1, "Alice", - "378282246310005" /* American Express */, "04", - "2999", "1"); - credit_card1.set_use_date(now - base::Days(4)); - - // Create a local card that was expired 400 days ago, but recently used. - // It is expected to remain. - CreditCard credit_card2(base::GenerateGUID(), test::kEmptyOrigin); - test::SetCreditCardInfo(&credit_card2, "Bob", - "378282246310006" /* American Express */, "04", - "1999", "1"); - credit_card2.set_use_date(now - base::Days(4)); - - // Create a local card expired recently, and last used 400 days ago. - // It is expected to remain. - CreditCard credit_card3(base::GenerateGUID(), test::kEmptyOrigin); - base::Time expiry_date = now - base::Days(32); - base::Time::Exploded exploded; - expiry_date.UTCExplode(&exploded); - test::SetCreditCardInfo(&credit_card3, "Clyde", "4111111111111111" /* Visa */, - base::StringPrintf("%02d", exploded.month).c_str(), - base::StringPrintf("%04d", exploded.year).c_str(), - "1"); - credit_card3.set_use_date(now - base::Days(400)); - - // Create a local card expired 400 days ago, and last used 400 days ago. - // It is expected to be deleted. - CreditCard credit_card4(base::GenerateGUID(), test::kEmptyOrigin); - test::SetCreditCardInfo(&credit_card4, "David", - "5105105105105100" /* Mastercard */, "04", "1999", - "1"); - credit_card4.set_use_date(now - base::Days(400)); - personal_data_->AddCreditCard(credit_card1); - personal_data_->AddCreditCard(credit_card2); - personal_data_->AddCreditCard(credit_card3); - personal_data_->AddCreditCard(credit_card4); - - // Create a unmasked server card expired 400 days ago, and last used 400 - // days ago. - // It is expected to remain because we do not delete server cards. - CreditCard credit_card5(CreditCard::FULL_SERVER_CARD, "c789"); - test::SetCreditCardInfo(&credit_card5, "Emma", "4234567890123456" /* Visa */, - "04", "1999", "1"); - credit_card5.set_use_date(now - base::Days(400)); - - // Create masked server card expired 400 days ago, and last used 400 days ago. - // It is expected to remain because we do not delete server cards. - CreditCard credit_card6(CreditCard::MASKED_SERVER_CARD, "c987"); - test::SetCreditCardInfo(&credit_card6, "Frank", "6543", "01", "1998", "1"); - credit_card6.set_use_date(now - base::Days(400)); - credit_card6.SetNetworkForMaskedCard(kVisaCard); - - // Save the server cards and set used_date to desired dates. - std::vector<CreditCard> server_cards; - server_cards.push_back(credit_card5); - server_cards.push_back(credit_card6); - SetServerCards(server_cards); - personal_data_->UpdateServerCardsMetadata({credit_card5, credit_card6}); - - WaitForOnPersonalDataChanged(); - EXPECT_EQ(6U, personal_data_->GetCreditCards().size()); - - // Setup histograms capturing. - base::HistogramTester histogram_tester; - - // DeleteDisusedCreditCards should return true to indicate it was run. - EXPECT_TRUE(personal_data_->personal_data_manager_cleaner_for_testing() - ->DeleteDisusedCreditCardsForTesting()); - - // Wait for the data to be refreshed. - WaitForOnPersonalDataChanged(); - - EXPECT_EQ(5U, personal_data_->GetCreditCards().size()); - std::unordered_set<std::u16string> expectedToRemain = { - u"Alice", u"Bob", u"Clyde", u"Emma", u"Frank"}; - for (auto* card : personal_data_->GetCreditCards()) { - EXPECT_NE(expectedToRemain.end(), - expectedToRemain.find(card->GetRawInfo(CREDIT_CARD_NAME_FULL))); - } - - // Verify histograms are logged. - histogram_tester.ExpectTotalCount(kHistogramName, 1); - histogram_tester.ExpectBucketCount(kHistogramName, 1, 1); -} - TEST_F(PersonalDataManagerTest, DeleteLocalCreditCards) { CreditCard credit_card1(base::GenerateGUID(), test::kEmptyOrigin); test::SetCreditCardInfo(&credit_card1, "Alice", @@ -5619,12 +4366,11 @@ TEST_F( // Tests that Wallet addresses do NOT get converted if they're stored in // ephemeral storage. -TEST_F(PersonalDataManagerTest, DoNotConvertWalletAddressesInEphemeralStorage) { +TEST_F(PersonalDataManagerSyncTransportModeTest, + DoNotConvertWalletAddressesInEphemeralStorage) { /////////////////////////////////////////////////////////////////////// // Setup. /////////////////////////////////////////////////////////////////////// - ResetPersonalDataManager(USER_MODE_NORMAL, - /*use_account_server_storage=*/true); ASSERT_FALSE(personal_data_->IsSyncFeatureEnabled()); // Add a local profile. @@ -6084,12 +4830,13 @@ TEST_F(PersonalDataManagerTest, ExcludeServerSideCards) { // Sync Transport mode is only for Win, Mac, and Linux. #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ BUILDFLAG(IS_CHROMEOS) -TEST_F(PersonalDataManagerTest, ServerCardsShowInTransportMode) { - // Set up PersonalDataManager in transport mode. - ResetPersonalDataManager(USER_MODE_NORMAL, - /*use_account_server_storage=*/true); +TEST_F(PersonalDataManagerSyncTransportModeTest, + ServerCardsShowInTransportMode) { SetUpThreeCardTypes(); - AccountInfo active_info = SetActiveSecondaryAccount(); + + CoreAccountInfo active_info = + identity_test_env_.identity_manager()->GetPrimaryAccountInfo( + signin::ConsentLevel::kSignin); // Opt-in to seeing server card in sync transport mode. ::autofill::prefs::SetUserOptedInWalletSyncTransport( @@ -6117,12 +4864,13 @@ TEST_F(PersonalDataManagerTest, ServerCardsShowInTransportMode) { // Make sure that the opt in is necessary to show server cards if the // appropriate feature is disabled. -TEST_F(PersonalDataManagerTest, ServerCardsShowInTransportMode_NeedOptIn) { - // Set up PersonalDataManager in transport mode. - ResetPersonalDataManager(USER_MODE_NORMAL, - /*use_account_server_storage=*/true); +TEST_F(PersonalDataManagerSyncTransportModeTest, + ServerCardsShowInTransportMode_NeedOptIn) { SetUpThreeCardTypes(); - AccountInfo active_info = SetActiveSecondaryAccount(); + + CoreAccountInfo active_info = + identity_test_env_.identity_manager()->GetPrimaryAccountInfo( + signin::ConsentLevel::kSignin); // The server cards should not be available at first. The user needs to // accept the opt-in offer. @@ -6148,120 +4896,6 @@ TEST_F(PersonalDataManagerTest, ServerCardsShowInTransportMode_NeedOptIn) { #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || // BUILDFLAG(IS_CHROMEOS) -// Tests that all the non settings origins of autofill profiles are cleared but -// that the settings origins are untouched. -TEST_F(PersonalDataManagerTest, ClearProfileNonSettingsOrigins) { - // Create three profile with a nonsettings, non-empty origin. - AutofillProfile profile0(base::GenerateGUID(), "https://www.example.com"); - test::SetProfileInfo(&profile0, "Marion0", "Mitchell", "Morrison", - "johnwayne@me.xyz", "Fox", - "123 Zoo St.\nSecond Line\nThird line", "unit 5", - "Hollywood", "CA", "91601", "US", "12345678910"); - profile0.set_use_count(10000); - AddProfileToPersonalDataManager(profile0); - - AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Marion1", "Mitchell", "Morrison", - "johnwayne@me.xyz", "Fox", - "123 Zoo St.\nSecond Line\nThird line", "unit 5", - "Hollywood", "CA", "91601", "US", "12345678910"); - profile1.set_use_count(1000); - AddProfileToPersonalDataManager(profile1); - - AutofillProfile profile2(base::GenerateGUID(), "1234"); - test::SetProfileInfo(&profile2, "Marion2", "Mitchell", "Morrison", - "johnwayne@me.xyz", "Fox", - "123 Zoo St.\nSecond Line\nThird line", "unit 5", - "Hollywood", "CA", "91601", "US", "12345678910"); - profile2.set_use_count(100); - AddProfileToPersonalDataManager(profile2); - - // Create a profile with a settings origin. - AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin); - test::SetProfileInfo(&profile3, "Marion3", "Mitchell", "Morrison", - "johnwayne@me.xyz", "Fox", - "123 Zoo St.\nSecond Line\nThird line", "unit 5", - "Hollywood", "CA", "91601", "US", "12345678910"); - profile3.set_use_count(10); - AddProfileToPersonalDataManager(profile3); - - ASSERT_EQ(4U, personal_data_->GetProfiles().size()); - - base::RunLoop run_loop; - EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) - .WillRepeatedly(QuitMessageLoop(&run_loop)); - EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) - .Times(2); // The setting of profiles 0 and 2 will be cleared. - - personal_data_->personal_data_manager_cleaner_for_testing() - ->ClearProfileNonSettingsOriginsForTesting(); - run_loop.Run(); - - ASSERT_EQ(4U, personal_data_->GetProfiles().size()); - - // The first three profiles' origin should be cleared and the fourth one still - // be the settings origin. - EXPECT_TRUE(personal_data_->GetProfilesToSuggest()[0]->origin().empty()); - EXPECT_TRUE(personal_data_->GetProfilesToSuggest()[1]->origin().empty()); - EXPECT_TRUE(personal_data_->GetProfilesToSuggest()[2]->origin().empty()); - EXPECT_EQ(kSettingsOrigin, - personal_data_->GetProfilesToSuggest()[3]->origin()); -} - -// Tests that all the non settings origins of autofill credit cards are cleared -// but that the settings origins are untouched. -TEST_F(PersonalDataManagerTest, ClearCreditCardNonSettingsOrigins) { - // Create three cards with a non settings origin. - CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com"); - test::SetCreditCardInfo(&credit_card0, "Bob0", - "5105105105105100" /* Mastercard */, "04", "1999", - "1"); - credit_card0.set_use_count(10000); - personal_data_->AddCreditCard(credit_card0); - - CreditCard credit_card1(base::GenerateGUID(), test::kEmptyOrigin); - test::SetCreditCardInfo(&credit_card1, "Bob1", - "5105105105105101" /* Mastercard */, "04", "1999", - "1"); - credit_card1.set_use_count(1000); - personal_data_->AddCreditCard(credit_card1); - - CreditCard credit_card2(base::GenerateGUID(), "1234"); - test::SetCreditCardInfo(&credit_card2, "Bob2", - "5105105105105102" /* Mastercard */, "04", "1999", - "1"); - credit_card2.set_use_count(100); - personal_data_->AddCreditCard(credit_card2); - - // Create a card with a settings origin. - CreditCard credit_card3(base::GenerateGUID(), kSettingsOrigin); - test::SetCreditCardInfo(&credit_card3, "Bob3", - "5105105105105103" /* Mastercard */, "04", "1999", - "1"); - credit_card3.set_use_count(10); - personal_data_->AddCreditCard(credit_card3); - - WaitForOnPersonalDataChanged(); - ASSERT_EQ(4U, personal_data_->GetCreditCards().size()); - - personal_data_->personal_data_manager_cleaner_for_testing() - ->ClearCreditCardNonSettingsOriginsForTesting(); - - WaitForOnPersonalDataChanged(); - ASSERT_EQ(4U, personal_data_->GetCreditCards().size()); - - // The first three profiles' origin should be cleared and the fourth one still - // be the settings origin. - EXPECT_TRUE( - personal_data_->GetCreditCardsToSuggest(false)[0]->origin().empty()); - EXPECT_TRUE( - personal_data_->GetCreditCardsToSuggest(false)[1]->origin().empty()); - EXPECT_TRUE( - personal_data_->GetCreditCardsToSuggest(false)[2]->origin().empty()); - EXPECT_EQ(kSettingsOrigin, - personal_data_->GetCreditCardsToSuggest(false)[3]->origin()); -} - // Tests that all the non settings origins of autofill profiles are cleared even // if sync is disabled. TEST_F( @@ -6330,8 +4964,9 @@ TEST_F( // Sanity check that the mode where we use the regular, persistent storage for // cards still works. TEST_F(PersonalDataManagerTest, UsePersistentServerStorage) { - ResetPersonalDataManager(USER_MODE_NORMAL, - /*use_account_server_storage=*/false); + ASSERT_TRUE(identity_test_env_.identity_manager()->HasPrimaryAccount( + signin::ConsentLevel::kSync)); + ASSERT_TRUE(sync_service_.HasSyncConsent()); SetUpThreeCardTypes(); // include_server_cards is set to false, therefore no server cards should be @@ -6346,10 +4981,8 @@ TEST_F(PersonalDataManagerTest, UsePersistentServerStorage) { } // Verify that PDM can switch at runtime between the different storages. -TEST_F(PersonalDataManagerTest, SwitchServerStorages) { +TEST_F(PersonalDataManagerSyncTransportModeTest, SwitchServerStorages) { // Start with account storage. - ResetPersonalDataManager(USER_MODE_NORMAL, - /*use_account_server_storage=*/true); SetUpThreeCardTypes(); // Check that we do have 2 server cards, as expected. @@ -6384,10 +5017,8 @@ TEST_F(PersonalDataManagerTest, SwitchServerStorages) { // Sanity check that the mode where we use the regular, persistent storage for // cards still works. -TEST_F(PersonalDataManagerTest, UseCorrectStorageForDifferentCards) { - ResetPersonalDataManager(USER_MODE_NORMAL, - /*use_account_server_storage=*/true); - +TEST_F(PersonalDataManagerSyncTransportModeTest, + UseCorrectStorageForDifferentCards) { // Add a server card. CreditCard server_card; test::SetCreditCardInfo(&server_card, "Server Card", @@ -6525,18 +5156,18 @@ TEST_F(PersonalDataManagerTest, TEST_F(PersonalDataManagerTest, GetAccountInfoForPaymentsServer) { // Make the IdentityManager return a non-empty AccountInfo when // GetPrimaryAccountInfo() is called. - identity_test_env_.SetPrimaryAccount(kPrimaryAccountEmail, - signin::ConsentLevel::kSync); - ResetPersonalDataManager(USER_MODE_NORMAL); + std::string sync_account_email = + identity_test_env_.identity_manager() + ->GetPrimaryAccountInfo(signin::ConsentLevel::kSync) + .email; + ASSERT_FALSE(sync_account_email.empty()); - // Make the sync service return a non-empty AccountInfo when - // GetAccountInfo() is called. - AccountInfo active_info; - active_info.email = kSyncTransportAccountEmail; - sync_service_.SetAccountInfo(active_info); + // Make the sync service returns consistent AccountInfo when GetAccountInfo() + // is called. + ASSERT_EQ(sync_service_.GetAccountInfo().email, sync_account_email); // The Active Sync AccountInfo should be returned. - EXPECT_EQ(kSyncTransportAccountEmail, + EXPECT_EQ(sync_account_email, personal_data_->GetAccountInfoForPaymentsServer().email); // The Active Sync AccountInfo should still be returned even if @@ -6546,7 +5177,7 @@ TEST_F(PersonalDataManagerTest, GetAccountInfoForPaymentsServer) { scoped_features.InitAndDisableFeature( features::kAutofillEnableAccountWalletStorage); - EXPECT_EQ(kSyncTransportAccountEmail, + EXPECT_EQ(sync_account_email, personal_data_->GetAccountInfoForPaymentsServer().email); } } @@ -6556,14 +5187,13 @@ TEST_F(PersonalDataManagerTest, OnAccountsCookieDeletedByUserAction) { ::autofill::prefs::SetUserOptedInWalletSyncTransport( prefs_.get(), CoreAccountId("account1"), true); EXPECT_FALSE( - prefs_->GetDictionary(prefs::kAutofillSyncTransportOptIn)->DictEmpty()); + prefs_->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); // Simulate that the cookies get cleared by the user. personal_data_->OnAccountsCookieDeletedByUserAction(); // Make sure the pref is now empty. - EXPECT_TRUE( - prefs_->GetDictionary(prefs::kAutofillSyncTransportOptIn)->DictEmpty()); + EXPECT_TRUE(prefs_->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); } TEST_F(PersonalDataManagerTest, SaveProfileUpdateStrikes) { @@ -6710,7 +5340,8 @@ TEST_F(PersonalDataManagerMigrationTest, #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_CHROMEOS_ASH) -TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { +TEST_F(PersonalDataManagerSyncTransportModeTest, + ShouldShowCardsFromAccountOption) { // The method should return false if one of these is not respected: // * The sync_service is not null // * The sync feature is not enabled @@ -6720,12 +5351,6 @@ TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { // independently, one by one. // Set everything up so that the proposition should be shown. - // Set an active secondary account. - AccountInfo active_info; - active_info.email = kPrimaryAccountEmail; - active_info.account_id = CoreAccountId("account_id"); - sync_service_.SetAccountInfo(active_info); - sync_service_.SetHasSyncConsent(false); // Set a server credit card. std::vector<CreditCard> server_cards; @@ -6738,23 +5363,24 @@ TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { WaitForOnPersonalDataChanged(); // Set the feature to enabled. - base::test::ScopedFeatureList scoped_features; - scoped_features.InitWithFeatures( - /*enabled_features=*/{features::kAutofillEnableAccountWalletStorage}, - /*disabled_features=*/{}); + base::test::ScopedFeatureList scoped_features( + features::kAutofillEnableAccountWalletStorage); // Make sure the function returns true. EXPECT_TRUE(personal_data_->ShouldShowCardsFromAccountOption()); // Set that the user already opted-in. Check that the function now returns // false. - ::autofill::prefs::SetUserOptedInWalletSyncTransport( - prefs_.get(), active_info.account_id, true); + CoreAccountId account_id = + identity_test_env_.identity_manager()->GetPrimaryAccountId( + signin::ConsentLevel::kSignin); + ::autofill::prefs::SetUserOptedInWalletSyncTransport(prefs_.get(), account_id, + true); EXPECT_FALSE(personal_data_->ShouldShowCardsFromAccountOption()); // Re-opt the user out. Check that the function now returns true. - ::autofill::prefs::SetUserOptedInWalletSyncTransport( - prefs_.get(), active_info.account_id, false); + ::autofill::prefs::SetUserOptedInWalletSyncTransport(prefs_.get(), account_id, + false); EXPECT_TRUE(personal_data_->ShouldShowCardsFromAccountOption()); // Set that the user has no server cards. Check that the function now returns @@ -6785,7 +5411,8 @@ TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { } #else // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && // !BUILDFLAG(IS_CHROMEOS_ASH) -TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { +TEST_F(PersonalDataManagerSyncTransportModeTest, + ShouldShowCardsFromAccountOption) { // The method should return false if one of these is not respected: // * The sync_service is not null // * The sync feature is not enabled @@ -6795,12 +5422,6 @@ TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { // independently, one by one. // Set everything up so that the proposition should be shown on Desktop. - // Set an an active secondary account. - AccountInfo active_info; - active_info.email = kPrimaryAccountEmail; - active_info.account_id = CoreAccountId("account_id"); - sync_service_.SetAccountInfo(active_info); - sync_service_.SetHasSyncConsent(false); // Set a server credit card. std::vector<CreditCard> server_cards; @@ -6823,13 +5444,16 @@ TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { // Set that the user already opted-in. Check that the function still returns // false. - ::autofill::prefs::SetUserOptedInWalletSyncTransport( - prefs_.get(), active_info.account_id, true); + CoreAccountId account_id = + identity_test_env_.identity_manager()->GetPrimaryAccountId( + signin::ConsentLevel::kSignin); + ::autofill::prefs::SetUserOptedInWalletSyncTransport(prefs_.get(), account_id, + true); EXPECT_FALSE(personal_data_->ShouldShowCardsFromAccountOption()); // Re-opt the user out. Check that the function now returns true. - ::autofill::prefs::SetUserOptedInWalletSyncTransport( - prefs_.get(), active_info.account_id, false); + ::autofill::prefs::SetUserOptedInWalletSyncTransport(prefs_.get(), account_id, + false); EXPECT_FALSE(personal_data_->ShouldShowCardsFromAccountOption()); // Set that the user has no server cards. Check that the function still @@ -6861,12 +5485,11 @@ TEST_F(PersonalDataManagerTest, ShouldShowCardsFromAccountOption) { #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && // !BUILDFLAG(IS_CHROMEOS_ASH) -TEST_F(PersonalDataManagerTest, GetSyncSigninState) { - // Make a non-primary account available with both a refresh token and cookie - // for the first few tests. - identity_test_env_.SetPrimaryAccount("test@gmail.com", - signin::ConsentLevel::kSync); - sync_service_.SetHasSyncConsent(false); +TEST_F(PersonalDataManagerSyncTransportModeTest, GetSyncSigninState) { + // Make sure a non-sync-consented account is available for the first tests. + ASSERT_TRUE(identity_test_env_.identity_manager()->HasPrimaryAccount( + signin::ConsentLevel::kSignin)); + ASSERT_FALSE(sync_service_.HasSyncConsent()); sync_service_.SetActiveDataTypes( syncer::ModelTypeSet(syncer::AUTOFILL_WALLET_DATA)); @@ -6874,10 +5497,8 @@ TEST_F(PersonalDataManagerTest, GetSyncSigninState) { // account info is not empty, the kAutofillEnableAccountWalletStorage feature // is enabled and the Wallet data type is active for the sync service. { - base::test::ScopedFeatureList scoped_features; - scoped_features.InitWithFeatures( - /*enabled_features=*/{features::kAutofillEnableAccountWalletStorage}, - /*disabled_features=*/{}); + base::test::ScopedFeatureList scoped_features( + features::kAutofillEnableAccountWalletStorage); EXPECT_EQ(AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled, personal_data_->GetSyncSigninState()); @@ -6887,9 +5508,8 @@ TEST_F(PersonalDataManagerTest, GetSyncSigninState) { // kAutofillEnableAccountWalletStorage feature is disabled. { base::test::ScopedFeatureList scoped_features; - scoped_features.InitWithFeatures( - /*enabled_features=*/{}, - /*disabled_features=*/{features::kAutofillEnableAccountWalletStorage}); + scoped_features.InitAndDisableFeature( + features::kAutofillEnableAccountWalletStorage); EXPECT_EQ(AutofillSyncSigninState::kSignedIn, personal_data_->GetSyncSigninState()); @@ -6898,10 +5518,8 @@ TEST_F(PersonalDataManagerTest, GetSyncSigninState) { // Check that the sync state is |SignedIn| if the sync service does not have // wallet data active. { - base::test::ScopedFeatureList scoped_features; - scoped_features.InitWithFeatures( - /*enabled_features=*/{features::kAutofillEnableAccountWalletStorage}, - /*disabled_features=*/{}); + base::test::ScopedFeatureList scoped_features( + features::kAutofillEnableAccountWalletStorage); sync_service_.SetActiveDataTypes(syncer::ModelTypeSet()); EXPECT_EQ(AutofillSyncSigninState::kSignedIn, @@ -6938,8 +5556,7 @@ TEST_F(PersonalDataManagerTest, GetSyncSigninState) { // feature is enabled even if the kAutofillEnableAccountWalletStorage feature // is enabled. { - base::test::ScopedFeatureList scoped_features; - scoped_features.InitAndEnableFeature( + base::test::ScopedFeatureList scoped_features( features::kAutofillEnableAccountWalletStorage); EXPECT_EQ(AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled, personal_data_->GetSyncSigninState()); @@ -6949,14 +5566,19 @@ TEST_F(PersonalDataManagerTest, GetSyncSigninState) { // On mobile, no dedicated opt-in is required for WalletSyncTransport - the // user is always considered opted-in and thus this test doesn't make sense. #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) -TEST_F(PersonalDataManagerTest, OnUserAcceptedUpstreamOffer) { +TEST_F(PersonalDataManagerSyncTransportModeTest, OnUserAcceptedUpstreamOffer) { /////////////////////////////////////////////////////////// // kSignedInAndWalletSyncTransportEnabled /////////////////////////////////////////////////////////// - // Make a primary account with no sync consent available to be in Sync - // Transport for Wallet mode. - CoreAccountInfo active_info = identity_test_env_.MakePrimaryAccountAvailable( - kSyncTransportAccountEmail, signin::ConsentLevel::kSignin); + // Make sure a primary account with no sync consent is available so + // AUTOFILL_WALLET_DATA can run in sync-transport mode. + ASSERT_TRUE(identity_test_env_.identity_manager()->HasPrimaryAccount( + signin::ConsentLevel::kSignin)); + ASSERT_FALSE(identity_test_env_.identity_manager()->HasPrimaryAccount( + signin::ConsentLevel::kSync)); + CoreAccountInfo active_info = + identity_test_env_.identity_manager()->GetPrimaryAccountInfo( + signin::ConsentLevel::kSignin); sync_service_.SetAccountInfo(active_info); sync_service_.SetHasSyncConsent(false); sync_service_.SetActiveDataTypes( diff --git a/chromium/components/autofill/core/browser/proto/server.proto b/chromium/components/autofill/core/browser/proto/server.proto index 25264f3988c..caa3efa92c3 100644 --- a/chromium/components/autofill/core/browser/proto/server.proto +++ b/chromium/components/autofill/core/browser/proto/server.proto @@ -218,6 +218,9 @@ message AutofillRandomizedFieldMetadata { // Example: <input value="VVVVVVV"> // XXXXXXXX = hash("VVVVVVV"") optional AutofillRandomizedValue initial_value_hash = 9; + + // Value of the autocomplete attribute. Example: <input autocomplete="XXX"> + optional AutofillRandomizedValue autocomplete = 10; } // This message contains information about the field types in a single form. diff --git a/chromium/components/autofill/core/browser/randomized_encoder.cc b/chromium/components/autofill/core/browser/randomized_encoder.cc index e9d84a1fcc5..1013f91a280 100644 --- a/chromium/components/autofill/core/browser/randomized_encoder.cc +++ b/chromium/components/autofill/core/browser/randomized_encoder.cc @@ -173,6 +173,7 @@ const char RandomizedEncoder::FIELD_CSS_CLASS[] = "field-css-classes"; const char RandomizedEncoder::FIELD_PLACEHOLDER[] = "field-placeholder"; const char RandomizedEncoder::FIELD_INITIAL_VALUE_HASH[] = "field-initial-hash-value"; +const char RandomizedEncoder::FIELD_AUTOCOMPLETE[] = "field-autocomplete"; // Copy of components/unified_consent/pref_names.cc // We could not use the constant from components/unified_constants because of a diff --git a/chromium/components/autofill/core/browser/randomized_encoder.h b/chromium/components/autofill/core/browser/randomized_encoder.h index 46cba968f16..c2eda37c624 100644 --- a/chromium/components/autofill/core/browser/randomized_encoder.h +++ b/chromium/components/autofill/core/browser/randomized_encoder.h @@ -46,6 +46,7 @@ class RandomizedEncoder { static const char FIELD_CSS_CLASS[]; static const char FIELD_PLACEHOLDER[]; static const char FIELD_INITIAL_VALUE_HASH[]; + static const char FIELD_AUTOCOMPLETE[]; static const char kUrlKeyedAnonymizedDataCollectionEnabled[]; diff --git a/chromium/components/autofill/core/browser/rationalization_util.cc b/chromium/components/autofill/core/browser/rationalization_util.cc index 1d606e4c6ef..8118bbe47f1 100644 --- a/chromium/components/autofill/core/browser/rationalization_util.cc +++ b/chromium/components/autofill/core/browser/rationalization_util.cc @@ -30,45 +30,37 @@ void RationalizePhoneNumberFields( ServerFieldType current_field_type = field->Type().GetStorableType(); switch (current_field_type) { case PHONE_HOME_NUMBER: - case PHONE_BILLING_NUMBER: + found_number_field = field; + phone_number_found = true; + break; + case PHONE_HOME_NUMBER_PREFIX: if (!found_number_field) { found_number_field = field; - if (field->max_length < 5) { - phone_number_separate_fields = true; - } else { - phone_number_found = true; - } - break; + phone_number_separate_fields = true; + } + break; + case PHONE_HOME_NUMBER_SUFFIX: + if (phone_number_separate_fields) { + found_number_field_second = field; + phone_number_found = true; } - // If the form has phone number separated into exchange and subscriber - // number we mark both of them as number fields. - // TODO(wuandy): A less hacky solution to have dedicated enum for - // exchange and subscriber number. - DCHECK(phone_number_separate_fields); - DCHECK(!found_number_field_second); - found_number_field_second = field; - phone_number_found = true; break; case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX: case PHONE_HOME_CITY_CODE: - case PHONE_BILLING_CITY_CODE: if (!found_city_code_field) found_city_code_field = field; break; case PHONE_HOME_COUNTRY_CODE: - case PHONE_BILLING_COUNTRY_CODE: if (!found_country_code_field) found_country_code_field = field; break; case PHONE_HOME_CITY_AND_NUMBER: case PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX: - case PHONE_BILLING_CITY_AND_NUMBER: DCHECK(!phone_number_found && !found_city_and_number_field); found_city_and_number_field = field; phone_number_found = true; break; case PHONE_HOME_WHOLE_NUMBER: - case PHONE_BILLING_WHOLE_NUMBER: DCHECK(!phone_number_found && !found_whole_number_field); found_whole_number_field = field; phone_number_found = true; @@ -125,29 +117,24 @@ void RationalizePhoneNumberFields( ServerFieldType current_field_type = field->Type().GetStorableType(); switch (current_field_type) { case PHONE_HOME_NUMBER: - case PHONE_BILLING_NUMBER: if (field != found_number_field && field != found_number_field_second) field->set_only_fill_when_focused(true); break; case PHONE_HOME_CITY_CODE: case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX: - case PHONE_BILLING_CITY_CODE: if (field != found_city_code_field) field->set_only_fill_when_focused(true); break; case PHONE_HOME_COUNTRY_CODE: - case PHONE_BILLING_COUNTRY_CODE: if (field != found_country_code_field) field->set_only_fill_when_focused(true); break; case PHONE_HOME_CITY_AND_NUMBER: case PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX: - case PHONE_BILLING_CITY_AND_NUMBER: if (field != found_city_and_number_field) field->set_only_fill_when_focused(true); break; case PHONE_HOME_WHOLE_NUMBER: - case PHONE_BILLING_WHOLE_NUMBER: if (field != found_whole_number_field) field->set_only_fill_when_focused(true); break; diff --git a/chromium/components/autofill/core/browser/single_field_form_fill_router.cc b/chromium/components/autofill/core/browser/single_field_form_fill_router.cc index 9bbc8381b4c..1ad9c237317 100644 --- a/chromium/components/autofill/core/browser/single_field_form_fill_router.cc +++ b/chromium/components/autofill/core/browser/single_field_form_fill_router.cc @@ -52,26 +52,28 @@ void SingleFieldFormFillRouter::OnWillSubmitForm( autocomplete_fields, is_autocomplete_enabled); } -void SingleFieldFormFillRouter::OnGetSingleFieldSuggestions( +bool SingleFieldFormFillRouter::OnGetSingleFieldSuggestions( int query_id, bool is_autocomplete_enabled, bool autoselect_first_suggestion, - const std::u16string& name, - const std::u16string& prefix, - const std::string& form_control_type, + const FormFieldData& field, base::WeakPtr<SingleFieldFormFiller::SuggestionsHandler> handler, const SuggestionsContext& context) { // Retrieving suggestions for a new field; select the appropriate filler. - if (merchant_promo_code_manager_ && context.focused_field && - context.focused_field->Type().GetStorableType() == MERCHANT_PROMO_CODE) { - merchant_promo_code_manager_->OnGetSingleFieldSuggestions( - query_id, is_autocomplete_enabled, autoselect_first_suggestion, name, - prefix, form_control_type, handler, context); - } else { - autocomplete_history_manager_->OnGetSingleFieldSuggestions( - query_id, is_autocomplete_enabled, autoselect_first_suggestion, name, - prefix, form_control_type, handler, context); + if (merchant_promo_code_manager_ && + merchant_promo_code_manager_->OnGetSingleFieldSuggestions( + query_id, is_autocomplete_enabled, autoselect_first_suggestion, field, + handler, context)) { + return true; } + + if (autocomplete_history_manager_->OnGetSingleFieldSuggestions( + query_id, is_autocomplete_enabled, autoselect_first_suggestion, field, + handler, context)) { + return true; + } + + return false; } void SingleFieldFormFillRouter::OnWillSubmitFormWithFields( diff --git a/chromium/components/autofill/core/browser/single_field_form_fill_router.h b/chromium/components/autofill/core/browser/single_field_form_fill_router.h index 28336d2187b..8564d2bb28b 100644 --- a/chromium/components/autofill/core/browser/single_field_form_fill_router.h +++ b/chromium/components/autofill/core/browser/single_field_form_fill_router.h @@ -42,13 +42,11 @@ class SingleFieldFormFillRouter : public SingleFieldFormFiller { bool is_autocomplete_enabled); // SingleFieldFormFiller overrides: - void OnGetSingleFieldSuggestions( + [[nodiscard]] bool OnGetSingleFieldSuggestions( int query_id, bool is_autocomplete_enabled, bool autoselect_first_suggestion, - const std::u16string& name, - const std::u16string& prefix, - const std::string& form_control_type, + const FormFieldData& field, base::WeakPtr<SingleFieldFormFiller::SuggestionsHandler> handler, const SuggestionsContext& context) override; void OnWillSubmitFormWithFields(const std::vector<FormFieldData>& fields, diff --git a/chromium/components/autofill/core/browser/single_field_form_fill_router_unittest.cc b/chromium/components/autofill/core/browser/single_field_form_fill_router_unittest.cc index f091d5f6d6d..a2fad7dc872 100644 --- a/chromium/components/autofill/core/browser/single_field_form_fill_router_unittest.cc +++ b/chromium/components/autofill/core/browser/single_field_form_fill_router_unittest.cc @@ -6,10 +6,10 @@ #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/mock_autocomplete_history_manager.h" #include "components/autofill/core/browser/mock_merchant_promo_code_manager.h" #include "components/autofill/core/browser/suggestions_context.h" -#include "components/autofill/core/browser/test_form_structure.h" #include "components/autofill/core/browser/test_personal_data_manager.h" #include "components/autofill/core/browser/webdata/mock_autofill_webdata_service.h" #include "components/autofill/core/common/autofill_features.h" @@ -25,9 +25,6 @@ using testing::SaveArg; namespace autofill { -using FieldPrediction = - AutofillQueryResponse::FormSuggestion::FieldSuggestion::FieldPrediction; - namespace { class MockSuggestionsHandler : public SingleFieldFormFiller::SuggestionsHandler { @@ -74,6 +71,8 @@ class SingleFieldFormFillRouterTest : public testing::Test { std::make_unique<SingleFieldFormFillRouter>( autocomplete_history_manager_.get(), merchant_promo_code_manager_.get()); + test::CreateTestFormField(/*label=*/"", "Some Field Name", "SomePrefix", + "SomeType", &test_field_); } base::test::SingleThreadTaskEnvironment task_environment_; @@ -83,22 +82,36 @@ class SingleFieldFormFillRouterTest : public testing::Test { std::unique_ptr<MockAutocompleteHistoryManager> autocomplete_history_manager_; std::unique_ptr<MockMerchantPromoCodeManager> merchant_promo_code_manager_; std::unique_ptr<PrefService> prefs_; + FormFieldData test_field_; }; // Ensure that the router routes to AutocompleteHistoryManager for this -// OnGetSingleFieldSuggestions call. +// OnGetSingleFieldSuggestions call if the field has autocomplete on. TEST_F(SingleFieldFormFillRouterTest, RouteToAutocompleteHistoryManager_OnGetSingleFieldSuggestions) { - auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); - - EXPECT_CALL(*autocomplete_history_manager_, OnGetSingleFieldSuggestions); - - single_field_form_fill_router_->OnGetSingleFieldSuggestions( - /*query_id=*/2, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name", - /*prefix=*/u"SomePrefix", - /*form_control_type=*/"SomeType", suggestions_handler->GetWeakPtr(), - SuggestionsContext()); + for (bool test_field_should_autocomplete : {true, false}) { + SCOPED_TRACE(testing::Message() << "test_field_should_autocomplete = " + << test_field_should_autocomplete); + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + test_field_.should_autocomplete = test_field_should_autocomplete; + + // If |test_field_.should_autocomplete| is true, that means autocomplete is + // turned on for the given test field and + // AutocompleteHistoryManager::OnGetSingleFieldSuggestions() should return + // true. If |test_field_.should_autocomplete| is false, then autocomplete is + // turned off for the given test field and + // AutocompleteHistoryManager::OnGetSingleFieldSuggestions() should return + // false. + EXPECT_CALL(*autocomplete_history_manager_, OnGetSingleFieldSuggestions) + .Times(1) + .WillOnce(testing::Return(test_field_.should_autocomplete)); + + EXPECT_EQ(test_field_.should_autocomplete, + single_field_form_fill_router_->OnGetSingleFieldSuggestions( + /*query_id=*/2, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); + } } // Ensure that the router routes to all SingleFieldFormFillers for this @@ -106,20 +119,11 @@ TEST_F(SingleFieldFormFillRouterTest, TEST_F(SingleFieldFormFillRouterTest, RouteToAllSingleFieldFormFillers_OnWillSubmitForm) { FormData form_data; - std::vector<FormFieldData> fields; size_t number_of_fields_for_testing = 3; - for (size_t i = 0; i < number_of_fields_for_testing; i++) { - fields.emplace_back(); - } - -#if !BUILDFLAG(IS_IOS) - for (size_t i = 0; i < number_of_fields_for_testing; i++) { - fields.emplace_back(); - } -#endif // !BUILDFLAG(IS_IOS) + std::vector<FormFieldData> fields(2 * number_of_fields_for_testing); form_data.fields = fields; - TestFormStructure form_structure{form_data}; + FormStructure form_structure{form_data}; // Set the first |number_of_fields_for_testing| fields to be autocomplete // fields. @@ -127,14 +131,12 @@ TEST_F(SingleFieldFormFillRouterTest, form_structure.set_server_field_type_for_testing(i, UNKNOWN_TYPE); } -#if !BUILDFLAG(IS_IOS) // Set the next |number_of_fields_for_testing| fields to be merchant promo // code fields. for (size_t i = number_of_fields_for_testing; i < number_of_fields_for_testing * 2; i++) { form_structure.set_server_field_type_for_testing(i, MERCHANT_PROMO_CODE); } -#endif // !BUILDFLAG(IS_IOS) std::vector<FormFieldData> submitted_autocomplete_fields; bool autocomplete_fields_is_autocomplete_enabled = false; @@ -143,14 +145,12 @@ TEST_F(SingleFieldFormFillRouterTest, (DoAll(SaveArg<0>(&submitted_autocomplete_fields), SaveArg<1>(&autocomplete_fields_is_autocomplete_enabled)))); -#if !BUILDFLAG(IS_IOS) std::vector<FormFieldData> submitted_merchant_promo_code_fields; bool merchant_promo_code_fields_is_autocomplete_enabled = false; EXPECT_CALL(*merchant_promo_code_manager_, OnWillSubmitFormWithFields(_, _)) .WillOnce((DoAll( SaveArg<0>(&submitted_merchant_promo_code_fields), SaveArg<1>(&merchant_promo_code_fields_is_autocomplete_enabled)))); -#endif // !BUILDFLAG(IS_IOS) single_field_form_fill_router_->OnWillSubmitForm( form_data, &form_structure, /*is_autocomplete_enabled=*/true); @@ -159,11 +159,9 @@ TEST_F(SingleFieldFormFillRouterTest, number_of_fields_for_testing); EXPECT_TRUE(autocomplete_fields_is_autocomplete_enabled); -#if !BUILDFLAG(IS_IOS) EXPECT_TRUE(submitted_merchant_promo_code_fields.size() == number_of_fields_for_testing); EXPECT_TRUE(merchant_promo_code_fields_is_autocomplete_enabled); -#endif // !BUILDFLAG(IS_IOS) } // Ensure that the router routes to SingleFieldFormFillers for this @@ -174,9 +172,7 @@ TEST_F(SingleFieldFormFillRouterTest, EXPECT_CALL(*autocomplete_history_manager_, CancelPendingQueries); -#if !BUILDFLAG(IS_IOS) EXPECT_CALL(*merchant_promo_code_manager_, CancelPendingQueries); -#endif // !BUILDFLAG(IS_IOS) single_field_form_fill_router_->CancelPendingQueries( suggestions_handler.get()); @@ -204,34 +200,90 @@ TEST_F(SingleFieldFormFillRouterTest, /*value=*/u"Value", POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY); } -#if !BUILDFLAG(IS_IOS) // Ensure that the router routes to MerchantPromoCodeManager for this // OnGetSingleFieldSuggestions call. TEST_F(SingleFieldFormFillRouterTest, RouteToMerchantPromoCodeManager_OnGetSingleFieldSuggestions) { + for (bool test_field_should_autocomplete : {true, false}) { + SCOPED_TRACE(testing::Message() << "test_field_should_autocomplete = " + << test_field_should_autocomplete); + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + {features::kAutofillFillMerchantPromoCodeFields}, {}); + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + test_field_.should_autocomplete = test_field_should_autocomplete; + + // |test_field_.should_autocomplete| should not affect merchant promo code + // autofill, so MerchantPromoCodeManager::OnGetSingleFieldSuggestions() + // should always be called since the given test field is a merchant promo + // code field. + EXPECT_CALL(*merchant_promo_code_manager_, OnGetSingleFieldSuggestions) + .Times(1) + .WillOnce(testing::Return(true)); + + EXPECT_TRUE(single_field_form_fill_router_->OnGetSingleFieldSuggestions( + /*query_id=*/2, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); + } +} + +// Ensure that the router routes to AutocompleteHistoryManager for this +// OnGetSingleFieldSuggestions call if MerchantPromoCodeManager is not present. +TEST_F(SingleFieldFormFillRouterTest, MerchantPromoCodeManagerNotPresent) { base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures({features::kAutofillFillMerchantPromoCodeFields, - features::kAutofillServerTypeTakesPrecedence}, - {}); + feature_list.InitWithFeatures( + {features::kAutofillFillMerchantPromoCodeFields}, {}); auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); - EXPECT_CALL(*merchant_promo_code_manager_, OnGetSingleFieldSuggestions); - std::vector<FieldPrediction> merchant_promo_code_field_predictions; - FieldPrediction merchant_promo_code_field_prediction; - merchant_promo_code_field_prediction.set_type(MERCHANT_PROMO_CODE); - merchant_promo_code_field_predictions.push_back( - merchant_promo_code_field_prediction); - SuggestionsContext context; - AutofillField autofill_field; - autofill_field.set_server_predictions( - std::move(merchant_promo_code_field_predictions)); - context.focused_field = &autofill_field; - single_field_form_fill_router_->OnGetSingleFieldSuggestions( + // This also invalidates the WeakPtr that the |single_field_form_fill_router_| + // holds on the promo code manager. + merchant_promo_code_manager_.reset(); + + // As the merchant promo code manager is gone, we should call + // AutocompleteHistoryManager::OnGetSingleFieldSuggestions(). + EXPECT_CALL(*autocomplete_history_manager_, OnGetSingleFieldSuggestions) + .Times(1) + .WillOnce(testing::Return(true)); + + // As |test_field_.should_autocomplete| is true, this was a valid field for + // autocomplete. SingleFieldFormFillRouter::OnGetSingleFieldSuggestions() + // should return true. + EXPECT_TRUE(single_field_form_fill_router_->OnGetSingleFieldSuggestions( + /*query_id=*/2, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); +} + +// Ensure that the router routes to AutocompleteHistoryManager for this +// OnGetSingleFieldSuggestions call if +// MerchantPromoCodeManager::OnGetSingleFieldSuggestions() returns false. +TEST_F(SingleFieldFormFillRouterTest, MerchantPromoCodeManagerReturnedFalse) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + {features::kAutofillFillMerchantPromoCodeFields}, {}); + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + + // Mock MerchantPromoCodeManager::OnGetSingleFieldSuggestions() returning + // false. + EXPECT_CALL(*merchant_promo_code_manager_, OnGetSingleFieldSuggestions) + .Times(1) + .WillOnce(testing::Return(false)); + + // Since MerchantPromoCodeManager::OnGetSingleFieldSuggestions() returned + // false, we should call + // AutocompleteHistoryManager::OnGetSingleFieldSuggestions(). + EXPECT_CALL(*autocomplete_history_manager_, OnGetSingleFieldSuggestions) + .Times(1) + .WillOnce(testing::Return(true)); + + // As |test_field_.should_autocomplete| is true, this was a valid field for + // autocomplete. SingleFieldFormFillRouter::OnGetSingleFieldSuggestions() + // should return true. + EXPECT_TRUE(single_field_form_fill_router_->OnGetSingleFieldSuggestions( /*query_id=*/2, /*is_autocomplete_enabled=*/true, - /*autoselect_first_suggestion=*/false, /*name=*/u"Some Field Name", - /*prefix=*/u"SomePrefix", - /*form_control_type=*/"SomeType", suggestions_handler->GetWeakPtr(), - context); + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); } // Ensure that the router routes to MerchantPromoCodeManager for this @@ -255,6 +307,31 @@ TEST_F(SingleFieldFormFillRouterTest, single_field_form_fill_router_->OnSingleFieldSuggestionSelected( /*value=*/u"Value", POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY); } -#endif // !BUILDFLAG(IS_IOS) + +// Ensure that SingleFieldFormFillRouter::OnGetSingleFieldSuggestions() returns +// false if all single field form fillers returned false. +TEST_F( + SingleFieldFormFillRouterTest, + FieldNotEligibleForAnySingleFieldFormFiller_OnGetSingleFieldSuggestions) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + {features::kAutofillFillMerchantPromoCodeFields}, {}); + auto suggestions_handler = std::make_unique<MockSuggestionsHandler>(); + + EXPECT_CALL(*merchant_promo_code_manager_, OnGetSingleFieldSuggestions) + .Times(1) + .WillOnce(testing::Return(false)); + + EXPECT_CALL(*autocomplete_history_manager_, OnGetSingleFieldSuggestions) + .Times(1) + .WillOnce(testing::Return(false)); + + // All SingleFieldFormFillers returned false, so we should return false as we + // did not attempt to display any single field form fill suggestions. + EXPECT_FALSE(single_field_form_fill_router_->OnGetSingleFieldSuggestions( + /*query_id=*/2, /*is_autocomplete_enabled=*/true, + /*autoselect_first_suggestion=*/false, test_field_, + suggestions_handler->GetWeakPtr(), SuggestionsContext())); +} } // namespace autofill diff --git a/chromium/components/autofill/core/browser/single_field_form_filler.h b/chromium/components/autofill/core/browser/single_field_form_filler.h index 41414abe32f..624d8fe6223 100644 --- a/chromium/components/autofill/core/browser/single_field_form_filler.h +++ b/chromium/components/autofill/core/browser/single_field_form_filler.h @@ -42,21 +42,23 @@ class SingleFieldFormFiller { // |query_id| is given by the client as context. // |is_autocomplete_enabled| is to determine if the feature is enable for the // requestor's context (e.g. Android WebViews have different contexts). - // |name| is the name of the field. - // |prefix| is the field's value. - // |form_control_type| is the field's control type. - // |handler| is weak pointer to the requestor, which we will call back once we - // receive the response. There can only be one pending query per |handler|, - // hence this function will cancel the previous pending query if it hadn't - // already been resolved, at which point no method of the handler will be - // called. - virtual void OnGetSingleFieldSuggestions( + // |field| is the given field. |handler| is weak pointer to the requestor, + // which we will call back once we receive the response. There can only be one + // pending query per |handler|, hence this function will cancel the previous + // pending query if it hadn't already been resolved, at which point no method + // of the handler will be called. + // The boolean return value denotes whether a SingleFieldFormFiller claims the + // opportunity to fill this field. By returning true, the + // SingleFieldFormFiller does not promise at this point to have a value + // available for filling. It just promises to call back the handler and voids + // the opportunity for other SingleFieldFormFillers to offer filling the + // field. The callback can happen synchronously even before + // OnGetSingleFieldSuggestions returns true. + [[nodiscard]] virtual bool OnGetSingleFieldSuggestions( int query_id, bool is_autocomplete_enabled, bool autoselect_first_suggestion, - const std::u16string& name, - const std::u16string& prefix, - const std::string& form_control_type, + const FormFieldData& field, base::WeakPtr<SuggestionsHandler> handler, const SuggestionsContext& context) = 0; diff --git a/chromium/components/autofill/core/browser/test_autofill_client.cc b/chromium/components/autofill/core/browser/test_autofill_client.cc index 55da3ffe7e0..8310d83be6b 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.cc +++ b/chromium/components/autofill/core/browser/test_autofill_client.cc @@ -44,6 +44,18 @@ MerchantPromoCodeManager* TestAutofillClient::GetMerchantPromoCodeManager() { return &mock_merchant_promo_code_manager_; } +CreditCardCVCAuthenticator* TestAutofillClient::GetCVCAuthenticator() { + if (!cvc_authenticator_) + cvc_authenticator_ = std::make_unique<CreditCardCVCAuthenticator>(this); + return cvc_authenticator_.get(); +} + +CreditCardOtpAuthenticator* TestAutofillClient::GetOtpAuthenticator() { + if (!otp_authenticator_) + otp_authenticator_ = std::make_unique<CreditCardOtpAuthenticator>(this); + return otp_authenticator_.get(); +} + PrefService* TestAutofillClient::GetPrefs() { return const_cast<PrefService*>(base::as_const(*this).GetPrefs()); } @@ -116,7 +128,7 @@ std::string TestAutofillClient::GetVariationConfigCountryCode() const { #if !BUILDFLAG(IS_IOS) std::unique_ptr<webauthn::InternalAuthenticator> TestAutofillClient::CreateCreditCardInternalAuthenticator( - content::RenderFrameHost* rfh) { + AutofillDriver* driver) { return std::make_unique<TestInternalAuthenticator>(); } #endif @@ -254,6 +266,17 @@ bool TestAutofillClient::HasCreditCardScanFeature() { void TestAutofillClient::ScanCreditCard(CreditCardScanCallback callback) {} +bool TestAutofillClient::IsTouchToFillCreditCardSupported() { + return false; +} + +bool TestAutofillClient::ShowTouchToFillCreditCard( + base::WeakPtr<TouchToFillDelegate> delegate) { + return false; +} + +void TestAutofillClient::HideTouchToFillCreditCard() {} + void TestAutofillClient::ShowAutofillPopup( const AutofillClient::PopupOpenArgs& open_args, base::WeakPtr<AutofillPopupDelegate> delegate) {} diff --git a/chromium/components/autofill/core/browser/test_autofill_client.h b/chromium/components/autofill/core/browser/test_autofill_client.h index a5b9d052531..b0743bbc9f9 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.h +++ b/chromium/components/autofill/core/browser/test_autofill_client.h @@ -17,6 +17,8 @@ #include "components/autofill/core/browser/mock_autocomplete_history_manager.h" #include "components/autofill/core/browser/mock_merchant_promo_code_manager.h" #include "components/autofill/core/browser/payments/autofill_offer_manager.h" +#include "components/autofill/core/browser/payments/credit_card_cvc_authenticator.h" +#include "components/autofill/core/browser/payments/credit_card_otp_authenticator.h" #include "components/autofill/core/browser/payments/legal_message_line.h" #include "components/autofill/core/browser/payments/test_payments_client.h" #include "components/autofill/core/browser/payments/test_strike_database.h" @@ -53,6 +55,8 @@ class TestAutofillClient : public AutofillClient { TestPersonalDataManager* GetPersonalDataManager() override; AutocompleteHistoryManager* GetAutocompleteHistoryManager() override; MerchantPromoCodeManager* GetMerchantPromoCodeManager() override; + CreditCardCVCAuthenticator* GetCVCAuthenticator() override; + CreditCardOtpAuthenticator* GetOtpAuthenticator() override; PrefService* GetPrefs() override; const PrefService* GetPrefs() const override; syncer::SyncService* GetSyncService() override; @@ -71,7 +75,7 @@ class TestAutofillClient : public AutofillClient { std::string GetVariationConfigCountryCode() const override; #if !BUILDFLAG(IS_IOS) std::unique_ptr<webauthn::InternalAuthenticator> - CreateCreditCardInternalAuthenticator(content::RenderFrameHost* rfh) override; + CreateCreditCardInternalAuthenticator(AutofillDriver* driver) override; #endif void ShowAutofillSettings(bool show_credit_card_settings) override; @@ -140,6 +144,10 @@ class TestAutofillClient : public AutofillClient { AddressProfileSavePromptCallback callback) override; bool HasCreditCardScanFeature() override; void ScanCreditCard(CreditCardScanCallback callback) override; + bool IsTouchToFillCreditCardSupported() override; + bool ShowTouchToFillCreditCard( + base::WeakPtr<TouchToFillDelegate> delegate) override; + void HideTouchToFillCreditCard() override; void ShowAutofillPopup( const AutofillClient::PopupOpenArgs& open_args, base::WeakPtr<AutofillPopupDelegate> delegate) override; @@ -190,6 +198,16 @@ class TestAutofillClient : public AutofillClient { test_personal_data_manager_ = std::move(pdm); } + void set_cvc_authenticator( + std::unique_ptr<CreditCardCVCAuthenticator> authenticator) { + cvc_authenticator_ = std::move(authenticator); + } + + void set_otp_authenticator( + std::unique_ptr<CreditCardOtpAuthenticator> authenticator) { + otp_authenticator_ = std::move(authenticator); + } + void set_test_strike_database( std::unique_ptr<TestStrikeDatabase> test_strike_database) { test_strike_database_ = std::move(test_strike_database); @@ -305,6 +323,8 @@ class TestAutofillClient : public AutofillClient { std::unique_ptr<PrefService> prefs_; std::unique_ptr<TestStrikeDatabase> test_strike_database_; std::unique_ptr<payments::PaymentsClient> payments_client_; + std::unique_ptr<CreditCardCVCAuthenticator> cvc_authenticator_; + std::unique_ptr<CreditCardOtpAuthenticator> otp_authenticator_; // AutofillOfferManager and TestFormDataImporter must be destroyed before // TestPersonalDataManager, because the former's destructors refer to the diff --git a/chromium/components/autofill/core/browser/test_autofill_clock.cc b/chromium/components/autofill/core/browser/test_autofill_clock.cc index 41254e32669..8b0dc260d9d 100644 --- a/chromium/components/autofill/core/browser/test_autofill_clock.cc +++ b/chromium/components/autofill/core/browser/test_autofill_clock.cc @@ -4,6 +4,7 @@ #include "components/autofill/core/browser/test_autofill_clock.h" +#include <memory> #include <utility> #include "base/test/simple_test_clock.h" @@ -11,22 +12,28 @@ namespace autofill { -TestAutofillClock::TestAutofillClock(base::Time now) { - AutofillClock::SetTestClock(&test_clock_); +TestAutofillClock::TestAutofillClock(base::Time now) + : TestAutofillClock(std::make_unique<base::SimpleTestClock>()) { SetNow(now); } +TestAutofillClock::TestAutofillClock( + std::unique_ptr<base::SimpleTestClock> test_clock) + : test_clock_(std::move(test_clock)) { + AutofillClock::SetTestClock(test_clock_.get()); +} + TestAutofillClock::~TestAutofillClock() { // Destroys the test clock and resets a normal clock. AutofillClock::SetClock(); } void TestAutofillClock::SetNow(base::Time now) { - test_clock_.SetNow(now); + test_clock_->SetNow(now); } void TestAutofillClock::Advance(base::TimeDelta delta) { - test_clock_.Advance(delta); + test_clock_->Advance(delta); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_autofill_clock.h b/chromium/components/autofill/core/browser/test_autofill_clock.h index 4fe92015e05..09f004f339a 100644 --- a/chromium/components/autofill/core/browser/test_autofill_clock.h +++ b/chromium/components/autofill/core/browser/test_autofill_clock.h @@ -5,10 +5,12 @@ #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_CLOCK_H_ #define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_CLOCK_H_ -#include "base/test/simple_test_clock.h" +#include <memory> + +#include "base/time/time.h" namespace base { -class Time; +class SimpleTestClock; } // namespace base namespace autofill { @@ -20,6 +22,7 @@ namespace autofill { class TestAutofillClock { public: explicit TestAutofillClock(base::Time now = {}); + explicit TestAutofillClock(std::unique_ptr<base::SimpleTestClock> test_clock); TestAutofillClock(const TestAutofillClock&) = delete; TestAutofillClock& operator=(const TestAutofillClock&) = delete; @@ -33,7 +36,7 @@ class TestAutofillClock { void Advance(base::TimeDelta delta); private: - base::SimpleTestClock test_clock_; + std::unique_ptr<base::SimpleTestClock> test_clock_; }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.cc b/chromium/components/autofill/core/browser/test_autofill_driver.cc index 80b5a85cea9..d6f2046987b 100644 --- a/chromium/components/autofill/core/browser/test_autofill_driver.cc +++ b/chromium/components/autofill/core/browser/test_autofill_driver.cc @@ -31,6 +31,10 @@ bool TestAutofillDriver::IsIncognito() const { return is_incognito_; } +bool TestAutofillDriver::IsInActiveFrame() const { + return is_in_active_frame_; +} + bool TestAutofillDriver::IsInAnyMainFrame() const { return is_in_any_main_frame_; } @@ -57,13 +61,6 @@ bool TestAutofillDriver::RendererIsAvailable() { return true; } -#if !BUILDFLAG(IS_IOS) -webauthn::InternalAuthenticator* -TestAutofillDriver::GetOrCreateCreditCardInternalAuthenticator() { - return test_authenticator_.get(); -} -#endif - std::vector<FieldGlobalId> TestAutofillDriver::FillOrPreviewForm( int query_id, mojom::RendererFormDataAction action, @@ -80,48 +77,18 @@ std::vector<FieldGlobalId> TestAutofillDriver::FillOrPreviewForm( return result; } -void TestAutofillDriver::HandleParsedForms( - const std::vector<const FormData*>& forms) {} - -void TestAutofillDriver::SendAutofillTypePredictionsToRenderer( - const std::vector<FormStructure*>& forms) { -} - -void TestAutofillDriver::RendererShouldAcceptDataListSuggestion( - const FieldGlobalId& field, - const std::u16string& value) {} - -void TestAutofillDriver::RendererShouldClearFilledSection() {} - -void TestAutofillDriver::RendererShouldClearPreviewedForm() { -} - -void TestAutofillDriver::RendererShouldFillFieldWithValue( - const FieldGlobalId& field, - const std::u16string& value) {} - -void TestAutofillDriver::RendererShouldPreviewFieldWithValue( - const FieldGlobalId& field, - const std::u16string& value) {} - -void TestAutofillDriver::RendererShouldSetSuggestionAvailability( - const FieldGlobalId& field, - const mojom::AutofillState state) {} - -void TestAutofillDriver::PopupHidden() { -} - net::IsolationInfo TestAutofillDriver::IsolationInfo() { return isolation_info_; } -void TestAutofillDriver::SendFieldsEligibleForManualFillingToRenderer( - const std::vector<FieldGlobalId>& fields) {} - void TestAutofillDriver::SetIsIncognito(bool is_incognito) { is_incognito_ = is_incognito; } +void TestAutofillDriver::SetIsInActiveFrame(bool is_in_active_frame) { + is_in_active_frame_ = is_in_active_frame; +} + void TestAutofillDriver::SetIsInAnyMainFrame(bool is_in_any_main_frame) { is_in_any_main_frame_ = is_in_any_main_frame; } diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.h b/chromium/components/autofill/core/browser/test_autofill_driver.h index e69366b4cc2..eeee3136d0c 100644 --- a/chromium/components/autofill/core/browser/test_autofill_driver.h +++ b/chromium/components/autofill/core/browser/test_autofill_driver.h @@ -44,16 +44,13 @@ class TestAutofillDriver : public ContentAutofillDriver { // AutofillDriver implementation overrides. bool IsIncognito() const override; + bool IsInActiveFrame() const override; bool IsInAnyMainFrame() const override; bool IsPrerendering() const override; bool CanShowAutofillUi() const override; ui::AXTreeID GetAxTreeId() const override; scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override; bool RendererIsAvailable() override; -#if !BUILDFLAG(IS_IOS) - webauthn::InternalAuthenticator* GetOrCreateCreditCardInternalAuthenticator() - override; -#endif // The return value contains the members (field, type) of `field_type_map` for // which `field_type_filter_.Run(triggered_origin, field, type)` is true. std::vector<FieldGlobalId> FillOrPreviewForm( @@ -63,31 +60,32 @@ class TestAutofillDriver : public ContentAutofillDriver { const url::Origin& triggered_origin, const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map) override; - void HandleParsedForms(const std::vector<const FormData*>& forms) override; + void HandleParsedForms(const std::vector<FormData>& forms) override {} void SendAutofillTypePredictionsToRenderer( - const std::vector<FormStructure*>& forms) override; + const std::vector<FormStructure*>& forms) override {} void RendererShouldAcceptDataListSuggestion( const FieldGlobalId& field, - const std::u16string& value) override; - void RendererShouldClearFilledSection() override; - void RendererShouldClearPreviewedForm() override; + const std::u16string& value) override {} + void RendererShouldClearFilledSection() override {} + void RendererShouldClearPreviewedForm() override {} void RendererShouldFillFieldWithValue(const FieldGlobalId& field, - const std::u16string& value) override; + const std::u16string& value) override {} void RendererShouldPreviewFieldWithValue( const FieldGlobalId& field, - const std::u16string& value) override; + const std::u16string& value) override {} void RendererShouldSetSuggestionAvailability( const FieldGlobalId& field, - const mojom::AutofillState state) override; - void PopupHidden() override; + const mojom::AutofillState state) override {} + void PopupHidden() override {} net::IsolationInfo IsolationInfo() override; void SendFieldsEligibleForManualFillingToRenderer( - const std::vector<FieldGlobalId>& fields) override; + const std::vector<FieldGlobalId>& fields) override {} // Methods unique to TestAutofillDriver that tests can use to specialize // functionality. void SetIsIncognito(bool is_incognito); + void SetIsInActiveFrame(bool is_in_active_frame); void SetIsInAnyMainFrame(bool is_in_any_main_frame); void SetIsolationInfo(const net::IsolationInfo& isolation_info); @@ -106,7 +104,8 @@ class TestAutofillDriver : public ContentAutofillDriver { network::TestURLLoaderFactory test_url_loader_factory_; scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_; bool is_incognito_ = false; - bool is_in_any_main_frame_ = false; + bool is_in_active_frame_ = true; + bool is_in_any_main_frame_ = true; net::IsolationInfo isolation_info_; base::RepeatingCallback< bool(const url::Origin&, FieldGlobalId, ServerFieldType)> diff --git a/chromium/components/autofill/core/browser/test_autofill_manager_waiter.cc b/chromium/components/autofill/core/browser/test_autofill_manager_waiter.cc new file mode 100644 index 00000000000..55b2d9d4461 --- /dev/null +++ b/chromium/components/autofill/core/browser/test_autofill_manager_waiter.cc @@ -0,0 +1,205 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/test_autofill_manager_waiter.h" + +#include "base/check_op.h" +#include "base/containers/contains.h" +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/test/scoped_run_loop_timeout.h" + +namespace autofill { + +TestAutofillManagerWaiter::State::State() = default; +TestAutofillManagerWaiter::State::~State() = default; + +TestAutofillManagerWaiter::EventCount* TestAutofillManagerWaiter::State::Get( + AfterEvent event) { + auto it = base::ranges::find(events, event, &EventCount::event); + return it != events.end() ? &*it : nullptr; +} + +TestAutofillManagerWaiter::EventCount& +TestAutofillManagerWaiter::State::GetOrCreate(AfterEvent event, + base::Location location) { + if (EventCount* e = Get(event)) + return *e; + return *events.insert(events.end(), {event, location}); +} + +size_t TestAutofillManagerWaiter::State::num_pending_calls() const { + size_t pending_calls = 0; + for (const EventCount& e : events) + pending_calls += e.num_pending_calls; + return pending_calls; +} + +size_t TestAutofillManagerWaiter::State::num_total_calls() const { + size_t total_calls = 0; + for (const EventCount& e : events) + total_calls += e.num_total_calls; + return total_calls; +} + +std::string TestAutofillManagerWaiter::State::Describe() const { + std::vector<std::string> strings; + for (const auto& e : events) { + strings.push_back(base::StringPrintf( + "[event=%s, pending=%zu, total=%zu]", e.location.function_name(), + e.num_pending_calls, e.num_total_calls)); + } + return base::JoinString(strings, ", "); +} + +TestAutofillManagerWaiter::TestAutofillManagerWaiter( + AutofillManager& manager, + std::initializer_list<AfterEvent> relevant_events) + : relevant_events_(relevant_events) { + observation_.Observe(&manager); +} + +TestAutofillManagerWaiter::~TestAutofillManagerWaiter() = default; + +void TestAutofillManagerWaiter::OnAutofillManagerDestroyed() { + observation_.Reset(); +} + +void TestAutofillManagerWaiter::OnAutofillManagerReset() { + Reset(); +} + +void TestAutofillManagerWaiter::OnBeforeLanguageDetermined() { + Increment(&AutofillManager::Observer::OnAfterLanguageDetermined); +} + +void TestAutofillManagerWaiter::OnAfterLanguageDetermined() { + Decrement(&AutofillManager::Observer::OnAfterLanguageDetermined); +} + +void TestAutofillManagerWaiter::OnBeforeFormsSeen() { + Increment(&AutofillManager::Observer::OnAfterFormsSeen); +} + +void TestAutofillManagerWaiter::OnAfterFormsSeen() { + Decrement(&AutofillManager::Observer::OnAfterFormsSeen); +} + +void TestAutofillManagerWaiter::OnBeforeTextFieldDidChange() { + Increment(&AutofillManager::Observer::OnAfterTextFieldDidChange); +} + +void TestAutofillManagerWaiter::OnAfterTextFieldDidChange() { + Decrement(&AutofillManager::Observer::OnAfterTextFieldDidChange); +} + +void TestAutofillManagerWaiter::OnBeforeAskForValuesToFill() { + Increment(&AutofillManager::Observer::OnAfterAskForValuesToFill); +} + +void TestAutofillManagerWaiter::OnAfterAskForValuesToFill() { + Decrement(&AutofillManager::Observer::OnAfterAskForValuesToFill); +} + +void TestAutofillManagerWaiter::OnBeforeDidFillAutofillFormData() { + Increment(&AutofillManager::Observer::OnAfterDidFillAutofillFormData); +} + +void TestAutofillManagerWaiter::OnAfterDidFillAutofillFormData() { + Decrement(&AutofillManager::Observer::OnAfterDidFillAutofillFormData); +} + +void TestAutofillManagerWaiter::OnBeforeJavaScriptChangedAutofilledValue() { + Increment( + &AutofillManager::Observer::OnAfterJavaScriptChangedAutofilledValue); +} + +void TestAutofillManagerWaiter::OnAfterJavaScriptChangedAutofilledValue() { + Decrement( + &AutofillManager::Observer::OnAfterJavaScriptChangedAutofilledValue); +} + +void TestAutofillManagerWaiter::Reset() { + // The declaration order ensures that `lock` is destroyed before `state`, so + // that `state_->lock` has been released at its own destruction time. + auto state = std::make_unique<State>(); + base::AutoLock lock(state_->lock); + VLOG(1) << __func__; + ASSERT_EQ(state_->num_pending_calls(), 0u) << state_->Describe(); + using std::swap; + swap(state_, state); +} + +bool TestAutofillManagerWaiter::IsRelevant(AfterEvent event) const { + return relevant_events_.empty() || base::Contains(relevant_events_, event); +} + +void TestAutofillManagerWaiter::Increment(AfterEvent event, + base::Location location) { + base::AutoLock lock(state_->lock); + if (!IsRelevant(event)) { + VLOG(1) << "Ignoring irrelevant event: " << __func__ << "(" + << location.function_name() << ")"; + return; + } + if (state_->run_loop.AnyQuitCalled()) { + VLOG(1) << "Ignoring event because no more calls are awaited: " << __func__ + << "(" << location.function_name() << ")"; + return; + } + VLOG(1) << __func__ << "(" << location.function_name() << ")"; + EventCount& e = state_->GetOrCreate(event, location); + e.location = location; + ++e.num_total_calls; + ++e.num_pending_calls; +} + +void TestAutofillManagerWaiter::Decrement(AfterEvent event, + base::Location location) { + base::AutoLock lock(state_->lock); + if (!IsRelevant(event)) { + VLOG(1) << "Ignoring irrelevant event: " << __func__ << "(" + << location.function_name() << ")"; + return; + } + if (state_->run_loop.AnyQuitCalled()) { + VLOG(1) << "Ignoring event because no more calls are awaited: " << __func__ + << "(" << location.function_name() << ")"; + return; + } + VLOG(1) << __func__ << "(" << location.function_name() << ")"; + EventCount* e = state_->Get(event); + ASSERT_TRUE(e) << state_->Describe(); + ASSERT_GT(e->num_pending_calls, 0u) << state_->Describe(); + if (state_->num_awaiting_total_calls > 0) + --state_->num_awaiting_total_calls; + --e->num_pending_calls; + if (state_->num_pending_calls() == 0 && state_->num_awaiting_total_calls == 0) + state_->run_loop.Quit(); +} + +testing::AssertionResult TestAutofillManagerWaiter::Wait( + size_t num_awaiting_calls) { + base::ReleasableAutoLock lock(&state_->lock); + if (state_->run_loop.AnyQuitCalled()) { + return testing::AssertionFailure() + << "Waiter has not been Reset() since last Wait()."; + } + // Some events may already have happened. + num_awaiting_calls = num_awaiting_calls > state_->num_total_calls() + ? num_awaiting_calls - state_->num_total_calls() + : 0u; + if (state_->num_pending_calls() > 0 || num_awaiting_calls > 0) { + base::test::ScopedRunLoopTimeout run_loop_timeout( + FROM_HERE, timeout_, + base::BindRepeating(&State::Describe, base::Unretained(state_.get()))); + state_->num_awaiting_total_calls = num_awaiting_calls; + lock.Release(); + state_->run_loop.Run(); + } + return testing::AssertionSuccess(); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_autofill_manager_waiter.h b/chromium/components/autofill/core/browser/test_autofill_manager_waiter.h new file mode 100644 index 00000000000..c382bbdf4eb --- /dev/null +++ b/chromium/components/autofill/core/browser/test_autofill_manager_waiter.h @@ -0,0 +1,163 @@ +// Copyright 2022 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_TEST_AUTOFILL_MANAGER_WAITER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_MANAGER_WAITER_H_ + +#include <list> +#include <memory> +#include <vector> + +#include "base/run_loop.h" +#include "base/scoped_observation.h" +#include "base/synchronization/lock.h" +#include "base/time/time.h" +#include "components/autofill/core/browser/autofill_manager.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +// Records AutofillManager::Observer::OnBeforeFoo() events and blocks until the +// corresponding OnAfterFoo() events have happened. +// +// This mechanism relies on AutofillManager::Observer's guarantee that +// OnBeforeFoo() is followed by OnAfterFoo() in normal circumstances. +// +// If an OnBeforeFoo() event happens multiple times, the waiter expects multiple +// OnAfterFoo() events. +// +// Which events Wait() should be waiting for can be limited by providing a list +// of `relevant_events` to the constructor. This list should contain the +// OnAfterFoo(), *not* the OnBeforeFoo() events. +// +// By default, Wait() succeeds immediately if there are no pending calls, that +// is, if no OnBeforeFoo() without matching OnAfterFoo() has been observed. +// Calling Wait(k) with an integer argument `k` overrides this behaviour: in +// this case, it expects a total of at least `k` OnAfterFoo() events to happen +// or have happened. +// +// The waiter resets itself on OnAutofillManagerDestroyed() events. This makes +// it suitable for use with TestAutofillManagerInjector. +// +// Typical usage is as follows: +// +// TestAutofillManagerWaiter waiter(manager, +// {&AutofillManager::Observer::OnAfterFoo, +// &AutofillManager::Observer::OnAfterBar, +// ...}); +// ... trigger events ... +// ASSERT_TRUE(waiter.Wait()); // Blocks. +// +// In browser tests, it may be necessary to tell Wait() to wait for at least, +// say, 1 event because triggering events is asynchronous due to Mojo: +// +// TestAutofillManagerWaiter waiter(manager, +// {&AutofillManager::Observer::OnAfterFoo, +// ...}); +// ... trigger asynchronous OnFoo event ... +// ASSERT_TRUE(waiter.Wait(1)); // Blocks until at least one OnFoo() event +// // has happened since the creation of +// // `waiter`. +// +// In case of failure, the error message of Wait() informs about the pending +// OnAfterFoo() calls. +class TestAutofillManagerWaiter : public AutofillManager::Observer { + public: + // An OnFooAfter() event. As a convention, throughout this class we use the + // OnAfterFoo() events to identify the pair of OnAfterFoo() / OnBeforeFoo(). + using AfterEvent = void (AutofillManager::Observer::*)(); + + explicit TestAutofillManagerWaiter( + AutofillManager& manager, + std::initializer_list<AfterEvent> relevant_events = {}); + ~TestAutofillManagerWaiter() override; + + // Blocks until all pending OnAfterFoo() events have been observed and at + // least `num_awaiting_calls` relevant events have been observed. + [[nodiscard]] testing::AssertionResult Wait(size_t num_awaiting_calls = 0); + + // Equivalent to re-initialization. + void Reset(); + + // The timeout of the RunLoop. + base::TimeDelta timeout() const { return timeout_; } + void set_timeout(base::TimeDelta timeout) { timeout_ = timeout; } + + private: + struct EventCount { + // An AutofillManager::Observer::OnAfterFoo() event. + AfterEvent event; + // The OnBeforeFoo() function. Used for meaningful error messages. + base::Location location; + // The total number of recorded OnBeforeFoo() events. + size_t num_total_calls = 0; + // The number of recorded OnBeforeFoo() events without a corresponding + // OnAfterFoo() event. + size_t num_pending_calls = 0; + }; + + // State variables for easy resetting. + struct State { + State(); + State(State&) = delete; + State& operator=(State&) = delete; + ~State(); + + EventCount& GetOrCreate(AfterEvent event, base::Location location); + EventCount* Get(AfterEvent event); + + size_t num_total_calls() const; + size_t num_pending_calls() const; + + std::string Describe() const; + + // Effectively a map from `AfterEvent` to its count. Since `AfterEvent` is + // only equality-comparable, we use a list. (The list, rather than a vector, + // avoids invalidation of the references returned by GetOrCreate().) + std::list<EventCount> events; + // Decrement() unblocks Wait() when the number of awaited calls reaches 0. + size_t num_awaiting_total_calls = std::numeric_limits<size_t>::max(); + // Running iff there are no awaited and no pending calls. + base::RunLoop run_loop = base::RunLoop(); + // Functions that access the state should be mutually exclusive. + base::Lock lock; + }; + + bool IsRelevant(AfterEvent event) const; + void Increment(AfterEvent event, + base::Location location = base::Location::Current()); + void Decrement(AfterEvent event, + base::Location location = base::Location::Current()); + + void OnAutofillManagerDestroyed() override; + void OnAutofillManagerReset() override; + + void OnBeforeLanguageDetermined() override; + void OnAfterLanguageDetermined() override; + + void OnBeforeFormsSeen() override; + void OnAfterFormsSeen() override; + + void OnBeforeTextFieldDidChange() override; + void OnAfterTextFieldDidChange() override; + + void OnBeforeAskForValuesToFill() override; + void OnAfterAskForValuesToFill() override; + + void OnBeforeDidFillAutofillFormData() override; + void OnAfterDidFillAutofillFormData() override; + + void OnBeforeJavaScriptChangedAutofilledValue() override; + void OnAfterJavaScriptChangedAutofilledValue() override; + + std::vector<AfterEvent> relevant_events_; + std::unique_ptr<State> state_ = std::make_unique<State>(); + base::TimeDelta timeout_ = base::Seconds(5); + base::ScopedObservation<AutofillManager, AutofillManager::Observer> + observation_{this}; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_MANAGER_WAITER_H_ diff --git a/chromium/components/autofill/core/browser/test_browser_autofill_manager.cc b/chromium/components/autofill/core/browser/test_browser_autofill_manager.cc index c51a3563b23..414bbccafd4 100644 --- a/chromium/components/autofill/core/browser/test_browser_autofill_manager.cc +++ b/chromium/components/autofill/core/browser/test_browser_autofill_manager.cc @@ -4,15 +4,18 @@ #include "components/autofill/core/browser/test_browser_autofill_manager.h" +#include "autofill_test_utils.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_suggestion_generator.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/mock_single_field_form_fill_router.h" #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/browser/test_autofill_driver.h" -#include "components/autofill/core/browser/test_form_structure.h" +#include "components/autofill/core/browser/test_autofill_manager_waiter.h" #include "components/autofill/core/browser/test_personal_data_manager.h" +#include "form_structure_test_api.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "testing/gtest/include/gtest/gtest.h" @@ -30,6 +33,76 @@ TestBrowserAutofillManager::TestBrowserAutofillManager( TestBrowserAutofillManager::~TestBrowserAutofillManager() = default; +void TestBrowserAutofillManager::OnLanguageDetermined( + const translate::LanguageDetectionDetails& details) { + TestAutofillManagerWaiter waiter( + *this, {&AutofillManager::Observer::OnAfterLanguageDetermined}); + AutofillManager::OnLanguageDetermined(details); + ASSERT_TRUE(waiter.Wait()); +} + +void TestBrowserAutofillManager::OnFormsSeen( + const std::vector<FormData>& updated_forms, + const std::vector<FormGlobalId>& removed_forms) { + TestAutofillManagerWaiter waiter(*this, {&Observer::OnAfterFormsSeen}); + AutofillManager::OnFormsSeen(updated_forms, removed_forms); + ASSERT_TRUE(waiter.Wait()); +} + +void TestBrowserAutofillManager::OnTextFieldDidChange( + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + const base::TimeTicks timestamp) { + TestAutofillManagerWaiter waiter(*this, + {&Observer::OnAfterTextFieldDidChange}); + AutofillManager::OnTextFieldDidChange(form, field, bounding_box, timestamp); + ASSERT_TRUE(waiter.Wait()); +} + +void TestBrowserAutofillManager::OnDidFillAutofillFormData( + const FormData& form, + const base::TimeTicks timestamp) { + TestAutofillManagerWaiter waiter(*this, + {&Observer::OnAfterDidFillAutofillFormData}); + AutofillManager::OnDidFillAutofillFormData(form, timestamp); + ASSERT_TRUE(waiter.Wait()); +} + +void TestBrowserAutofillManager::OnAskForValuesToFill( + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + int query_id, + bool autoselect_first_suggestion, + TouchToFillEligible touch_to_fill_eligible) { + TestAutofillManagerWaiter waiter(*this, + {&Observer::OnAfterAskForValuesToFill}); + AutofillManager::OnAskForValuesToFill(form, field, bounding_box, query_id, + autoselect_first_suggestion, + touch_to_fill_eligible); + ASSERT_TRUE(waiter.Wait()); +} + +void TestBrowserAutofillManager::OnJavaScriptChangedAutofilledValue( + const FormData& form, + const FormFieldData& field, + const std::u16string& old_value) { + TestAutofillManagerWaiter waiter( + *this, {&Observer::OnAfterJavaScriptChangedAutofilledValue}); + AutofillManager::OnJavaScriptChangedAutofilledValue(form, field, old_value); + ASSERT_TRUE(waiter.Wait()); +} + +void TestBrowserAutofillManager::OnFormSubmitted( + const FormData& form, + const bool known_success, + const mojom::SubmissionSource source) { + TestAutofillManagerWaiter waiter(*this, {&Observer::OnAfterFormsSeen}); + AutofillManager::OnFormSubmitted(form, known_success, source); + ASSERT_TRUE(waiter.Wait()); +} + bool TestBrowserAutofillManager::IsAutofillProfileEnabled() const { return autofill_profile_enabled_; } @@ -107,37 +180,51 @@ int TestBrowserAutofillManager::GetPackedCreditCardID(int credit_card_id) { void TestBrowserAutofillManager::AddSeenForm( const FormData& form, const std::vector<ServerFieldType>& heuristic_types, - const std::vector<ServerFieldType>& server_types) { + const std::vector<ServerFieldType>& server_types, + bool preserve_values_in_form_structure) { std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>> all_heuristic_types; - - base::ranges::transform( - heuristic_types, std::back_inserter(all_heuristic_types), - [](ServerFieldType type) - -> std::vector<std::pair<PatternSource, ServerFieldType>> { - return {{GetActivePatternSource(), type}}; - }); - - AddSeenForm(form, all_heuristic_types, server_types); + for (ServerFieldType type : heuristic_types) + all_heuristic_types.push_back({{GetActivePatternSource(), type}}); + AddSeenForm(form, all_heuristic_types, server_types, + preserve_values_in_form_structure); } void TestBrowserAutofillManager::AddSeenForm( const FormData& form, const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>& heuristic_types, + const std::vector<ServerFieldType>& server_types, + bool preserve_values_in_form_structure) { + auto form_structure = std::make_unique<FormStructure>( + preserve_values_in_form_structure ? form : test::WithoutValues(form)); + FormGlobalId form_id = form_structure->global_id(); + AddSeenFormStructure(std::move(form_structure)); + SetSeenFormPredictions(form_id, heuristic_types, server_types); + form_interactions_ukm_logger()->OnFormsParsed(client()->GetUkmSourceId()); +} + +void TestBrowserAutofillManager::SetSeenFormPredictions( + FormGlobalId form_id, + const std::vector<ServerFieldType>& heuristic_types, const std::vector<ServerFieldType>& server_types) { - FormData empty_form = form; - for (auto& field : empty_form.fields) { - field.value = std::u16string(); - } + std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>> + all_heuristic_types; + for (ServerFieldType type : heuristic_types) + all_heuristic_types.push_back({{GetActivePatternSource(), type}}); + SetSeenFormPredictions(form_id, all_heuristic_types, server_types); +} - std::unique_ptr<TestFormStructure> form_structure = - std::make_unique<TestFormStructure>(empty_form); - form_structure->SetFieldTypes(heuristic_types, server_types); +void TestBrowserAutofillManager::SetSeenFormPredictions( + FormGlobalId form_id, + const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>& + heuristic_types, + const std::vector<ServerFieldType>& server_types) { + FormStructure* form_structure = FindCachedFormByRendererId(form_id); + ASSERT_TRUE(form_structure); + FormStructureTestApi(form_structure) + .SetFieldTypes(heuristic_types, server_types); form_structure->identify_sections_for_testing(); - AddSeenFormStructure(std::move(form_structure)); - - form_interactions_ukm_logger()->OnFormsParsed(client()->GetUkmSourceId()); } void TestBrowserAutofillManager::AddSeenFormStructure( @@ -161,9 +248,12 @@ void TestBrowserAutofillManager::OnAskForValuesToFillTest( const gfx::RectF& bounding_box, bool autoselect_first_suggestion, TouchToFillEligible touch_to_fill_eligible) { + TestAutofillManagerWaiter waiter( + *this, {&AutofillManager::Observer::OnAfterAskForValuesToFill}); BrowserAutofillManager::OnAskForValuesToFill( - query_id, form, field, bounding_box, autoselect_first_suggestion, + form, field, bounding_box, query_id, autoselect_first_suggestion, touch_to_fill_eligible); + ASSERT_TRUE(waiter.Wait()); } void TestBrowserAutofillManager::SetAutofillProfileEnabled( diff --git a/chromium/components/autofill/core/browser/test_browser_autofill_manager.h b/chromium/components/autofill/core/browser/test_browser_autofill_manager.h index 422bf0e0d49..f017a080f3a 100644 --- a/chromium/components/autofill/core/browser/test_browser_autofill_manager.h +++ b/chromium/components/autofill/core/browser/test_browser_autofill_manager.h @@ -37,6 +37,34 @@ class TestBrowserAutofillManager : public BrowserAutofillManager { TestAutofillClient* client() { return client_; } TestAutofillDriver* driver() { return driver_; } + // AutofillManager overrides. + // The overrides ensure that the thread is blocked until the form has been + // parsed (perhaps asynchronously, depending on AutofillParseAsync). + void OnLanguageDetermined( + const translate::LanguageDetectionDetails& details) override; + void OnFormsSeen(const std::vector<FormData>& updated_forms, + const std::vector<FormGlobalId>& removed_forms) override; + void OnTextFieldDidChange(const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + const base::TimeTicks timestamp) override; + void OnDidFillAutofillFormData(const FormData& form, + const base::TimeTicks timestamp) override; + void OnAskForValuesToFill( + const FormData& form, + const FormFieldData& field, + const gfx::RectF& bounding_box, + int query_id, + bool autoselect_first_suggestion, + TouchToFillEligible touch_to_fill_eligible) override; + void OnJavaScriptChangedAutofilledValue( + const FormData& form, + const FormFieldData& field, + const std::u16string& old_value) override; + void OnFormSubmitted(const FormData& form, + const bool known_success, + const mojom::SubmissionSource source) override; + // BrowserAutofillManager overrides. bool IsAutofillProfileEnabled() const override; bool IsAutofillCreditCardEnabled() const override; @@ -58,12 +86,25 @@ class TestBrowserAutofillManager : public BrowserAutofillManager { void AddSeenForm(const FormData& form, const std::vector<ServerFieldType>& heuristic_types, - const std::vector<ServerFieldType>& server_types); + const std::vector<ServerFieldType>& server_types, + bool preserve_values_in_form_structure = false); void AddSeenForm( const FormData& form, const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>& heuristic_types, + const std::vector<ServerFieldType>& server_types, + bool preserve_values_in_form_structure = false); + + void SetSeenFormPredictions( + FormGlobalId form_id, + const std::vector<ServerFieldType>& heuristic_types, + const std::vector<ServerFieldType>& server_types); + + void SetSeenFormPredictions( + FormGlobalId form_id, + const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>& + heuristic_types, const std::vector<ServerFieldType>& server_types); void AddSeenFormStructure(std::unique_ptr<FormStructure> form_structure); diff --git a/chromium/components/autofill/core/browser/test_event_waiter.h b/chromium/components/autofill/core/browser/test_event_waiter.h index b3d4ded4341..b9e228acfd9 100644 --- a/chromium/components/autofill/core/browser/test_event_waiter.h +++ b/chromium/components/autofill/core/browser/test_event_waiter.h @@ -11,7 +11,6 @@ #include "base/run_loop.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" namespace autofill { diff --git a/chromium/components/autofill/core/browser/test_form_data_importer.h b/chromium/components/autofill/core/browser/test_form_data_importer.h index 775deefc9fa..12dbe2e9bf4 100644 --- a/chromium/components/autofill/core/browser/test_form_data_importer.h +++ b/chromium/components/autofill/core/browser/test_form_data_importer.h @@ -23,6 +23,10 @@ class TestFormDataImporter : public FormDataImporter { std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager = nullptr); ~TestFormDataImporter() override = default; + + absl::optional<int64_t> fetched_card_instrument_id() { + return fetched_card_instrument_id_; + } }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_form_structure.h b/chromium/components/autofill/core/browser/test_form_structure.h deleted file mode 100644 index b4fd0258e91..00000000000 --- a/chromium/components/autofill/core/browser/test_form_structure.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_FORM_STRUCTURE_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_FORM_STRUCTURE_H_ - -#include <vector> - -#include "components/autofill/core/browser/form_parsing/field_candidates.h" -#include "components/autofill/core/browser/form_structure.h" - -namespace autofill { - -class TestFormStructure : public FormStructure { - public: - explicit TestFormStructure(const FormData& form); - - TestFormStructure(const TestFormStructure&) = delete; - TestFormStructure& operator=(const TestFormStructure&) = delete; - - ~TestFormStructure() override; - - // Set the heuristic and server types for each field. The `heuristic_types` - // and `server_types` vectors must be aligned with the indices of the fields - // in the form. - void SetFieldTypes(const std::vector<ServerFieldType>& heuristic_types, - const std::vector<ServerFieldType>& server_types); - - // Set the heuristic and server types for each field. The `heuristic_types` - // and `server_types` vectors must be aligned with the indices of the fields - // in the form. For each field in `heuristic_types` there must be exactly one - // `GetActivePatternSource()` prediction and any number of alternative - // predictions. - void SetFieldTypes( - const std::vector<std::vector<std::pair<PatternSource, ServerFieldType>>>& - heuristic_types, - const std::vector<ServerFieldType>& server_types); -}; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_FORM_STRUCTURE_H_ diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.cc b/chromium/components/autofill/core/browser/test_personal_data_manager.cc index 0eb5bb147a7..fbb123b8556 100644 --- a/chromium/components/autofill/core/browser/test_personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/test_personal_data_manager.cc @@ -214,8 +214,20 @@ void TestPersonalDataManager::LoadCreditCardCloudTokenData() { } } +void TestPersonalDataManager::LoadIBANs() { + pending_ibans_query_ = 128; + { + std::vector<std::unique_ptr<IBAN>> ibans; + local_ibans_.swap(ibans); + std::unique_ptr<WDTypedResult> result = + std::make_unique<WDResult<std::vector<std::unique_ptr<IBAN>>>>( + AUTOFILL_IBANS_RESULT, std::move(ibans)); + OnWebDataServiceRequestDone(pending_ibans_query_, std::move(result)); + } +} + void TestPersonalDataManager::LoadUpiIds() { - pending_upi_ids_query_ = 128; + pending_upi_ids_query_ = 129; { std::vector<std::string> upi_ids = {"vpa@indianbank"}; std::unique_ptr<WDTypedResult> result = diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.h b/chromium/components/autofill/core/browser/test_personal_data_manager.h index c97952560e7..b42fd4c7ab8 100644 --- a/chromium/components/autofill/core/browser/test_personal_data_manager.h +++ b/chromium/components/autofill/core/browser/test_personal_data_manager.h @@ -59,6 +59,7 @@ class TestPersonalDataManager : public PersonalDataManager { void LoadProfiles() override; void LoadCreditCards() override; void LoadCreditCardCloudTokenData() override; + void LoadIBANs() override; void LoadUpiIds() override; bool IsAutofillEnabled() const override; bool IsAutofillProfileEnabled() const override; diff --git a/chromium/components/autofill/core/browser/touch_to_fill_delegate.cc b/chromium/components/autofill/core/browser/touch_to_fill_delegate.cc deleted file mode 100644 index 55af4f1d8d9..00000000000 --- a/chromium/components/autofill/core/browser/touch_to_fill_delegate.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2022 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/touch_to_fill_delegate.h" - -namespace autofill { - -TouchToFillDelegate::TouchToFillDelegate() = default; -TouchToFillDelegate::~TouchToFillDelegate() = default; - -bool TouchToFillDelegate::TryToShowTouchToFill(int query_id, - const FormData& form, - const FormFieldData& field) { - // TODO(crbug.com/1247698): Add eligibility checks and trigger TTF. - return false; -} - -} // namespace autofill diff --git a/chromium/components/autofill/core/browser/touch_to_fill_delegate.h b/chromium/components/autofill/core/browser/touch_to_fill_delegate.h deleted file mode 100644 index 2bea4e8cbe9..00000000000 --- a/chromium/components/autofill/core/browser/touch_to_fill_delegate.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2022 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_TOUCH_TO_FILL_DELEGATE_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_TOUCH_TO_FILL_DELEGATE_H_ - -#include "components/autofill/core/common/form_data.h" -#include "components/autofill/core/common/form_field_data.h" - -namespace autofill { - -// Delegate for in-browser Touch To Fill (TTF) surface display and selection. -// TODO(crbug.com/1324900): Consider using more descriptive name. -class TouchToFillDelegate { - public: - TouchToFillDelegate(); - TouchToFillDelegate(const TouchToFillDelegate&) = delete; - TouchToFillDelegate& operator=(const TouchToFillDelegate&) = delete; - virtual ~TouchToFillDelegate(); - - // Checks whether TTF is eligible for the given web form data. On success - // triggers the corresponding surface and returns |true|. - virtual bool TryToShowTouchToFill(int query_id, - const FormData& form, - const FormFieldData& field); -}; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TOUCH_TO_FILL_DELEGATE_H_ diff --git a/chromium/components/autofill/core/browser/touch_to_fill_delegate_impl.cc b/chromium/components/autofill/core/browser/touch_to_fill_delegate_impl.cc new file mode 100644 index 00000000000..e71d78a50d2 --- /dev/null +++ b/chromium/components/autofill/core/browser/touch_to_fill_delegate_impl.cc @@ -0,0 +1,86 @@ +// Copyright 2022 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/touch_to_fill_delegate_impl.h" + +#include "components/autofill/core/browser/browser_autofill_manager.h" +#include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_util.h" + +namespace autofill { + +TouchToFillDelegateImpl::TouchToFillDelegateImpl( + BrowserAutofillManager* manager) + : manager_(manager) { + DCHECK(manager); +} + +TouchToFillDelegateImpl::~TouchToFillDelegateImpl() = default; + +bool TouchToFillDelegateImpl::TryToShowTouchToFill(int query_id, + const FormData& form, + const FormFieldData& field) { + // Trigger only for a credit card field/form. + // TODO(crbug.com/1247698): Clarify field/form requirements. + if (manager_->GetPopupType(form, field) != PopupType::kCreditCards) + return false; + // Trigger only on supported platforms. + if (!manager_->client()->IsTouchToFillCreditCardSupported()) + return false; + // Trigger only if not shown before. + if (ttf_credit_card_state_ != TouchToFillState::kShouldShow) + return false; + // Trigger only on focusable empty field. + if (!field.is_focusable || !SanitizedFieldIsEmpty(field.value)) + return false; + // Trigger only if there is at least 1 complete valid credit card on file. + // Complete = contains number, expiration date and name on card. + // Valid = unexpired with valid number format. + PersonalDataManager* pdm = manager_->client()->GetPersonalDataManager(); + DCHECK(pdm); + std::vector<CreditCard*> cards_to_suggest = pdm->GetCreditCardsToSuggest( + manager_->client()->AreServerCardsSupported()); + auto NotACompleteValidCard = [](const CreditCard* card) { + return card->IsExpired(AutofillClock::Now()) || !card->HasNameOnCard() || + (card->record_type() != autofill::CreditCard::MASKED_SERVER_CARD && + !card->HasValidCardNumber()); + }; + base::EraseIf(cards_to_suggest, NotACompleteValidCard); + if (cards_to_suggest.empty()) + return false; + // Trigger only if the UI is available. + if (!manager_->driver()->CanShowAutofillUi()) + return false; + // Finally try showing the surface. + if (!manager_->client()->ShowTouchToFillCreditCard(GetWeakPtr())) + return false; + + ttf_credit_card_state_ = TouchToFillState::kIsShowing; + manager_->client()->HideAutofillPopup( + PopupHidingReason::kOverlappingWithTouchToFillSurface); + return true; +} + +bool TouchToFillDelegateImpl::IsShowingTouchToFill() { + return ttf_credit_card_state_ == TouchToFillState::kIsShowing; +} + +// TODO(crbug.com/1348538): Create a central point for TTF hiding decision. +void TouchToFillDelegateImpl::HideTouchToFill() { + if (IsShowingTouchToFill()) { + manager_->client()->HideTouchToFillCreditCard(); + ttf_credit_card_state_ = TouchToFillState::kWasShown; + } +} + +void TouchToFillDelegateImpl::Reset() { + HideTouchToFill(); + ttf_credit_card_state_ = TouchToFillState::kShouldShow; +} + +base::WeakPtr<TouchToFillDelegateImpl> TouchToFillDelegateImpl::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/touch_to_fill_delegate_impl.h b/chromium/components/autofill/core/browser/touch_to_fill_delegate_impl.h new file mode 100644 index 00000000000..7e5b98a60ca --- /dev/null +++ b/chromium/components/autofill/core/browser/touch_to_fill_delegate_impl.h @@ -0,0 +1,69 @@ +// Copyright 2022 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_TOUCH_TO_FILL_DELEGATE_IMPL_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_TOUCH_TO_FILL_DELEGATE_IMPL_H_ + +#include "base/memory/weak_ptr.h" +#include "components/autofill/core/browser/ui/touch_to_fill_delegate.h" +#include "components/autofill/core/common/form_data.h" +#include "components/autofill/core/common/form_field_data.h" + +namespace autofill { + +class BrowserAutofillManager; + +// Delegate for in-browser Touch To Fill (TTF) surface display and selection. +// Currently TTF surface is eligible only for credit card forms on click on +// an empty focusable field. +// +// If the surface was shown once, it won't be triggered again on the same page. +// But calling |Reset()| on navigation restores such showing eligibility. +// +// It is supposed to be owned by the given |BrowserAutofillManager|, and +// interact with it and its |AutofillClient| and |AutofillDriver|. +// +// Public methods are marked virtual for testing. +// TODO(crbug.com/1324900): Consider using more descriptive name. +class TouchToFillDelegateImpl : public TouchToFillDelegate { + public: + explicit TouchToFillDelegateImpl(BrowserAutofillManager* manager); + TouchToFillDelegateImpl(const TouchToFillDelegateImpl&) = delete; + TouchToFillDelegateImpl& operator=(const TouchToFillDelegateImpl&) = delete; + ~TouchToFillDelegateImpl() override; + + // Checks whether TTF is eligible for the given web form data. On success + // triggers the corresponding surface and returns |true|. + virtual bool TryToShowTouchToFill(int query_id, + const FormData& form, + const FormFieldData& field); + + // Returns whether the TTF surface is currently being shown. + virtual bool IsShowingTouchToFill(); + + // Hides the TTF surface if one is shown. + virtual void HideTouchToFill(); + + // Resets the delegate to its starting state (e.g. on navigation). + virtual void Reset(); + + private: + base::WeakPtr<TouchToFillDelegateImpl> GetWeakPtr(); + + enum class TouchToFillState { + kShouldShow, + kIsShowing, + kWasShown, + }; + + TouchToFillState ttf_credit_card_state_ = TouchToFillState::kShouldShow; + + const raw_ptr<BrowserAutofillManager> manager_; + + base::WeakPtrFactory<TouchToFillDelegateImpl> weak_ptr_factory_{this}; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TOUCH_TO_FILL_DELEGATE_IMPL_H_ diff --git a/chromium/components/autofill/core/browser/touch_to_fill_delegate_impl_unittest.cc b/chromium/components/autofill/core/browser/touch_to_fill_delegate_impl_unittest.cc new file mode 100644 index 00000000000..37e6fde6b72 --- /dev/null +++ b/chromium/components/autofill/core/browser/touch_to_fill_delegate_impl_unittest.cc @@ -0,0 +1,297 @@ +// Copyright 2022 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/touch_to_fill_delegate_impl.h" +#include "base/test/task_environment.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/test_autofill_client.h" +#include "components/autofill/core/browser/test_autofill_driver.h" +#include "components/autofill/core/browser/test_browser_autofill_manager.h" +#include "components/autofill/core/common/autofill_clock.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::NiceMock; +using testing::Ref; +using testing::Return; + +namespace autofill { + +namespace { + +// A constant value to use as a suggestions query ID. +const int kQueryId = 1; + +class MockAutofillDriver : public TestAutofillDriver { + public: + MockAutofillDriver() = default; + MockAutofillDriver(const MockAutofillDriver&) = delete; + MockAutofillDriver& operator=(const MockAutofillDriver&) = delete; + ~MockAutofillDriver() override = default; + + MOCK_METHOD(bool, CanShowAutofillUi, (), (const, override)); +}; + +class MockAutofillClient : public TestAutofillClient { + public: + MockAutofillClient() = default; + MockAutofillClient(const MockAutofillClient&) = delete; + MockAutofillClient& operator=(const MockAutofillClient&) = delete; + ~MockAutofillClient() override = default; + + MOCK_METHOD(bool, IsTouchToFillCreditCardSupported, (), (override)); + MOCK_METHOD(bool, + ShowTouchToFillCreditCard, + (base::WeakPtr<autofill::TouchToFillDelegate>), + (override)); + MOCK_METHOD(void, HideTouchToFillCreditCard, (), (override)); + MOCK_METHOD(void, HideAutofillPopup, (PopupHidingReason reason), (override)); +}; + +class MockBrowserAutofillManager : public TestBrowserAutofillManager { + public: + MockBrowserAutofillManager(TestAutofillDriver* driver, + TestAutofillClient* client) + : TestBrowserAutofillManager(driver, client) {} + MockBrowserAutofillManager(const MockBrowserAutofillManager&) = delete; + MockBrowserAutofillManager& operator=(const MockBrowserAutofillManager&) = + delete; + ~MockBrowserAutofillManager() override = default; + + MOCK_METHOD(PopupType, + GetPopupType, + (const FormData& form, const FormFieldData& field), + (override)); +}; + +} // namespace + +class TouchToFillDelegateImplUnitTest : public testing::Test { + protected: + void SetUp() override { + autofill_client_.SetPrefs(test::PrefServiceForTesting()); + autofill_client_.GetPersonalDataManager()->SetPrefService( + autofill_client_.GetPrefs()); + autofill_driver_ = std::make_unique<NiceMock<MockAutofillDriver>>(); + browser_autofill_manager_ = + std::make_unique<NiceMock<MockBrowserAutofillManager>>( + autofill_driver_.get(), &autofill_client_); + touch_to_fill_delegate_ = std::make_unique<TouchToFillDelegateImpl>( + browser_autofill_manager_.get()); + + // Default setup for successful |TryToShowTouchToFill|. + field_.is_focusable = true; + autofill_client_.GetPersonalDataManager()->AddCreditCard( + test::GetCreditCard()); + ON_CALL(*browser_autofill_manager_, GetPopupType(_, _)) + .WillByDefault(Return(PopupType::kCreditCards)); + ON_CALL(autofill_client_, IsTouchToFillCreditCardSupported) + .WillByDefault(Return(true)); + ON_CALL(*autofill_driver_, CanShowAutofillUi).WillByDefault(Return(true)); + ON_CALL(autofill_client_, ShowTouchToFillCreditCard) + .WillByDefault(Return(true)); + } + + void TearDown() override { + browser_autofill_manager_.reset(); + touch_to_fill_delegate_.reset(); + autofill_driver_.reset(); + } + + void TryToShowTouchToFill(bool expected_success) { + EXPECT_CALL(autofill_client_, + HideAutofillPopup( + PopupHidingReason::kOverlappingWithTouchToFillSurface)) + .Times(expected_success ? 1 : 0); + EXPECT_EQ(expected_success, touch_to_fill_delegate_->TryToShowTouchToFill( + kQueryId, form_, field_)); + EXPECT_EQ(expected_success, + touch_to_fill_delegate_->IsShowingTouchToFill()); + } + + FormData form_; + FormFieldData field_; + + base::test::TaskEnvironment task_environment_; + NiceMock<MockAutofillClient> autofill_client_; + std::unique_ptr<NiceMock<MockAutofillDriver>> autofill_driver_; + std::unique_ptr<TouchToFillDelegateImpl> touch_to_fill_delegate_; + std::unique_ptr<MockBrowserAutofillManager> browser_autofill_manager_; +}; + +TEST_F(TouchToFillDelegateImplUnitTest, TryToShowTouchToFillSucceeds) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + + TryToShowTouchToFill(/*expected_success=*/true); +} + +TEST_F(TouchToFillDelegateImplUnitTest, + TryToShowTouchToFillFailsIfNotCreditCardField) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + EXPECT_CALL(*browser_autofill_manager_, GetPopupType(Ref(form_), Ref(field_))) + .WillOnce(Return(PopupType::kAddresses)); + + TryToShowTouchToFill(/*expected_success=*/false); +} + +TEST_F(TouchToFillDelegateImplUnitTest, + TryToShowTouchToFillFailsIfNotSupported) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + EXPECT_CALL(autofill_client_, IsTouchToFillCreditCardSupported) + .WillOnce(Return(false)); + + TryToShowTouchToFill(/*expected_success=*/false); +} + +TEST_F(TouchToFillDelegateImplUnitTest, + TryToShowTouchToFillFailsIfAlreadyShown) { + TryToShowTouchToFill(/*expected_success=*/true); + + EXPECT_CALL( + autofill_client_, + HideAutofillPopup(PopupHidingReason::kOverlappingWithTouchToFillSurface)) + .Times(0); + EXPECT_FALSE( + touch_to_fill_delegate_->TryToShowTouchToFill(kQueryId, form_, field_)); + EXPECT_TRUE(touch_to_fill_delegate_->IsShowingTouchToFill()); +} + +TEST_F(TouchToFillDelegateImplUnitTest, TryToShowTouchToFillFailsIfWasShown) { + TryToShowTouchToFill(/*expected_success=*/true); + touch_to_fill_delegate_->HideTouchToFill(); + + TryToShowTouchToFill(/*expected_success=*/false); +} + +TEST_F(TouchToFillDelegateImplUnitTest, + TryToShowTouchToFillFailsIfFieldIsNotFocusable) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + field_.is_focusable = false; + + TryToShowTouchToFill(/*expected_success=*/false); +} + +TEST_F(TouchToFillDelegateImplUnitTest, + TryToShowTouchToFillFailsIfFieldHasValue) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + field_.value = u"Initial value"; + + TryToShowTouchToFill(/*expected_success=*/false); + + // But should ignore formatting characters. + field_.value = u"____-____-____-____"; + + TryToShowTouchToFill(/*expected_success=*/true); +} + +TEST_F(TouchToFillDelegateImplUnitTest, + TryToShowTouchToFillFailsIfNoCardsOnFile) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + autofill_client_.GetPersonalDataManager()->ClearCreditCards(); + + TryToShowTouchToFill(/*expected_success=*/false); +} + +TEST_F(TouchToFillDelegateImplUnitTest, + TryToShowTouchToFillFailsIfCardIsIncomplete) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + autofill_client_.GetPersonalDataManager()->ClearCreditCards(); + CreditCard cc_no_number = test::GetCreditCard(); + cc_no_number.SetNumber(u""); + autofill_client_.GetPersonalDataManager()->AddCreditCard(cc_no_number); + + TryToShowTouchToFill(/*expected_success=*/false); + + CreditCard cc_no_exp_date = test::GetCreditCard(); + cc_no_exp_date.SetExpirationMonth(0); + cc_no_exp_date.SetExpirationYear(0); + autofill_client_.GetPersonalDataManager()->AddCreditCard(cc_no_exp_date); + + TryToShowTouchToFill(/*expected_success=*/false); + + CreditCard cc_no_name = test::GetCreditCard(); + cc_no_name.SetRawInfo(CREDIT_CARD_NAME_FULL, u""); + autofill_client_.GetPersonalDataManager()->AddCreditCard(cc_no_name); + + TryToShowTouchToFill(/*expected_success=*/false); +} + +TEST_F(TouchToFillDelegateImplUnitTest, + TryToShowTouchToFillFailsIfCardIsExpired) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + autofill_client_.GetPersonalDataManager()->ClearCreditCards(); + autofill_client_.GetPersonalDataManager()->AddCreditCard( + test::GetExpiredCreditCard()); + + TryToShowTouchToFill(/*expected_success=*/false); +} + +TEST_F(TouchToFillDelegateImplUnitTest, + TryToShowTouchToFillFailsIfCardNumberIsInvalid) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + autofill_client_.GetPersonalDataManager()->ClearCreditCards(); + CreditCard cc_invalid_number = test::GetCreditCard(); + cc_invalid_number.SetNumber(u"invalid number"); + autofill_client_.GetPersonalDataManager()->AddCreditCard(cc_invalid_number); + + TryToShowTouchToFill(/*expected_success=*/false); + + // But succeeds for existing masked server card with incomplete number. + autofill_client_.GetPersonalDataManager()->AddCreditCard( + test::GetMaskedServerCard()); + + TryToShowTouchToFill(/*expected_success=*/true); +} + +TEST_F(TouchToFillDelegateImplUnitTest, + TryToShowTouchToFillFailsIfCanNotShowUi) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + EXPECT_CALL(*autofill_driver_, CanShowAutofillUi).WillOnce(Return(false)); + + TryToShowTouchToFill(/*expected_success=*/false); +} + +TEST_F(TouchToFillDelegateImplUnitTest, TryToShowTouchToFillFailsIfShowFails) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + EXPECT_CALL(autofill_client_, ShowTouchToFillCreditCard) + .WillOnce(Return(false)); + + TryToShowTouchToFill(/*expected_success=*/false); +} + +TEST_F(TouchToFillDelegateImplUnitTest, HideTouchToFillDoesNothingIfNotShown) { + ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); + + EXPECT_CALL(autofill_client_, HideTouchToFillCreditCard).Times(0); + touch_to_fill_delegate_->HideTouchToFill(); + EXPECT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); +} + +TEST_F(TouchToFillDelegateImplUnitTest, HideTouchToFillHidesIfShown) { + TryToShowTouchToFill(/*expected_success=*/true); + + EXPECT_CALL(autofill_client_, HideTouchToFillCreditCard).Times(1); + touch_to_fill_delegate_->HideTouchToFill(); + EXPECT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); +} + +TEST_F(TouchToFillDelegateImplUnitTest, ResetHidesTouchToFillIfShown) { + TryToShowTouchToFill(/*expected_success=*/true); + + EXPECT_CALL(autofill_client_, HideTouchToFillCreditCard).Times(1); + touch_to_fill_delegate_->Reset(); + EXPECT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); +} + +TEST_F(TouchToFillDelegateImplUnitTest, ResetAllowsShowingTouchToFillAgain) { + TryToShowTouchToFill(/*expected_success=*/true); + touch_to_fill_delegate_->HideTouchToFill(); + TryToShowTouchToFill(/*expected_success=*/false); + + touch_to_fill_delegate_->Reset(); + TryToShowTouchToFill(/*expected_success=*/true); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/address_combobox_model.cc b/chromium/components/autofill/core/browser/ui/address_combobox_model.cc index d52dc0e925c..58fda879804 100644 --- a/chromium/components/autofill/core/browser/ui/address_combobox_model.cc +++ b/chromium/components/autofill/core/browser/ui/address_combobox_model.cc @@ -20,7 +20,7 @@ namespace autofill { namespace { // There's one header entry to prompt the user to select an address, and a // separator. -int kNbHeaderEntries = 2; +constexpr size_t kNbHeaderEntries = 2; } // namespace AddressComboboxModel::AddressComboboxModel( @@ -36,7 +36,7 @@ AddressComboboxModel::AddressComboboxModel( AddressComboboxModel::~AddressComboboxModel() {} -int AddressComboboxModel::GetItemCount() const { +size_t AddressComboboxModel::GetItemCount() const { // When there are not addresses, a special entry is shown to prompt the user // to add addresses, but nothing else is shown, since there are no address to // select from, and no need for a separator. @@ -47,10 +47,9 @@ int AddressComboboxModel::GetItemCount() const { return addresses_.size() + kNbHeaderEntries; } -std::u16string AddressComboboxModel::GetItemAt(int index) const { - DCHECK_GE(index, 0); +std::u16string AddressComboboxModel::GetItemAt(size_t index) const { // A special entry is always added at index 0 and a separator at index 1. - DCHECK_LT(static_cast<size_t>(index), addresses_.size() + kNbHeaderEntries); + DCHECK_LT(index, addresses_.size() + kNbHeaderEntries); // Special entry when no profiles have been created yet. if (addresses_.empty()) @@ -59,15 +58,13 @@ std::u16string AddressComboboxModel::GetItemAt(int index) const { // Always show the "Select" entry at the top, default selection position. if (index == 0) return l10n_util::GetStringUTF16(IDS_AUTOFILL_SELECT); - - // Always show the "Select" entry at the top, default selection position. if (index == 1) return u"---"; return addresses_[index - kNbHeaderEntries].second; } -bool AddressComboboxModel::IsItemSeparatorAt(int index) const { +bool AddressComboboxModel::IsItemSeparatorAt(size_t index) const { // The only separator is between the "Select" entry at 0 and the first address // at index 2. So there must be at least one address for a separator to be // shown. @@ -75,37 +72,37 @@ bool AddressComboboxModel::IsItemSeparatorAt(int index) const { return index == 1; } -int AddressComboboxModel::GetDefaultIndex() const { +absl::optional<size_t> AddressComboboxModel::GetDefaultIndex() const { if (!default_selected_guid_.empty()) { - int address_index = GetIndexOfIdentifier(default_selected_guid_); - if (address_index != -1) - return address_index; + const auto index = GetIndexOfIdentifier(default_selected_guid_); + if (index.has_value()) + return index; } return ui::ComboboxModel::GetDefaultIndex(); } -int AddressComboboxModel::AddNewProfile(const AutofillProfile& profile) { +size_t AddressComboboxModel::AddNewProfile(const AutofillProfile& profile) { profiles_cache_.push_back(std::make_unique<AutofillProfile>(profile)); UpdateAddresses(); - DCHECK_GT(addresses_.size(), 0UL); + DCHECK(!addresses_.empty()); return addresses_.size() + kNbHeaderEntries - 1; } -std::string AddressComboboxModel::GetItemIdentifierAt(int index) { +std::string AddressComboboxModel::GetItemIdentifierAt(size_t index) { // The first two indices are special entries, with no addresses. if (index < kNbHeaderEntries) return std::string(); - DCHECK_LT(static_cast<size_t>(index), addresses_.size() + kNbHeaderEntries); + DCHECK_LT(index, addresses_.size() + kNbHeaderEntries); return addresses_[index - kNbHeaderEntries].first; } -int AddressComboboxModel::GetIndexOfIdentifier( +absl::optional<size_t> AddressComboboxModel::GetIndexOfIdentifier( const std::string& identifier) const { for (size_t i = 0; i < addresses_.size(); ++i) { if (addresses_[i].first == identifier) return i + kNbHeaderEntries; } - return -1; + return absl::nullopt; } void AddressComboboxModel::UpdateAddresses() { diff --git a/chromium/components/autofill/core/browser/ui/address_combobox_model.h b/chromium/components/autofill/core/browser/ui/address_combobox_model.h index 3a41b30c925..a7844d6f7ba 100644 --- a/chromium/components/autofill/core/browser/ui/address_combobox_model.h +++ b/chromium/components/autofill/core/browser/ui/address_combobox_model.h @@ -36,22 +36,23 @@ class AddressComboboxModel : public ui::ComboboxModel { ~AddressComboboxModel() override; // ui::ComboboxModel implementation: - int GetItemCount() const override; - std::u16string GetItemAt(int index) const override; - bool IsItemSeparatorAt(int index) const override; - int GetDefaultIndex() const override; + size_t GetItemCount() const override; + std::u16string GetItemAt(size_t index) const override; + bool IsItemSeparatorAt(size_t index) const override; + absl::optional<size_t> GetDefaultIndex() const override; // Adds |profile| to model and return its combobox index. The lifespan of // |profile| beyond this call is undefined so a copy must be made. - int AddNewProfile(const AutofillProfile& profile); + size_t AddNewProfile(const AutofillProfile& profile); // Returns the unique identifier of the profile at |index|, unless |index| // refers to a special entry, in which case an empty string is returned. - std::string GetItemIdentifierAt(int index); + std::string GetItemIdentifierAt(size_t index); - // Returns the combobox index of the item with the given id or -1 if it's not - // found. - int GetIndexOfIdentifier(const std::string& identifier) const; + // Returns the combobox index of the item with the given id or nullopt if it's + // not found. + absl::optional<size_t> GetIndexOfIdentifier( + const std::string& identifier) const; private: // Update |addresses_| based on |profiles_cache_| and notify observers. diff --git a/chromium/components/autofill/core/browser/ui/address_combobox_model_unittest.cc b/chromium/components/autofill/core/browser/ui/address_combobox_model_unittest.cc index 24e2c360482..812a7ae745f 100644 --- a/chromium/components/autofill/core/browser/ui/address_combobox_model_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/address_combobox_model_unittest.cc @@ -20,10 +20,10 @@ TEST(AddressComboboxModelTest, Empty) { test_personal_data_manager.SetAutofillProfileEnabled(true); AddressComboboxModel model(test_personal_data_manager, kAppLocale, ""); - EXPECT_EQ(1, model.GetItemCount()); + EXPECT_EQ(1u, model.GetItemCount()); EXPECT_FALSE(model.IsItemSeparatorAt(0)); EXPECT_TRUE(model.GetItemIdentifierAt(0).empty()); - EXPECT_EQ(-1, model.GetIndexOfIdentifier("Anything")); + EXPECT_FALSE(model.GetIndexOfIdentifier("Anything").has_value()); } TEST(AddressComboboxModelTest, OneAddress) { @@ -34,15 +34,15 @@ TEST(AddressComboboxModelTest, OneAddress) { AddressComboboxModel model(test_personal_data_manager, kAppLocale, profile1.guid()); - EXPECT_EQ(3, model.GetItemCount()); + EXPECT_EQ(3u, model.GetItemCount()); EXPECT_FALSE(model.IsItemSeparatorAt(0)); EXPECT_TRUE(model.IsItemSeparatorAt(1)); EXPECT_TRUE(model.GetItemIdentifierAt(0).empty()); EXPECT_TRUE(model.GetItemIdentifierAt(1).empty()); - EXPECT_EQ(-1, model.GetIndexOfIdentifier("Anything")); + EXPECT_FALSE(model.GetIndexOfIdentifier("Anything").has_value()); EXPECT_EQ(profile1.guid(), model.GetItemIdentifierAt(2)); - EXPECT_EQ(2, model.GetIndexOfIdentifier(profile1.guid())); - EXPECT_EQ(2, model.GetDefaultIndex()); + EXPECT_EQ(2u, model.GetIndexOfIdentifier(profile1.guid())); + EXPECT_EQ(2u, model.GetDefaultIndex()); } TEST(AddressComboboxModelTest, TwoAddresses) { @@ -58,17 +58,17 @@ TEST(AddressComboboxModelTest, TwoAddresses) { AddressComboboxModel model(test_personal_data_manager, kAppLocale, profile2.guid()); - EXPECT_EQ(4, model.GetItemCount()); + EXPECT_EQ(4u, model.GetItemCount()); EXPECT_FALSE(model.IsItemSeparatorAt(0)); EXPECT_TRUE(model.IsItemSeparatorAt(1)); EXPECT_TRUE(model.GetItemIdentifierAt(0).empty()); EXPECT_TRUE(model.GetItemIdentifierAt(1).empty()); - EXPECT_EQ(-1, model.GetIndexOfIdentifier("Anything")); + EXPECT_FALSE(model.GetIndexOfIdentifier("Anything").has_value()); EXPECT_EQ(profile1.guid(), model.GetItemIdentifierAt(2)); EXPECT_EQ(profile2.guid(), model.GetItemIdentifierAt(3)); - EXPECT_EQ(2, model.GetIndexOfIdentifier(profile1.guid())); - EXPECT_EQ(3, model.GetIndexOfIdentifier(profile2.guid())); - EXPECT_EQ(3, model.GetDefaultIndex()); + EXPECT_EQ(2u, model.GetIndexOfIdentifier(profile1.guid())); + EXPECT_EQ(3u, model.GetIndexOfIdentifier(profile2.guid())); + EXPECT_EQ(3u, model.GetDefaultIndex()); } TEST(AddressComboboxModelTest, AddAnAddress) { @@ -78,20 +78,19 @@ TEST(AddressComboboxModelTest, AddAnAddress) { test_personal_data_manager.AddProfile(profile1); AddressComboboxModel model(test_personal_data_manager, kAppLocale, ""); - EXPECT_EQ(3, model.GetItemCount()); + EXPECT_EQ(3u, model.GetItemCount()); EXPECT_EQ(profile1.guid(), model.GetItemIdentifierAt(2)); - EXPECT_EQ(2, model.GetIndexOfIdentifier(profile1.guid())); + EXPECT_EQ(2u, model.GetIndexOfIdentifier(profile1.guid())); AutofillProfile profile2(test::GetFullProfile2()); - int new_profile_index = model.AddNewProfile(profile2); - EXPECT_EQ(3, new_profile_index); - EXPECT_EQ(4, model.GetItemCount()); + EXPECT_EQ(3u, model.AddNewProfile(profile2)); + EXPECT_EQ(4u, model.GetItemCount()); EXPECT_EQ(profile2.guid(), model.GetItemIdentifierAt(3)); - EXPECT_EQ(3, model.GetIndexOfIdentifier(profile2.guid())); + EXPECT_EQ(3u, model.GetIndexOfIdentifier(profile2.guid())); // First profile shouldn't have changed, here the order is guaranteed. EXPECT_EQ(profile1.guid(), model.GetItemIdentifierAt(2)); - EXPECT_EQ(2, model.GetIndexOfIdentifier(profile1.guid())); + EXPECT_EQ(2u, model.GetIndexOfIdentifier(profile1.guid())); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc b/chromium/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc index cb8521200e3..40e81a148b1 100644 --- a/chromium/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc @@ -23,23 +23,18 @@ namespace autofill { namespace { std::vector<ServerFieldType> GetFieldTypes() { - return {NO_SERVER_DATA, - NAME_BILLING_FULL, - EMAIL_ADDRESS, - ADDRESS_BILLING_LINE1, - ADDRESS_BILLING_LINE2, - ADDRESS_BILLING_DEPENDENT_LOCALITY, - ADDRESS_BILLING_CITY, - ADDRESS_BILLING_STATE, - ADDRESS_BILLING_ZIP, - ADDRESS_BILLING_COUNTRY, - PHONE_BILLING_WHOLE_NUMBER}; + return {NO_SERVER_DATA, NAME_FULL, + EMAIL_ADDRESS, ADDRESS_HOME_LINE1, + ADDRESS_HOME_LINE2, ADDRESS_HOME_DEPENDENT_LOCALITY, + ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, + ADDRESS_HOME_ZIP, ADDRESS_HOME_COUNTRY, + PHONE_HOME_WHOLE_NUMBER}; } TEST(AddressContactFormLabelFormatterTest, GetLabelsWithMissingProfiles) { const std::vector<AutofillProfile*> profiles{}; - const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", NAME_BILLING_FULL, GetFieldTypes()); + const std::unique_ptr<LabelFormatter> formatter = + LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes()); EXPECT_TRUE(formatter->GetLabels().empty()); } @@ -80,8 +75,8 @@ TEST(AddressContactFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3, &profile4, &profile5, &profile6}; - const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", NAME_BILLING_FULL, GetFieldTypes()); + const std::unique_ptr<LabelFormatter> formatter = + LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -131,7 +126,7 @@ TEST(AddressContactFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3, &profile4, &profile5, &profile6}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", ADDRESS_BILLING_LINE1, GetFieldTypes()); + profiles, "en-US", ADDRESS_HOME_LINE1, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -182,7 +177,7 @@ TEST(AddressContactFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3, &profile4, &profile5, &profile6}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", ADDRESS_BILLING_CITY, GetFieldTypes()); + profiles, "en-US", ADDRESS_HOME_CITY, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -283,7 +278,7 @@ TEST(AddressContactFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3, &profile4, &profile5, &profile6}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", PHONE_BILLING_WHOLE_NUMBER, GetFieldTypes()); + profiles, "en-US", PHONE_HOME_WHOLE_NUMBER, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -313,8 +308,8 @@ TEST(AddressContactFormLabelFormatterTest, "21987650000"); const std::vector<AutofillProfile*> profiles{&profile1, &profile2}; - const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "pt-BR", NAME_BILLING_FULL, GetFieldTypes()); + const std::unique_ptr<LabelFormatter> formatter = + LabelFormatter::Create(profiles, "pt-BR", NAME_FULL, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -343,7 +338,7 @@ TEST(AddressContactFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "pt-BR", ADDRESS_BILLING_LINE1, GetFieldTypes()); + profiles, "pt-BR", ADDRESS_HOME_LINE1, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -371,7 +366,7 @@ TEST(AddressContactFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "pt-BR", ADDRESS_BILLING_ZIP, GetFieldTypes()); + profiles, "pt-BR", ADDRESS_HOME_ZIP, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -429,7 +424,7 @@ TEST(AddressContactFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "pt-BR", PHONE_BILLING_WHOLE_NUMBER, GetFieldTypes()); + profiles, "pt-BR", PHONE_HOME_WHOLE_NUMBER, GetFieldTypes()); EXPECT_THAT(formatter->GetLabels(), ElementsAre(ConstructLabelLine({u"Tarsila do Amaral", @@ -449,10 +444,9 @@ TEST(AddressContactFormLabelFormatterTest, "16175232338"); const std::vector<AutofillProfile*> profiles{&profile}; - const std::unique_ptr<LabelFormatter> formatter = - LabelFormatter::Create(profiles, "en-US", EMAIL_ADDRESS, - {NAME_BILLING_FULL, EMAIL_ADDRESS, - ADDRESS_BILLING_ZIP, PHONE_BILLING_WHOLE_NUMBER}); + const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( + profiles, "en-US", EMAIL_ADDRESS, + {NAME_FULL, EMAIL_ADDRESS, ADDRESS_HOME_ZIP, PHONE_HOME_WHOLE_NUMBER}); // Checks that only address fields in the form are shown in the label. EXPECT_THAT(formatter->GetLabels(), @@ -469,18 +463,17 @@ TEST(AddressContactFormLabelFormatterTest, std::vector<AutofillProfile*> profiles{&profile1}; std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", ADDRESS_BILLING_LINE1, - {ADDRESS_BILLING_ZIP, EMAIL_ADDRESS, PHONE_BILLING_WHOLE_NUMBER}); + profiles, "en-US", ADDRESS_HOME_LINE1, + {ADDRESS_HOME_ZIP, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER}); // Checks that the name is not in the label and that the phone number is for // a unique profile. EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"(617) 523-2338")); profiles = {&profile1, &profile1}; - formatter = - LabelFormatter::Create(profiles, "en-US", ADDRESS_BILLING_LINE1, - {ADDRESS_BILLING_LINE1, ADDRESS_BILLING_ZIP, - EMAIL_ADDRESS, PHONE_BILLING_WHOLE_NUMBER}); + formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1, + {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP, + EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER}); // Checks that the name is not in the label and that the phone number is for // multiple profiles with the same phone number and email address. @@ -494,10 +487,9 @@ TEST(AddressContactFormLabelFormatterTest, "16175232338"); profiles = {&profile1, &profile2}; - formatter = - LabelFormatter::Create(profiles, "en-US", ADDRESS_BILLING_LINE1, - {ADDRESS_BILLING_LINE1, ADDRESS_BILLING_ZIP, - EMAIL_ADDRESS, PHONE_BILLING_WHOLE_NUMBER}); + formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1, + {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP, + EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER}); // Checks that the name is not in the label and that the email address is // shown because the profiles' email addresses are different. EXPECT_THAT(formatter->GetLabels(), diff --git a/chromium/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc b/chromium/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc index a4d3b82341a..6474e331305 100644 --- a/chromium/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc @@ -25,19 +25,19 @@ namespace { std::vector<ServerFieldType> GetFieldTypes() { return {NAME_FULL, EMAIL_ADDRESS, - ADDRESS_BILLING_LINE1, - ADDRESS_BILLING_LINE2, - ADDRESS_BILLING_CITY, - ADDRESS_BILLING_STATE, - ADDRESS_BILLING_DEPENDENT_LOCALITY, - ADDRESS_BILLING_ZIP, - ADDRESS_BILLING_COUNTRY}; + ADDRESS_HOME_LINE1, + ADDRESS_HOME_LINE2, + ADDRESS_HOME_CITY, + ADDRESS_HOME_STATE, + ADDRESS_HOME_DEPENDENT_LOCALITY, + ADDRESS_HOME_ZIP, + ADDRESS_HOME_COUNTRY}; } TEST(AddressEmailFormLabelFormatterTest, GetLabelsWithMissingProfiles) { const std::vector<AutofillProfile*> profiles{}; - const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", NAME_BILLING_FULL, GetFieldTypes()); + const std::unique_ptr<LabelFormatter> formatter = + LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes()); EXPECT_TRUE(formatter->GetLabels().empty()); } @@ -66,8 +66,8 @@ TEST(AddressEmailFormLabelFormatterTest, GetLabelsForUSProfilesAndFocusedName) { const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3, &profile4}; - const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", NAME_BILLING_FULL, GetFieldTypes()); + const std::unique_ptr<LabelFormatter> formatter = + LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -102,7 +102,7 @@ TEST(AddressEmailFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3, &profile4}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", ADDRESS_BILLING_LINE1, GetFieldTypes()); + profiles, "en-US", ADDRESS_HOME_LINE1, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -137,7 +137,7 @@ TEST(AddressEmailFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3, &profile4}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", ADDRESS_BILLING_ZIP, GetFieldTypes()); + profiles, "en-US", ADDRESS_HOME_ZIP, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -195,8 +195,8 @@ TEST(AddressEmailFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedName) { "21987650000"); const std::vector<AutofillProfile*> profiles{&profile1, &profile2}; - const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "pt-BR", NAME_BILLING_FULL, GetFieldTypes()); + const std::unique_ptr<LabelFormatter> formatter = + LabelFormatter::Create(profiles, "pt-BR", NAME_FULL, GetFieldTypes()); EXPECT_THAT(formatter->GetLabels(), ElementsAre(ConstructLabelLine({u"Av. Pedro Álvares Cabral, 1301", @@ -223,7 +223,7 @@ TEST(AddressEmailFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "pt-BR", ADDRESS_BILLING_LINE1, GetFieldTypes()); + profiles, "pt-BR", ADDRESS_HOME_LINE1, GetFieldTypes()); EXPECT_THAT( formatter->GetLabels(), @@ -250,7 +250,7 @@ TEST(AddressEmailFormLabelFormatterTest, const std::vector<AutofillProfile*> profiles{&profile1, &profile2}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "pt-BR", ADDRESS_BILLING_DEPENDENT_LOCALITY, GetFieldTypes()); + profiles, "pt-BR", ADDRESS_HOME_DEPENDENT_LOCALITY, GetFieldTypes()); EXPECT_THAT(formatter->GetLabels(), ElementsAre(ConstructLabelLine({u"Av. Pedro Álvares Cabral, 1301", @@ -296,10 +296,9 @@ TEST(AddressEmailFormLabelFormatterTest, "US", "16177302000"); const std::vector<AutofillProfile*> profiles{&profile}; - const std::unique_ptr<LabelFormatter> formatter = - LabelFormatter::Create(profiles, "en-US", EMAIL_ADDRESS, - {NAME_BILLING_FULL, EMAIL_ADDRESS, - ADDRESS_BILLING_CITY, ADDRESS_BILLING_STATE}); + const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( + profiles, "en-US", EMAIL_ADDRESS, + {NAME_FULL, EMAIL_ADDRESS, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE}); // Checks that only address fields in the form are shown in the label. EXPECT_THAT( @@ -316,8 +315,8 @@ TEST(AddressEmailFormLabelFormatterTest, GetLabelsForFormWithoutName) { const std::vector<AutofillProfile*> profiles{&profile}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", ADDRESS_BILLING_LINE1, - {ADDRESS_BILLING_LINE1, ADDRESS_BILLING_ZIP, EMAIL_ADDRESS}); + profiles, "en-US", ADDRESS_HOME_LINE1, + {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP, EMAIL_ADDRESS}); // Checks that the name does not appear in the labels. EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"sarah.revere@aol.com")); diff --git a/chromium/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc b/chromium/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc index fd0569e4c3a..9936f8eeb1f 100644 --- a/chromium/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc @@ -310,12 +310,12 @@ TEST(AddressPhoneFormLabelFormatterTest, GetLabelsForFormWithoutName) { const std::vector<AutofillProfile*> profiles{&profile}; const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create( - profiles, "en-US", ADDRESS_BILLING_LINE1, - {ADDRESS_BILLING_LINE1, ADDRESS_BILLING_ZIP, PHONE_HOME_WHOLE_NUMBER}); + profiles, "en-US", ADDRESS_HOME_LINE1, + {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP, PHONE_HOME_WHOLE_NUMBER}); // Checks that the name does not appear in the labels. EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"(617) 523-2338")); } } // namespace -} // namespace autofill
\ No newline at end of file +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/autofill_popup_delegate.h b/chromium/components/autofill/core/browser/ui/autofill_popup_delegate.h index b3410873b5c..d884117b04f 100644 --- a/chromium/components/autofill/core/browser/ui/autofill_popup_delegate.h +++ b/chromium/components/autofill/core/browser/ui/autofill_popup_delegate.h @@ -89,6 +89,8 @@ class AutofillPopupDelegate { // should not outlive it. virtual void RegisterDeletionCallback( base::OnceClosure deletion_callback) = 0; + + virtual ~AutofillPopupDelegate() = default; }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/country_combobox_model.cc b/chromium/components/autofill/core/browser/ui/country_combobox_model.cc index 2abb96fe50f..fbd39e68d21 100644 --- a/chromium/components/autofill/core/browser/ui/country_combobox_model.cc +++ b/chromium/components/autofill/core/browser/ui/country_combobox_model.cc @@ -75,11 +75,11 @@ void CountryComboboxModel::SetCountries( std::back_inserter(countries_)); } -int CountryComboboxModel::GetItemCount() const { +size_t CountryComboboxModel::GetItemCount() const { return countries_.size(); } -std::u16string CountryComboboxModel::GetItemAt(int index) const { +std::u16string CountryComboboxModel::GetItemAt(size_t index) const { AutofillCountry* country = countries_[index].get(); if (country) return countries_[index]->name(); @@ -89,12 +89,12 @@ std::u16string CountryComboboxModel::GetItemAt(int index) const { return u"---"; } -bool CountryComboboxModel::IsItemSeparatorAt(int index) const { +bool CountryComboboxModel::IsItemSeparatorAt(size_t index) const { return !countries_[index]; } std::string CountryComboboxModel::GetDefaultCountryCode() const { - return countries_[GetDefaultIndex()]->country_code(); + return countries_[GetDefaultIndex().value()]->country_code(); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/country_combobox_model.h b/chromium/components/autofill/core/browser/ui/country_combobox_model.h index 873d4838aa2..a9260126977 100644 --- a/chromium/components/autofill/core/browser/ui/country_combobox_model.h +++ b/chromium/components/autofill/core/browser/ui/country_combobox_model.h @@ -38,9 +38,9 @@ class CountryComboboxModel : public ui::ComboboxModel { const std::string& app_locale); // ui::ComboboxModel implementation: - int GetItemCount() const override; - std::u16string GetItemAt(int index) const override; - bool IsItemSeparatorAt(int index) const override; + size_t GetItemCount() const override; + std::u16string GetItemAt(size_t index) const override; + bool IsItemSeparatorAt(size_t index) const override; // The list of countries always has the default country at the top as well as // within the sorted vector. diff --git a/chromium/components/autofill/core/browser/ui/country_combobox_model_unittest.cc b/chromium/components/autofill/core/browser/ui/country_combobox_model_unittest.cc index 1e5c5318537..f00d722ba87 100644 --- a/chromium/components/autofill/core/browser/ui/country_combobox_model_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/country_combobox_model_unittest.cc @@ -50,7 +50,7 @@ TEST_F(CountryComboboxModelTest, DefaultCountryCode) { TEST_F(CountryComboboxModelTest, AllCountriesHaveComponents) { ::i18n::addressinput::Localization localization; std::string unused; - for (int i = 0; i < model()->GetItemCount(); ++i) { + for (size_t i = 0; i < model()->GetItemCount(); ++i) { if (model()->IsItemSeparatorAt(i)) continue; diff --git a/chromium/components/autofill/core/browser/ui/label_formatter.cc b/chromium/components/autofill/core/browser/ui/label_formatter.cc index 9aa99315d2b..0e0eae78dbc 100644 --- a/chromium/components/autofill/core/browser/ui/label_formatter.cc +++ b/chromium/components/autofill/core/browser/ui/label_formatter.cc @@ -40,7 +40,7 @@ LabelFormatter::LabelFormatter(const std::vector<AutofillProfile*>& profiles, app_locale_(app_locale), focused_field_type_(focused_field_type), groups_(groups) { - const FieldTypeGroup focused_group = GetFocusedNonBillingGroup(); + const FieldTypeGroup focused_group = AutofillType(focused_field_type).group(); DenseSet<FieldTypeGroup> groups_for_labels{ FieldTypeGroup::kName, FieldTypeGroup::kAddressHome, FieldTypeGroup::kEmail, FieldTypeGroup::kPhoneHome}; @@ -60,7 +60,7 @@ LabelFormatter::LabelFormatter(const std::vector<AutofillProfile*>& profiles, return groups_for_labels.find( AutofillType(AutofillType(type).GetStorableType()).group()) != groups_for_labels.end() && - type != ADDRESS_HOME_COUNTRY && type != ADDRESS_BILLING_COUNTRY; + type != ADDRESS_HOME_COUNTRY; }; std::copy_if(field_types.begin(), field_types.end(), @@ -73,16 +73,12 @@ LabelFormatter::~LabelFormatter() = default; std::vector<std::u16string> LabelFormatter::GetLabels() const { std::vector<std::u16string> labels; for (const AutofillProfile* profile : profiles_) { - labels.push_back(GetLabelForProfile(*profile, GetFocusedNonBillingGroup())); + labels.push_back(GetLabelForProfile( + *profile, AutofillType(focused_field_type_).group())); } return labels; } -FieldTypeGroup LabelFormatter::GetFocusedNonBillingGroup() const { - return AutofillType(AutofillType(focused_field_type_).GetStorableType()) - .group(); -} - // static std::unique_ptr<LabelFormatter> LabelFormatter::Create( const std::vector<AutofillProfile*>& profiles, diff --git a/chromium/components/autofill/core/browser/ui/label_formatter.h b/chromium/components/autofill/core/browser/ui/label_formatter.h index cf66bfb3a66..8bb5a3b9cb9 100644 --- a/chromium/components/autofill/core/browser/ui/label_formatter.h +++ b/chromium/components/autofill/core/browser/ui/label_formatter.h @@ -49,12 +49,6 @@ class LabelFormatter { const AutofillProfile& profile, FieldTypeGroup focused_group) const = 0; - // Returns the FieldTypeGroup with which |focused_field_type_| is associated. - // Billing field types are mapped to their corresponding home address field - // types. For example, if focused_field_type_ is ADDRESS_BILLING_ZIP, then - // the resulting FieldTypeGroup is kAddressHome instead of kAddressBilling. - FieldTypeGroup GetFocusedNonBillingGroup() const; - const std::string& app_locale() const { return app_locale_; } ServerFieldType focused_field_type() const { return focused_field_type_; } diff --git a/chromium/components/autofill/core/browser/ui/label_formatter_utils.cc b/chromium/components/autofill/core/browser/ui/label_formatter_utils.cc index f7d0ced2e08..506df6e01f8 100644 --- a/chromium/components/autofill/core/browser/ui/label_formatter_utils.cc +++ b/chromium/components/autofill/core/browser/ui/label_formatter_utils.cc @@ -86,17 +86,11 @@ std::u16string ConstructMobileLabelLine( bool IsNonStreetAddressPart(ServerFieldType type) { switch (type) { case ADDRESS_HOME_CITY: - case ADDRESS_BILLING_CITY: case ADDRESS_HOME_ZIP: - case ADDRESS_BILLING_ZIP: case ADDRESS_HOME_STATE: - case ADDRESS_BILLING_STATE: case ADDRESS_HOME_COUNTRY: - case ADDRESS_BILLING_COUNTRY: case ADDRESS_HOME_SORTING_CODE: - case ADDRESS_BILLING_SORTING_CODE: case ADDRESS_HOME_DEPENDENT_LOCALITY: - case ADDRESS_BILLING_DEPENDENT_LOCALITY: return true; default: return false; @@ -108,13 +102,8 @@ bool IsStreetAddressPart(ServerFieldType type) { case ADDRESS_HOME_LINE1: case ADDRESS_HOME_LINE2: case ADDRESS_HOME_APT_NUM: - case ADDRESS_BILLING_LINE1: - case ADDRESS_BILLING_LINE2: - case ADDRESS_BILLING_APT_NUM: case ADDRESS_HOME_STREET_ADDRESS: - case ADDRESS_BILLING_STREET_ADDRESS: case ADDRESS_HOME_LINE3: - case ADDRESS_BILLING_LINE3: return true; default: return false; diff --git a/chromium/components/autofill/core/browser/ui/mobile_label_formatter.cc b/chromium/components/autofill/core/browser/ui/mobile_label_formatter.cc index 09f00ce40b0..a37b0232467 100644 --- a/chromium/components/autofill/core/browser/ui/mobile_label_formatter.cc +++ b/chromium/components/autofill/core/browser/ui/mobile_label_formatter.cc @@ -35,7 +35,7 @@ MobileLabelFormatter::MobileLabelFormatter( focused_field_type, groups, field_types) { - const FieldTypeGroup focused_group = GetFocusedNonBillingGroup(); + const FieldTypeGroup focused_group = AutofillType(focused_field_type).group(); could_show_email_ = HasUnfocusedEmailField(focused_group, groups) && !HaveSameEmailAddresses(profiles, app_locale); diff --git a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc index 0cd137c9748..a14f7f01f56 100644 --- a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc @@ -279,55 +279,13 @@ TEST_F(CardUnmaskPromptControllerImplTest, TEST_F(CardUnmaskPromptControllerImplTest, DisplayCardInformation) { ShowPrompt(); #if BUILDFLAG(IS_IOS) - EXPECT_TRUE(base::UTF16ToUTF8(controller_->GetInstructionsMessage()) - .find("Mastercard " + test::ObfuscatedCardDigitsAsUTF8( - "2109")) != std::string::npos); -#else - EXPECT_TRUE(base::UTF16ToUTF8(controller_->GetWindowTitle()) - .find("Mastercard " + test::ObfuscatedCardDigitsAsUTF8( - "2109")) != std::string::npos); -#endif -} - -// Ensures to fallback to network name in the instruction message on iOS and in -// the title on other platforms when the nickname is invalid. -TEST_F(CardUnmaskPromptControllerImplTest, Nickname_NicknameInvalid) { - SetCreditCardForTesting(test::GetMaskedServerCardWithInvalidNickname()); - ShowPrompt(); -#if BUILDFLAG(IS_IOS) - EXPECT_TRUE( - base::UTF16ToUTF8(controller_->GetInstructionsMessage()).find("Visa") != - std::string::npos); - EXPECT_FALSE(base::UTF16ToUTF8(controller_->GetInstructionsMessage()) - .find("Invalid nickname which is too long") != - std::string::npos); -#else - EXPECT_TRUE(base::UTF16ToUTF8(controller_->GetWindowTitle()).find("Visa") != + EXPECT_TRUE(controller_->GetInstructionsMessage().find( + card_.CardIdentifierStringForAutofillDisplay()) != std::string::npos); - EXPECT_FALSE(base::UTF16ToUTF8(controller_->GetWindowTitle()) - .find("Invalid nickname which is too long") != - std::string::npos); -#endif -} - -// Ensures the nickname is displayed (instead of network) in the instruction -// message on iOS and in the title on other platforms when the nickname is -// valid. -TEST_F(CardUnmaskPromptControllerImplTest, Nickname_NicknameValid) { - SetCreditCardForTesting(test::GetMaskedServerCardWithNickname()); - ShowPrompt(); -#if BUILDFLAG(IS_IOS) - EXPECT_FALSE( - base::UTF16ToUTF8(controller_->GetInstructionsMessage()).find("Visa") != - std::string::npos); - EXPECT_TRUE(base::UTF16ToUTF8(controller_->GetInstructionsMessage()) - .find("Test nickname") != std::string::npos); #else - EXPECT_FALSE(base::UTF16ToUTF8(controller_->GetWindowTitle()).find("Visa") != - std::string::npos); - EXPECT_TRUE( - base::UTF16ToUTF8(controller_->GetWindowTitle()).find("Test nickname") != - std::string::npos); + EXPECT_TRUE(controller_->GetWindowTitle().find( + card_.CardIdentifierStringForAutofillDisplay()) != + std::string::npos); #endif } diff --git a/chromium/components/autofill/core/browser/ui/popup_item_ids.h b/chromium/components/autofill/core/browser/ui/popup_item_ids.h index 10d1a3e3d82..8373853fd8e 100644 --- a/chromium/components/autofill/core/browser/ui/popup_item_ids.h +++ b/chromium/components/autofill/core/browser/ui/popup_item_ids.h @@ -37,7 +37,9 @@ enum PopupItemId { POPUP_ITEM_ID_VIRTUAL_CREDIT_CARD_ENTRY = -27, POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL = -28, POPUP_ITEM_ID_MERCHANT_PROMO_CODE_ENTRY = -29, - POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS = -30 + POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS = -30, + POPUP_ITEM_ID_WEBAUTHN_SIGN_IN_WITH_ANOTHER_DEVICE = -31, + POPUP_ITEM_ID_IBAN_ENTRY = -32, }; // List of `PopupItemId` that trigger filling a value into an input element diff --git a/chromium/components/autofill/core/browser/ui/popup_types.h b/chromium/components/autofill/core/browser/ui/popup_types.h index 048ae1936fc..9d817b25994 100644 --- a/chromium/components/autofill/core/browser/ui/popup_types.h +++ b/chromium/components/autofill/core/browser/ui/popup_types.h @@ -62,7 +62,11 @@ enum class PopupHidingReason { kMouseLocked = 16, // The password generation popup would overlap and hide autofill popup. kOverlappingWithPasswordGenerationPopup = 17, - kMaxValue = kOverlappingWithPasswordGenerationPopup + // The Touch To Fill surface is shown instead of autofill suggestions. + kOverlappingWithTouchToFillSurface = 18, + // The context menu is shown instead of the autofill suggestions. + kOverlappingWithAutofillContextMenu = 19, + kMaxValue = kOverlappingWithAutofillContextMenu }; } // namespace autofill 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 42626aff427..df17223dd52 100644 --- a/chromium/components/autofill/core/browser/ui/region_combobox_model.cc +++ b/chromium/components/autofill/core/browser/ui/region_combobox_model.cc @@ -37,18 +37,15 @@ void RegionComboboxModel::LoadRegionData(const std::string& country_code, weak_factory_.GetWeakPtr())); } -int RegionComboboxModel::GetItemCount() const { +size_t RegionComboboxModel::GetItemCount() const { // The combobox view needs to always have at least one item. If the regions // have not been completely loaded yet, we display a single "loading" item. - if (regions_.size() == 0) - return 1; - return regions_.size(); + return std::max(regions_.size(), size_t{1}); } -std::u16string RegionComboboxModel::GetItemAt(int index) const { - DCHECK_GE(index, 0); +std::u16string RegionComboboxModel::GetItemAt(size_t index) const { // This might happen because of the asynchronous nature of the data. - if (static_cast<size_t>(index) >= regions_.size()) + if (index >= regions_.size()) return l10n_util::GetStringUTF16(IDS_AUTOFILL_LOADING_REGIONS); if (!regions_[index].second.empty()) @@ -59,12 +56,8 @@ std::u16string RegionComboboxModel::GetItemAt(int index) const { return u"---"; } -bool RegionComboboxModel::IsItemSeparatorAt(int index) const { - // This might happen because of the asynchronous nature of the data. - DCHECK_GE(index, 0); - if (static_cast<size_t>(index) >= regions_.size()) - return false; - return regions_[index].first.empty(); +bool RegionComboboxModel::IsItemSeparatorAt(size_t index) const { + return index < regions_.size() && regions_[index].first.empty(); } void RegionComboboxModel::OnRegionDataLoaded( diff --git a/chromium/components/autofill/core/browser/ui/region_combobox_model.h b/chromium/components/autofill/core/browser/ui/region_combobox_model.h index 52cffd5d006..ead471de804 100644 --- a/chromium/components/autofill/core/browser/ui/region_combobox_model.h +++ b/chromium/components/autofill/core/browser/ui/region_combobox_model.h @@ -51,9 +51,9 @@ class RegionComboboxModel : public ui::ComboboxModel { } // ui::ComboboxModel implementation: - int GetItemCount() const override; - std::u16string GetItemAt(int index) const override; - bool IsItemSeparatorAt(int index) const override; + size_t GetItemCount() const override; + std::u16string GetItemAt(size_t index) const override; + bool IsItemSeparatorAt(size_t index) const override; private: // Callback for the RegionDataLoader. diff --git a/chromium/components/autofill/core/browser/ui/region_combobox_model_unittest.cc b/chromium/components/autofill/core/browser/ui/region_combobox_model_unittest.cc index 3c018e2e55c..6984b134ce8 100644 --- a/chromium/components/autofill/core/browser/ui/region_combobox_model_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/region_combobox_model_unittest.cc @@ -38,7 +38,7 @@ TEST(RegionComboboxModelTest, QuebecOntarioRegions) { test_region_data_loader.SendAsynchronousData(regions); - EXPECT_EQ(3, model.GetItemCount()); + EXPECT_EQ(3u, model.GetItemCount()); EXPECT_EQ(u"---", model.GetItemAt(0)); EXPECT_EQ(kQuebecName16, model.GetItemAt(1)); EXPECT_EQ(kOntarioName16, model.GetItemAt(2)); @@ -54,7 +54,7 @@ TEST(RegionComboboxModelTest, FailingSource) { std::vector<std::pair<std::string, std::string>>()); // There's always 1 item, even in failure cases. - EXPECT_EQ(1, model.GetItemCount()); + EXPECT_EQ(1u, model.GetItemCount()); EXPECT_TRUE(model.failed_to_load_data()); } diff --git a/chromium/components/autofill/core/browser/ui/suggestion.h b/chromium/components/autofill/core/browser/ui/suggestion.h index ad9eb7894ae..1368f10a3ad 100644 --- a/chromium/components/autofill/core/browser/ui/suggestion.h +++ b/chromium/components/autofill/core/browser/ui/suggestion.h @@ -75,9 +75,23 @@ struct Suggestion { template <typename T> T GetPayload() const { +#if DCHECK_IS_ON() + DCHECK(Invariant()); +#endif return absl::holds_alternative<T>(payload) ? absl::get<T>(payload) : T{}; } +#if DCHECK_IS_ON() + bool Invariant() const { + switch (frontend_id) { + case PopupItemId::POPUP_ITEM_ID_SEE_PROMO_CODE_DETAILS: + return absl::holds_alternative<GURL>(payload); + default: + return absl::holds_alternative<std::string>(payload); + } + } +#endif + // Payload generated by the backend layer. This payload is either a GUID that // identifies the exact autofill profile that generated this suggestion, or a // GURL that the suggestion should navigate to upon being accepted. @@ -117,6 +131,10 @@ struct Suggestion { // The url for the custom icon. This is used by android to fetch the image as // android does not support gfx::Image directly. GURL custom_icon_url; + + // On Android, the icon can be at the start of the suggestion before the label + // or at the end of the label. + bool is_icon_at_start = false; #endif // BUILDFLAG(IS_ANDROID) // TODO(crbug.com/1019660): Identify icons with enum instead of strings. diff --git a/chromium/components/autofill/core/browser/ui/touch_to_fill_delegate.h b/chromium/components/autofill/core/browser/ui/touch_to_fill_delegate.h new file mode 100644 index 00000000000..0f211d9d301 --- /dev/null +++ b/chromium/components/autofill/core/browser/ui/touch_to_fill_delegate.h @@ -0,0 +1,19 @@ +// Copyright 2022 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_UI_TOUCH_TO_FILL_DELEGATE_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_TOUCH_TO_FILL_DELEGATE_H_ + +namespace autofill { + +// An interface for interaction with the corresponding UI controller. +class TouchToFillDelegate { + public: + // TODO(crbug.com/1247698): Define the API. + virtual ~TouchToFillDelegate() = default; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_TOUCH_TO_FILL_DELEGATE_H_ diff --git a/chromium/components/autofill/core/browser/validation.cc b/chromium/components/autofill/core/browser/validation.cc index 53165336edc..afe2c981ab3 100644 --- a/chromium/components/autofill/core/browser/validation.cc +++ b/chromium/components/autofill/core/browser/validation.cc @@ -17,12 +17,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" @@ -154,7 +154,7 @@ bool IsValidEmailAddress(const std::u16string& text) { // E-Mail pattern as defined by the WhatWG. (4.10.7.1.5 E-Mail state) static constexpr char16_t kEmailPattern[] = u"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"; - return MatchesPattern(text, kEmailPattern); + return MatchesRegex<kEmailPattern>(text); } bool IsValidState(const std::u16string& text) { @@ -169,7 +169,7 @@ bool IsPossiblePhoneNumber(const std::u16string& text, bool IsValidZip(const std::u16string& text) { static constexpr char16_t kZipPattern[] = u"^\\d{5}(-\\d{4})?$"; - return MatchesPattern(text, kZipPattern); + return MatchesRegex<kZipPattern>(text); } bool IsSSN(const std::u16string& text) { @@ -290,16 +290,17 @@ bool IsValidForType(const std::u16string& value, case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: { - const std::u16string pattern = type == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR - ? u"^[0-9]{1,2}[-/|]?[0-9]{2}$" - : u"^[0-9]{1,2}[-/|]?[0-9]{4}$"; + static constexpr char16_t kDateYY[] = u"^[0-9]{1,2}[-/|]?[0-9]{2}$"; + static constexpr char16_t kDateYYYY[] = u"^[0-9]{1,2}[-/|]?[0-9]{4}$"; CreditCard temp; temp.SetExpirationDateFromString(value); // Expiration date was in an invalid format. if (temp.expiration_month() == 0 || temp.expiration_year() == 0 || - !MatchesPattern(value, pattern)) { + (type == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR + ? !MatchesRegex<kDateYY>(value) + : !MatchesRegex<kDateYYYY>(value))) { if (error_message) { *error_message = l10n_util::GetStringUTF16( IDS_PAYMENTS_CARD_EXPIRATION_INVALID_VALIDATION_MESSAGE); @@ -343,20 +344,20 @@ size_t GetCvcLengthForCardNetwork(const base::StringPiece card_network) { } bool IsUPIVirtualPaymentAddress(const std::u16string& value) { - return MatchesPattern(value, kUPIVirtualPaymentAddressRe); + return MatchesRegex<kUPIVirtualPaymentAddressRe>(value); } bool IsInternationalBankAccountNumber(const std::u16string& value) { std::u16string no_spaces; base::RemoveChars(value, u" ", &no_spaces); - return MatchesPattern(no_spaces, kInternationalBankAccountNumberRe); + return MatchesRegex<kInternationalBankAccountNumberValueRe>(no_spaces); } bool IsPlausibleCreditCardCVCNumber(const std::u16string& value) { - return MatchesPattern(value, kCreditCardCVCPattern); + return MatchesRegex<kCreditCardCVCPattern>(value); } bool IsPlausible4DigitExpirationYear(const std::u16string& value) { - return MatchesPattern(value, kCreditCard4DigitExpYearPattern); + return MatchesRegex<kCreditCard4DigitExpYearPattern>(value); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/validation_unittest.cc b/chromium/components/autofill/core/browser/validation_unittest.cc index 378094b085e..7628002ce6a 100644 --- a/chromium/components/autofill/core/browser/validation_unittest.cc +++ b/chromium/components/autofill/core/browser/validation_unittest.cc @@ -98,11 +98,11 @@ const char16_t* const kPlausibleCreditCardCVCNumbers[] = {u"1234", u"2099", TEST(AutofillValidation, IsValidCreditCardNumber) { for (const char16_t* valid_number : kValidNumbers) { - SCOPED_TRACE(valid_number); + SCOPED_TRACE(base::UTF16ToUTF8(valid_number)); EXPECT_TRUE(IsValidCreditCardNumber(valid_number)); } for (const char16_t* invalid_number : kInvalidNumbers) { - SCOPED_TRACE(invalid_number); + SCOPED_TRACE(base::UTF16ToUTF8(invalid_number)); EXPECT_FALSE(IsValidCreditCardNumber(invalid_number)); } } @@ -111,25 +111,26 @@ TEST(AutofillValidation, IsValidCreditCardNumber) { TEST(AutofillValidation, IsPlausibleCreditCardExparationYear) { for (const char16_t* plausible_year : kPlausibleCreditCardExpirationYears) { EXPECT_TRUE(IsPlausible4DigitExpirationYear(plausible_year)) - << plausible_year; + << base::UTF16ToUTF8(plausible_year); } for (const char16_t* unplausible_year : kUnplausibleCreditCardExpirationYears) { EXPECT_FALSE(IsPlausible4DigitExpirationYear(unplausible_year)) - << unplausible_year; + << base::UTF16ToUTF8(unplausible_year); } } // Test the plausibility of supplied CVC numbers. TEST(AutofillValidation, IsPlausibleCreditCardCVCNumber) { for (const char16_t* plausible_cvc : kPlausibleCreditCardCVCNumbers) { - EXPECT_TRUE(IsPlausibleCreditCardCVCNumber(plausible_cvc)) << plausible_cvc; + EXPECT_TRUE(IsPlausibleCreditCardCVCNumber(plausible_cvc)) + << base::UTF16ToUTF8(plausible_cvc); } for (const char16_t* unplausible_cvc : kUnplausibleCreditCardCVCNumbers) { EXPECT_FALSE(IsPlausibleCreditCardCVCNumber(unplausible_cvc)) - << unplausible_cvc; + << base::UTF16ToUTF8(unplausible_cvc); } } @@ -151,13 +152,13 @@ TEST(AutofillValidation, IsValidCreditCardIntExpirationDate) { TEST(AutofillValidation, IsValidCreditCardSecurityCode) { for (const auto data : kValidSecurityCodeCardTypePairs) { - SCOPED_TRACE(data.security_code); + SCOPED_TRACE(base::UTF16ToUTF8(data.security_code)); SCOPED_TRACE(data.card_network); EXPECT_TRUE( IsValidCreditCardSecurityCode(data.security_code, data.card_network)); } for (const auto data : kInvalidSecurityCodeCardTypePairs) { - SCOPED_TRACE(data.security_code); + SCOPED_TRACE(base::UTF16ToUTF8(data.security_code)); SCOPED_TRACE(data.card_network); EXPECT_FALSE( IsValidCreditCardSecurityCode(data.security_code, data.card_network)); @@ -166,11 +167,11 @@ TEST(AutofillValidation, IsValidCreditCardSecurityCode) { TEST(AutofillValidation, IsValidEmailAddress) { for (const char16_t* valid_email : kValidEmailAddress) { - SCOPED_TRACE(valid_email); + SCOPED_TRACE(base::UTF16ToUTF8(valid_email)); EXPECT_TRUE(IsValidEmailAddress(valid_email)); } for (const char16_t* invalid_email : kInvalidEmailAddress) { - SCOPED_TRACE(invalid_email); + SCOPED_TRACE(base::UTF16ToUTF8(invalid_email)); EXPECT_FALSE(IsValidEmailAddress(invalid_email)); } } @@ -200,8 +201,8 @@ TEST_P(AutofillTypeValidationTest, IsValidForType) { EXPECT_EQ( GetParam().expected_valid, IsValidForType(GetParam().value, GetParam().field_type, &error_message)) - << "Failed to validate " << GetParam().value << " (type " - << GetParam().field_type << ")"; + << "Failed to validate " << base::UTF16ToUTF8(GetParam().value) + << " (type " << GetParam().field_type << ")"; if (!GetParam().expected_valid) { EXPECT_EQ(l10n_util::GetStringUTF16(GetParam().expected_error_id), error_message); @@ -360,7 +361,7 @@ TEST_P(AutofillCCNumberValidationTest, IsValidCreditCardNumber) { IsValidCreditCardNumberForBasicCardNetworks( GetParam().value, GetParam().supported_basic_card_networks, &error_message)) - << "Failed to validate CC number " << GetParam().value; + << "Failed to validate CC number " << base::UTF16ToUTF8(GetParam().value); if (!GetParam().expected_valid) { EXPECT_EQ(l10n_util::GetStringUTF16(GetParam().expected_error_id), error_message); 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 7912eff9149..e074e8c79b9 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 @@ -33,8 +33,8 @@ #include "components/sync/protocol/autofill_specifics.pb.h" #include "components/sync/protocol/entity_metadata.pb.h" #include "components/sync/protocol/model_type_state.pb.h" -#include "components/sync/test/model/mock_model_type_change_processor.h" -#include "components/sync/test/model/test_matchers.h" +#include "components/sync/test/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" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/components/autofill/core/browser/webdata/autofill_change.h b/chromium/components/autofill/core/browser/webdata/autofill_change.h index 9da26e5da7c..a7dbcca157a 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_change.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_change.h @@ -16,6 +16,7 @@ namespace autofill { class CreditCard; +class IBAN; // For classic Autofill form fields, the KeyType is AutofillKey. // Autofill++ types such as AutofillProfile and CreditCard simply use a string. @@ -80,6 +81,7 @@ class AutofillDataModelChange : public GenericAutofillChange<std::string> { typedef AutofillDataModelChange<AutofillProfile> AutofillProfileChange; typedef AutofillDataModelChange<CreditCard> CreditCardChange; +typedef AutofillDataModelChange<IBAN> IBANChange; class AutofillProfileDeepChange : public AutofillProfileChange { public: 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 1b7749f9dc7..19385e1de0e 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 @@ -42,8 +42,8 @@ #include "components/sync/protocol/entity_data.h" #include "components/sync/protocol/entity_specifics.pb.h" #include "components/sync/protocol/model_type_state.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/sync/test/mock_model_type_change_processor.h" +#include "components/sync/test/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_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc index 3366f595c0c..0eeacd5407e 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc @@ -7,11 +7,13 @@ #include <stdint.h> #include <algorithm> +#include <initializer_list> #include <limits> #include <map> #include <memory> #include <set> #include <utility> +#include <vector> #include "base/command_line.h" #include "base/containers/contains.h" @@ -21,6 +23,7 @@ #include "base/numerics/safe_conversions.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" @@ -30,6 +33,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/data_model/credit_card_cloud_token_data.h" +#include "components/autofill/core/browser/data_model/iban.h" #include "components/autofill/core/browser/geo/autofill_country.h" #include "components/autofill/core/browser/payments/payments_customer_data.h" #include "components/autofill/core/browser/personal_data_manager.h" @@ -57,6 +61,432 @@ namespace autofill { namespace { +constexpr base::StringPiece kAutofillTable = "autofill"; +constexpr base::StringPiece kName = "name"; +constexpr base::StringPiece kValue = "value"; +constexpr base::StringPiece kValueLower = "value_lower"; +constexpr base::StringPiece kDateCreated = "date_created"; +constexpr base::StringPiece kDateLastUsed = "date_last_used"; +constexpr base::StringPiece kCount = "count"; + +constexpr base::StringPiece kAutofillProfilesTable = "autofill_profiles"; +constexpr base::StringPiece kGuid = "guid"; +constexpr base::StringPiece kLabel = "label"; +constexpr base::StringPiece kCompanyName = "company_name"; +constexpr base::StringPiece kStreetAddress = "street_address"; +constexpr base::StringPiece kDependentLocality = "dependent_locality"; +constexpr base::StringPiece kCity = "city"; +constexpr base::StringPiece kState = "state"; +constexpr base::StringPiece kZipcode = "zipcode"; +constexpr base::StringPiece kSortingCode = "sorting_code"; +constexpr base::StringPiece kCountryCode = "country_code"; +constexpr base::StringPiece kUseCount = "use_count"; +constexpr base::StringPiece kUseDate = "use_date"; +constexpr base::StringPiece kDateModified = "date_modified"; +constexpr base::StringPiece kOrigin = "origin"; +constexpr base::StringPiece kLanguageCode = "language_code"; +constexpr base::StringPiece kDisallowSettingsVisibleUpdates = + "disallow_settings_visible_updates"; + +constexpr base::StringPiece kAutofillProfileAddressesTable = + "autofill_profile_addresses"; +// kGuid = "guid" +// kStreetAddress = "street_address" +constexpr base::StringPiece kStreetName = "street_name"; +constexpr base::StringPiece kDependentStreetName = "dependent_street_name"; +constexpr base::StringPiece kHouseNumber = "house_number"; +constexpr base::StringPiece kSubpremise = "subpremise"; +// kDependentLocality = "dependent_locality" +// kCity = "city" +// kState = "state" +constexpr base::StringPiece kZipCode = "zip_code"; +// kCountryCode = "country_code" +// kSortingCode = "sorting_code" +constexpr base::StringPiece kPremiseName = "premise_name"; +constexpr base::StringPiece kApartmentNumber = "apartment_number"; +constexpr base::StringPiece kFloor = "floor"; +constexpr base::StringPiece kStreetAddressStatus = "street_address_status"; +constexpr base::StringPiece kStreetNameStatus = "street_name_status"; +constexpr base::StringPiece kDependentStreetNameStatus = + "dependent_street_name_status"; +constexpr base::StringPiece kHouseNumberStatus = "house_number_status"; +constexpr base::StringPiece kSubpremiseStatus = "subpremise_status"; +constexpr base::StringPiece kPremiseNameStatus = "premise_name_status"; +constexpr base::StringPiece kDependentLocalityStatus = + "dependent_locality_status"; +constexpr base::StringPiece kCityStatus = "city_status"; +constexpr base::StringPiece kStateStatus = "state_status"; +constexpr base::StringPiece kZipCodeStatus = "zip_code_status"; +constexpr base::StringPiece kCountryCodeStatus = "country_code_status"; +constexpr base::StringPiece kSortingCodeStatus = "sorting_code_status"; +constexpr base::StringPiece kApartmentNumberStatus = "apartment_number_status"; +constexpr base::StringPiece kFloorStatus = "floor_status"; + +constexpr base::StringPiece kAutofillProfileNamesTable = + "autofill_profile_names"; +// kGuid = "guid" +constexpr base::StringPiece kHonorificPrefix = "honorific_prefix"; +constexpr base::StringPiece kFirstName = "first_name"; +constexpr base::StringPiece kMiddleName = "middle_name"; +constexpr base::StringPiece kLastName = "last_name"; +constexpr base::StringPiece kFirstLastName = "first_last_name"; +constexpr base::StringPiece kConjunctionLastName = "conjunction_last_name"; +constexpr base::StringPiece kSecondLastName = "second_last_name"; +constexpr base::StringPiece kFullName = "full_name"; +constexpr base::StringPiece kFullNameWithHonorificPrefix = + "full_name_with_honorific_prefix"; +constexpr base::StringPiece kHonorificPrefixStatus = "honorific_prefix_status"; +constexpr base::StringPiece kFirstNameStatus = "first_name_status"; +constexpr base::StringPiece kMiddleNameStatus = "middle_name_status"; +constexpr base::StringPiece kLastNameStatus = "last_name_status"; +constexpr base::StringPiece kFirstLastNameStatus = "first_last_name_status"; +constexpr base::StringPiece kConjunctionLastNameStatus = + "conjunction_last_name_status"; +constexpr base::StringPiece kSecondLastNameStatus = "second_last_name_status"; +constexpr base::StringPiece kFullNameStatus = "full_name_status"; +constexpr base::StringPiece kFullNameWithHonorificPrefixStatus = + "full_name_with_honorific_prefix_status"; + +constexpr base::StringPiece kAutofillProfileEmailsTable = + "autofill_profile_emails"; +// kGuid = "guid" +constexpr base::StringPiece kEmail = "email"; + +constexpr base::StringPiece kAutofillProfilePhonesTable = + "autofill_profile_phones"; +// kGuid = "guid" +constexpr base::StringPiece kNumber = "number"; + +constexpr base::StringPiece kAutofillProfileBirthdatesTable = + "autofill_profile_birthdates"; +// kGuid = "guid" +constexpr base::StringPiece kDay = "day"; +constexpr base::StringPiece kMonth = "month"; +constexpr base::StringPiece kYear = "year"; + +constexpr base::StringPiece kCreditCardsTable = "credit_cards"; +// kGuid = "guid" +constexpr base::StringPiece kNameOnCard = "name_on_card"; +constexpr base::StringPiece kExpirationMonth = "expiration_month"; +constexpr base::StringPiece kExpirationYear = "expiration_year"; +constexpr base::StringPiece kCardNumberEncrypted = "card_number_encrypted"; +// kUseCount = "use_count" +// kUseDate = "use_date" +// kDateModified = "date_modified" +// kOrigin = "origin" +constexpr base::StringPiece kBillingAddressId = "billing_address_id"; +constexpr base::StringPiece kNickname = "nickname"; + +constexpr base::StringPiece kMaskedCreditCardsTable = "masked_credit_cards"; +constexpr base::StringPiece kId = "id"; +constexpr base::StringPiece kStatus = "status"; +// kNameOnCard = "name_on_card" +constexpr base::StringPiece kNetwork = "network"; +constexpr base::StringPiece kLastFour = "last_four"; +constexpr base::StringPiece kExpMonth = "exp_month"; +constexpr base::StringPiece kExpYear = "exp_year"; +constexpr base::StringPiece kBankName = "bank_name"; +// kNickname = "nickname" +constexpr base::StringPiece kCardIssuer = "card_issuer"; +constexpr base::StringPiece kInstrumentId = "instrument_id"; +constexpr base::StringPiece kVirtualCardEnrollmentState = + "virtual_card_enrollment_state"; +constexpr base::StringPiece kCardArtUrl = "card_art_url"; +constexpr base::StringPiece kProductDescription = "product_description"; + +constexpr base::StringPiece kUnmaskedCreditCardsTable = "unmasked_credit_cards"; +// kId = "id" +// kCardNumberEncrypted = "card_number_encrypted" +constexpr base::StringPiece kUnmaskDate = "unmask_date"; + +constexpr base::StringPiece kServerCardCloudTokenDataTable = + "server_card_cloud_token_data"; +// kId = "id" +constexpr base::StringPiece kSuffix = "suffix"; +// kExpMonth = "exp_month" +// kExpYear = "exp_year" +// kCardArtUrl = "card_art_url" +constexpr base::StringPiece kInstrumentToken = "instrument_token"; + +constexpr base::StringPiece kServerCardMetadataTable = "server_card_metadata"; +// kId = "id" +// kUseCount = "use_count" +// kUseDate = "use_date" +// kBillingAddressId = "billing_address_id" + +constexpr base::StringPiece kIBANsTable = "ibans"; +// kGuid = "guid" +// kUseCount = "use_count" +// kUseDate = "use_date" +// kValue = "value" +// kNickname = "nickname" + +constexpr base::StringPiece kServerAddressesTable = "server_addresses"; +// kId = "id" +constexpr base::StringPiece kRecipientName = "recipient_name"; +// kCompanyName = "company_name" +// kStreetAddress = "street_address" +constexpr base::StringPiece kAddress1 = "address_1"; +constexpr base::StringPiece kAddress2 = "address_2"; +constexpr base::StringPiece kAddress3 = "address_3"; +constexpr base::StringPiece kAddress4 = "address_4"; +constexpr base::StringPiece kPostalCode = "postal_code"; +// kSortingCode = "sorting_code" +// kCountryCode = "country_code" +// kLanguageCode = "language_code" +constexpr base::StringPiece kPhoneNumber = "phone_number"; + +constexpr base::StringPiece kServerAddressMetadataTable = + "server_address_metadata"; +// kId = "id" +// kUseCount = "use_count" +// kUseDate = "use_date" +constexpr base::StringPiece kHasConverted = "has_converted"; + +constexpr base::StringPiece kAutofillSyncMetadataTable = + "autofill_sync_metadata"; +constexpr base::StringPiece kModelType = "model_type"; +constexpr base::StringPiece kStorageKey = "storage_key"; +// kValue = "value" + +constexpr base::StringPiece kAutofillModelTypeStateTable = + "autofill_model_type_state"; +// kModelType = "model_type" +// kValue = "value" + +constexpr base::StringPiece kPaymentsCustomerDataTable = + "payments_customer_data"; +constexpr base::StringPiece kCustomerId = "customer_id"; + +constexpr base::StringPiece kPaymentsUpiVpaTable = "payments_upi_vpa"; +constexpr base::StringPiece kVpa = "vpa"; + +constexpr base::StringPiece kOfferDataTable = "offer_data"; +constexpr base::StringPiece kOfferId = "offer_id"; +constexpr base::StringPiece kOfferRewardAmount = "offer_reward_amount"; +constexpr base::StringPiece kExpiry = "expiry"; +constexpr base::StringPiece kOfferDetailsUrl = "offer_details_url"; +constexpr base::StringPiece kPromoCode = "promo_code"; +constexpr base::StringPiece kValuePropText = "value_prop_text"; +constexpr base::StringPiece kSeeDetailsText = "see_details_text"; +constexpr base::StringPiece kUsageInstructionsText = "usage_instructions_text"; + +constexpr base::StringPiece kOfferEligibleInstrumentTable = + "offer_eligible_instrument"; +// kOfferId = "offer_id" +// kInstrumentId = "instrument_id" + +constexpr base::StringPiece kOfferMerchantDomainTable = "offer_merchant_domain"; +// kOfferId = "offer_id" +constexpr base::StringPiece kMerchantDomain = "merchant_domain"; + +// Helper functions to construct SQL statements from string constants. +// - Functions with names corresponding to SQL keywords execute the statement +// directly and return if it was successful. +// - Builder functions only assign the statement, which enables binding +// values to placeholders before running it. + +// Executes a CREATE TABLE statement on `db` which the provided `table_name`. +// The columns are described in `column_names_and_types` as pairs of +// (name, type), where type can include modifiers such as NOT NULL. +// By specifying `compositive_primary_key`, a PRIMARY KEY (col1, col2, ..) +// clause is generated. +// Returns true if successful. +bool CreateTable( + sql::Database* db, + base::StringPiece table_name, + std::initializer_list<std::pair<base::StringPiece, base::StringPiece>> + column_names_and_types, + std::initializer_list<base::StringPiece> composite_primary_key = {}) { + DCHECK(composite_primary_key.size() == 0 || + composite_primary_key.size() >= 2); + + std::vector<std::string> combined_names_and_types; + combined_names_and_types.reserve(column_names_and_types.size()); + for (const auto& [name, type] : column_names_and_types) + combined_names_and_types.push_back(base::StrCat({name, " ", type})); + + auto primary_key_clause = + composite_primary_key.size() == 0 + ? "" + : base::StrCat({", PRIMARY KEY (", + base::JoinString(composite_primary_key, ", "), ")"}); + + return db->Execute( + base::StrCat({"CREATE TABLE ", table_name, " (", + base::JoinString(combined_names_and_types, ", "), + primary_key_clause, ")"}) + .c_str()); +} + +// Wrapper around `CreateTable()` that condition the creation on the +// `table_name` not existing. +// Returns true if the table now exists. +bool CreateTableIfNotExists( + sql::Database* db, + base::StringPiece table_name, + std::initializer_list<std::pair<base::StringPiece, base::StringPiece>> + column_names_and_types, + std::initializer_list<base::StringPiece> composite_primary_key = {}) { + return db->DoesTableExist(table_name) || + CreateTable(db, table_name, column_names_and_types, + composite_primary_key); +} + +// Creates and index on `table_name` for the provided `columns`. +// The index is named after the table and columns, separated by '_'. +// Returns true if successful. +bool CreateIndex(sql::Database* db, + base::StringPiece table_name, + std::initializer_list<base::StringPiece> columns) { + auto index_name = + base::StrCat({table_name, "_", base::JoinString(columns, "_")}); + return db->Execute( + base::StrCat({"CREATE INDEX ", index_name, " ON ", table_name, "(", + base::JoinString(columns, ", "), ")"}) + .c_str()); +} + +// Initializes `statement` with INSERT INTO `table_name`, with placeholders for +// all `column_names`. +// By setting `or_replace`, INSERT OR REPLACE INTO is used instead. +void InsertBuilder(sql::Database* db, + sql::Statement& statement, + base::StringPiece table_name, + std::initializer_list<base::StringPiece> column_names, + bool or_replace = false) { + auto insert_or_replace = + base::StrCat({"INSERT ", or_replace ? "OR REPLACE " : ""}); + auto placeholders = base::JoinString( + std::vector<std::string>(column_names.size(), "?"), ", "); + statement.Assign(db->GetUniqueStatement( + base::StrCat({insert_or_replace, "INTO ", table_name, " (", + base::JoinString(column_names, ", "), ") VALUES (", + placeholders, ")"}) + .c_str())); +} + +// Renames the table `from` into `to` and returns true if successful. +bool RenameTable(sql::Database* db, + base::StringPiece from, + base::StringPiece to) { + return db->Execute( + base::StrCat({"ALTER TABLE ", from, " RENAME TO ", to}).c_str()); +} + +// Wrapper around `sql::Database::DoesColumnExist()`, because that function +// only accepts const char* parameters. +bool DoesColumnExist(sql::Database* db, + base::StringPiece table_name, + base::StringPiece column_name) { + return db->DoesColumnExist(std::string(table_name).c_str(), + std::string(column_name).c_str()); +} + +// Adds a column named `column_name` of `type` to `table_name` and returns true +// if successful. +bool AddColumn(sql::Database* db, + base::StringPiece table_name, + base::StringPiece column_name, + base::StringPiece type) { + return db->Execute(base::StrCat({"ALTER TABLE ", table_name, " ADD COLUMN ", + column_name, " ", type}) + .c_str()); +} + +// Like `AddColumn()`, but conditioned on `column` not existing in `table_name`. +// Returns true if the column is now part of the table +bool AddColumnIfNotExists(sql::Database* db, + base::StringPiece table_name, + base::StringPiece column_name, + base::StringPiece type) { + return DoesColumnExist(db, table_name, column_name) || + AddColumn(db, table_name, column_name, type); +} + +// Drops `table_name` and returns true if successful. +bool DropTable(sql::Database* db, base::StringPiece table_name) { + return db->Execute(base::StrCat({"DROP TABLE ", table_name}).c_str()); +} + +// Initializes `statement` with DELETE FROM `table_name`. A WHERE clause +// can optionally be specified in `where_clause`. +void DeleteBuilder(sql::Database* db, + sql::Statement& statement, + base::StringPiece table_name, + base::StringPiece where_clause = "") { + auto where = + where_clause.empty() ? "" : base::StrCat({" WHERE ", where_clause}); + statement.Assign(db->GetUniqueStatement( + base::StrCat({"DELETE FROM ", table_name, where}).c_str())); +} + +// Like `DeleteBuilder()`, but runs the statement and returns true if it was +// successful. +bool Delete(sql::Database* db, + base::StringPiece table_name, + base::StringPiece where_clause = "") { + sql::Statement statement; + DeleteBuilder(db, statement, table_name, where_clause); + return statement.Run(); +} + +// Wrapper around `DeleteBuilder()`, which initializes the where clause as +// `column` = `value`. +// Runs the statement and returns true if it was successful. +bool DeleteWhereColumnEq(sql::Database* db, + base::StringPiece table_name, + base::StringPiece column, + base::StringPiece value) { + sql::Statement statement; + DeleteBuilder(db, statement, table_name, base::StrCat({column, " = ?"})); + statement.BindString(0, value); + return statement.Run(); +} + +// Initializes `statement` with SELECT `columns` FROM `table_name` and +// optionally further `modifiers`, such as WHERE, ORDER BY, etc. +void SelectBuilder(sql::Database* db, + sql::Statement& statement, + base::StringPiece table_name, + std::initializer_list<base::StringPiece> columns, + base::StringPiece modifiers = "") { + statement.Assign(db->GetUniqueStatement( + base::StrCat({"SELECT ", base::JoinString(columns, ", "), " FROM ", + table_name, " ", modifiers}) + .c_str())); +} + +// Wrapper around `SelectBuilder()` that restricts the it to the provided `guid` +// and limits the results to 1. Returns `statement.is_valid() && +// statement.Step()`. +bool SelectByGuid(sql::Database* db, + sql::Statement& statement, + base::StringPiece table_name, + std::initializer_list<base::StringPiece> columns, + base::StringPiece guid) { + DCHECK(base::Contains(columns, kGuid)); + SelectBuilder(db, statement, table_name, columns, "WHERE guid=? LIMIT 1"); + statement.BindString(0, guid); + return statement.is_valid() && statement.Step(); +} + +// Wrapper around `SelectBuilder()` that restricts it to the half-open interval +// [low, high[ of `column_between`. +void SelectBetween(sql::Database* db, + sql::Statement& statement, + base::StringPiece table_name, + std::initializer_list<base::StringPiece> columns, + base::StringPiece column_between, + int64_t low, + int64_t high) { + auto between_selector = base::StrCat( + {"WHERE ", column_between, " >= ? AND ", column_between, " < ?"}); + SelectBuilder(db, statement, table_name, columns, between_selector); + statement.BindInt64(0, low); + statement.BindInt64(1, high); +} + // Constant to assign an unset verification status to structured address // components stored for legacy profiles. constexpr structured_address::VerificationStatus kNoStatus = @@ -90,14 +520,12 @@ void BindAutofillProfileToStatement(const AutofillProfile& profile, int index = 0; s->BindString(index++, profile.guid()); - s->BindString16(index++, GetInfo(profile, COMPANY_NAME)); - s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_STREET_ADDRESS)); - s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_DEPENDENT_LOCALITY)); - s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_CITY)); - s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_STATE)); - s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_ZIP)); - s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_SORTING_CODE)); - s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_COUNTRY)); + for (ServerFieldType type : + {COMPANY_NAME, ADDRESS_HOME_STREET_ADDRESS, + ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, + ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY}) { + s->BindString16(index++, GetInfo(profile, type)); + } s->BindInt64(index++, profile.use_count()); s->BindInt64(index++, profile.use_date().ToTimeT()); s->BindInt64(index++, modification_date.ToTimeT()); @@ -110,15 +538,12 @@ void BindAutofillProfileToStatement(const AutofillProfile& profile, void AddAutofillProfileDetailsFromStatement(sql::Statement& s, AutofillProfile* profile) { int index = 1; // 0 is for the guid. - profile->SetRawInfo(COMPANY_NAME, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, - s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_SORTING_CODE, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(index++)); + for (ServerFieldType type : + {COMPANY_NAME, ADDRESS_HOME_STREET_ADDRESS, + ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, + ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY}) { + profile->SetRawInfo(type, s.ColumnString16(index++)); + } profile->set_use_count(s.ColumnInt64(index++)); profile->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++))); profile->set_modification_date(base::Time::FromTimeT(s.ColumnInt64(index++))); @@ -145,9 +570,10 @@ void BindCreditCardToStatement(const CreditCard& credit_card, int index = 0; s->BindString(index++, credit_card.guid()); - s->BindString16(index++, GetInfo(credit_card, CREDIT_CARD_NAME_FULL)); - s->BindString16(index++, GetInfo(credit_card, CREDIT_CARD_EXP_MONTH)); - s->BindString16(index++, GetInfo(credit_card, CREDIT_CARD_EXP_4_DIGIT_YEAR)); + for (ServerFieldType type : {CREDIT_CARD_NAME_FULL, CREDIT_CARD_EXP_MONTH, + CREDIT_CARD_EXP_4_DIGIT_YEAR}) { + s->BindString16(index++, GetInfo(credit_card, type)); + } BindEncryptedCardToColumn( s, index++, credit_card.GetRawInfo(CREDIT_CARD_NUMBER), encryptor); @@ -159,6 +585,20 @@ void BindCreditCardToStatement(const CreditCard& credit_card, s->BindString16(index++, credit_card.nickname()); } +void BindIBANToStatement(const IBAN& iban, + sql::Statement* s, + const AutofillTableEncryptor& encryptor) { + DCHECK(base::IsValidGUID(iban.guid())); + int index = 0; + s->BindString(index++, iban.guid()); + + s->BindInt64(index++, iban.use_count()); + s->BindInt64(index++, iban.use_date().ToTimeT()); + + s->BindString16(index++, iban.value()); + s->BindString16(index++, iban.nickname()); +} + std::u16string UnencryptedCardFromColumn( sql::Statement& s, int column_index, @@ -180,10 +620,10 @@ std::unique_ptr<CreditCard> CreditCardFromStatement( credit_card->set_guid(s.ColumnString(index++)); DCHECK(base::IsValidGUID(credit_card->guid())); - credit_card->SetRawInfo(CREDIT_CARD_NAME_FULL, s.ColumnString16(index++)); - credit_card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(index++)); - credit_card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, - s.ColumnString16(index++)); + for (ServerFieldType type : {CREDIT_CARD_NAME_FULL, CREDIT_CARD_EXP_MONTH, + CREDIT_CARD_EXP_4_DIGIT_YEAR}) { + credit_card->SetRawInfo(type, s.ColumnString16(index++)); + } credit_card->SetRawInfo(CREDIT_CARD_NUMBER, UnencryptedCardFromColumn(s, index++, encryptor)); credit_card->set_use_count(s.ColumnInt64(index++)); @@ -196,57 +636,55 @@ std::unique_ptr<CreditCard> CreditCardFromStatement( return credit_card; } +std::unique_ptr<IBAN> IBANFromStatement( + sql::Statement& s, + const AutofillTableEncryptor& encryptor) { + auto iban = std::make_unique<IBAN>(); + + int index = 0; + iban->set_guid(s.ColumnString(index++)); + DCHECK(base::IsValidGUID(iban->guid())); + iban->set_use_count(s.ColumnInt64(index++)); + iban->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++))); + + iban->SetRawInfo(IBAN_VALUE, s.ColumnString16(index++)); + iban->set_nickname(s.ColumnString16(index++)); + return iban; +} + bool AddAutofillProfileNames(const AutofillProfile& profile, sql::Database* db) { if (base::FeatureList::IsEnabled( features::kAutofillEnableSupportForMoreStructureInNames)) { - sql::Statement s(db->GetUniqueStatement( - "INSERT INTO autofill_profile_names " - "(guid, " - "honorific_prefix, honorific_prefix_status, " - "first_name, first_name_status, " - "middle_name, middle_name_status, " - "first_last_name, first_last_name_status, " - "conjunction_last_name, conjunction_last_name_status, " - "second_last_name, second_last_name_status, " - "last_name, last_name_status, " - "full_name, full_name_status, " - "full_name_with_honorific_prefix, " - "full_name_with_honorific_prefix_status) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); + sql::Statement s; + InsertBuilder( + db, s, kAutofillProfileNamesTable, + {kGuid, kHonorificPrefix, kHonorificPrefixStatus, kFirstName, + kFirstNameStatus, kMiddleName, kMiddleNameStatus, kFirstLastName, + kFirstLastNameStatus, kConjunctionLastName, kConjunctionLastNameStatus, + kSecondLastName, kSecondLastNameStatus, kLastName, kLastNameStatus, + kFullName, kFullNameStatus, kFullNameWithHonorificPrefix, + kFullNameWithHonorificPrefixStatus}); s.BindString(0, profile.guid()); - s.BindString16(1, profile.GetRawInfo(NAME_HONORIFIC_PREFIX)); - s.BindInt(2, profile.GetVerificationStatusInt(NAME_HONORIFIC_PREFIX)); - s.BindString16(3, profile.GetRawInfo(NAME_FIRST)); - s.BindInt(4, profile.GetVerificationStatusInt(NAME_FIRST)); - s.BindString16(5, profile.GetRawInfo(NAME_MIDDLE)); - s.BindInt(6, profile.GetVerificationStatusInt(NAME_MIDDLE)); - s.BindString16(7, profile.GetRawInfo(NAME_LAST_FIRST)); - s.BindInt(8, profile.GetVerificationStatusInt(NAME_LAST_FIRST)); - s.BindString16(9, profile.GetRawInfo(NAME_LAST_CONJUNCTION)); - s.BindInt(10, profile.GetVerificationStatusInt(NAME_LAST_CONJUNCTION)); - s.BindString16(11, profile.GetRawInfo(NAME_LAST_SECOND)); - s.BindInt(12, profile.GetVerificationStatusInt(NAME_LAST_SECOND)); - s.BindString16(13, profile.GetRawInfo(NAME_LAST)); - s.BindInt(14, profile.GetVerificationStatusInt(NAME_LAST)); - s.BindString16(15, profile.GetRawInfo(NAME_FULL)); - s.BindInt(16, profile.GetVerificationStatusInt(NAME_FULL)); - s.BindString16(17, profile.GetRawInfo(NAME_FULL_WITH_HONORIFIC_PREFIX)); - s.BindInt( - 18, profile.GetVerificationStatusInt(NAME_FULL_WITH_HONORIFIC_PREFIX)); + int index = 1; + for (ServerFieldType type : + {NAME_HONORIFIC_PREFIX, NAME_FIRST, NAME_MIDDLE, NAME_LAST_FIRST, + NAME_LAST_CONJUNCTION, NAME_LAST_SECOND, NAME_LAST, NAME_FULL, + NAME_FULL_WITH_HONORIFIC_PREFIX}) { + s.BindString16(index++, profile.GetRawInfo(type)); + s.BindInt(index++, profile.GetVerificationStatusInt(type)); + } return s.Run(); } // Add the new name. - sql::Statement s( - db->GetUniqueStatement("INSERT INTO autofill_profile_names" - " (guid, first_name, middle_name, last_name, " - "full_name) " - "VALUES (?,?,?,?,?)")); + sql::Statement s; + InsertBuilder(db, s, kAutofillProfileNamesTable, + {kGuid, kFirstName, kMiddleName, kLastName, kFullName}); s.BindString(0, profile.guid()); - s.BindString16(1, profile.GetRawInfo(NAME_FIRST)); - s.BindString16(2, profile.GetRawInfo(NAME_MIDDLE)); - s.BindString16(3, profile.GetRawInfo(NAME_LAST)); - s.BindString16(4, profile.GetRawInfo(NAME_FULL)); + int index = 1; + for (ServerFieldType type : {NAME_FIRST, NAME_MIDDLE, NAME_LAST, NAME_FULL}) { + s.BindString16(index++, profile.GetRawInfo(type)); + } return s.Run(); } @@ -257,57 +695,50 @@ bool AddAutofillProfileAddresses(const AutofillProfile& profile, // votes. if (base::FeatureList::IsEnabled( features::kAutofillEnableSupportForMoreStructureInAddresses)) { - sql::Statement s(db->GetUniqueStatement( - "INSERT INTO autofill_profile_addresses " - "(guid, " - "street_address, street_address_status, " - "street_name, street_name_status, " - "dependent_street_name, dependent_street_name_status, " - "house_number, house_number_status, " - "subpremise, subpremise_status, " - "premise_name, premise_name_status, " - "dependent_locality, dependent_locality_status, " - "city, city_status, " - "state, state_status, " - "zip_code, zip_code_status, " - "sorting_code, sorting_code_status, " - "country_code, country_code_status, " - "apartment_number, apartment_number_status, " - "floor, floor_status) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); + sql::Statement s; + InsertBuilder(db, s, kAutofillProfileAddressesTable, + {kGuid, + kStreetAddress, + kStreetAddressStatus, + kStreetName, + kStreetNameStatus, + kDependentStreetName, + kDependentStreetNameStatus, + kHouseNumber, + kHouseNumberStatus, + kSubpremise, + kSubpremiseStatus, + kPremiseName, + kPremiseNameStatus, + kDependentLocality, + kDependentLocalityStatus, + kCity, + kCityStatus, + kState, + kStateStatus, + kZipCode, + kZipCodeStatus, + kSortingCode, + kSortingCodeStatus, + kCountryCode, + kCountryCodeStatus, + kApartmentNumber, + kApartmentNumberStatus, + kFloor, + kFloorStatus}); s.BindString(0, profile.guid()); - s.BindString16(1, profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); - s.BindInt(2, profile.GetVerificationStatusInt(ADDRESS_HOME_STREET_ADDRESS)); - s.BindString16(3, profile.GetRawInfo(ADDRESS_HOME_STREET_NAME)); - s.BindInt(4, profile.GetVerificationStatusInt(ADDRESS_HOME_STREET_NAME)); - s.BindString16(5, profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_STREET_NAME)); - s.BindInt(6, profile.GetVerificationStatusInt( - ADDRESS_HOME_DEPENDENT_STREET_NAME)); - s.BindString16(7, profile.GetRawInfo(ADDRESS_HOME_HOUSE_NUMBER)); - s.BindInt(8, profile.GetVerificationStatusInt(ADDRESS_HOME_HOUSE_NUMBER)); - s.BindString16(9, profile.GetRawInfo(ADDRESS_HOME_SUBPREMISE)); - s.BindInt(10, profile.GetVerificationStatusInt(ADDRESS_HOME_SUBPREMISE)); - s.BindString16(11, profile.GetRawInfo(ADDRESS_HOME_PREMISE_NAME)); - s.BindInt(12, profile.GetVerificationStatusInt(ADDRESS_HOME_PREMISE_NAME)); - s.BindString16(13, profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY)); - s.BindInt( - 14, profile.GetVerificationStatusInt(ADDRESS_HOME_DEPENDENT_LOCALITY)); - s.BindString16(15, profile.GetRawInfo(ADDRESS_HOME_CITY)); - s.BindInt(16, profile.GetVerificationStatusInt(ADDRESS_HOME_CITY)); - s.BindString16(17, profile.GetRawInfo(ADDRESS_HOME_STATE)); - s.BindInt(18, profile.GetVerificationStatusInt(ADDRESS_HOME_STATE)); - s.BindString16(19, profile.GetRawInfo(ADDRESS_HOME_ZIP)); - s.BindInt(20, profile.GetVerificationStatusInt(ADDRESS_HOME_ZIP)); - s.BindString16(21, profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE)); - s.BindInt(22, profile.GetVerificationStatusInt(ADDRESS_HOME_SORTING_CODE)); - s.BindString16(23, profile.GetRawInfo(ADDRESS_HOME_COUNTRY)); - s.BindInt(24, profile.GetVerificationStatusInt(ADDRESS_HOME_COUNTRY)); - s.BindString16(25, profile.GetRawInfo(ADDRESS_HOME_APT_NUM)); - s.BindInt(26, profile.GetVerificationStatusInt(ADDRESS_HOME_APT_NUM)); - s.BindString16(27, profile.GetRawInfo(ADDRESS_HOME_FLOOR)); - s.BindInt(28, profile.GetVerificationStatusInt(ADDRESS_HOME_FLOOR)); - + int index = 1; + for (ServerFieldType type : + {ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_STREET_NAME, + ADDRESS_HOME_DEPENDENT_STREET_NAME, ADDRESS_HOME_HOUSE_NUMBER, + ADDRESS_HOME_SUBPREMISE, ADDRESS_HOME_PREMISE_NAME, + ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, + ADDRESS_HOME_COUNTRY, ADDRESS_HOME_APT_NUM, ADDRESS_HOME_FLOOR}) { + s.BindString16(index++, profile.GetRawInfo(type)); + s.BindInt(index++, profile.GetVerificationStatusInt(type)); + } return s.Run(); } return true; @@ -315,27 +746,16 @@ bool AddAutofillProfileAddresses(const AutofillProfile& profile, bool AddAutofillProfileNamesToProfile(sql::Database* db, AutofillProfile* profile) { - sql::Statement s(db->GetUniqueStatement( - "SELECT " - "guid, " - "honorific_prefix, honorific_prefix_status, " - "first_name, first_name_status, " - "middle_name, middle_name_status, " - "first_last_name, first_last_name_status, " - "conjunction_last_name, conjunction_last_name_status, " - "second_last_name, second_last_name_status, " - "last_name, last_name_status, " - "full_name, full_name_status, " - "full_name_with_honorific_prefix, full_name_with_honorific_prefix_status " - "FROM autofill_profile_names " - "WHERE guid=? " - "LIMIT 1")); - s.BindString(0, profile->guid()); - - if (!s.is_valid()) - return false; - - if (s.Step()) { + sql::Statement s; + if (SelectByGuid( + db, s, kAutofillProfileNamesTable, + {kGuid, kHonorificPrefix, kHonorificPrefixStatus, kFirstName, + kFirstNameStatus, kMiddleName, kMiddleNameStatus, kFirstLastName, + kFirstLastNameStatus, kConjunctionLastName, + kConjunctionLastNameStatus, kSecondLastName, kSecondLastNameStatus, + kLastName, kLastNameStatus, kFullName, kFullNameStatus, + kFullNameWithHonorificPrefix, kFullNameWithHonorificPrefixStatus}, + profile->guid())) { DCHECK_EQ(profile->guid(), s.ColumnString(0)); if (base::FeatureList::IsEnabled( @@ -343,25 +763,15 @@ bool AddAutofillProfileNamesToProfile(sql::Database* db, // Whether or not the name has a legacy structure, set all // components. The Profile can detect that it must be migrated because // all values have the validation status |kNoStatus|. - profile->SetRawInfoWithVerificationStatusInt( - NAME_HONORIFIC_PREFIX, s.ColumnString16(1), s.ColumnInt(2)); - profile->SetRawInfoWithVerificationStatusInt( - NAME_FIRST, s.ColumnString16(3), s.ColumnInt(4)); - profile->SetRawInfoWithVerificationStatusInt( - NAME_MIDDLE, s.ColumnString16(5), s.ColumnInt(6)); - profile->SetRawInfoWithVerificationStatusInt( - NAME_LAST_FIRST, s.ColumnString16(7), s.ColumnInt(8)); - profile->SetRawInfoWithVerificationStatusInt( - NAME_LAST_CONJUNCTION, s.ColumnString16(9), s.ColumnInt(10)); - profile->SetRawInfoWithVerificationStatusInt( - NAME_LAST_SECOND, s.ColumnString16(11), s.ColumnInt(12)); - profile->SetRawInfoWithVerificationStatusInt( - NAME_LAST, s.ColumnString16(13), s.ColumnInt(14)); - profile->SetRawInfoWithVerificationStatusInt( - NAME_FULL, s.ColumnString16(15), s.ColumnInt(16)); - profile->SetRawInfoWithVerificationStatusInt( - NAME_FULL_WITH_HONORIFIC_PREFIX, s.ColumnString16(17), - s.ColumnInt(18)); + int index = 1; + for (ServerFieldType type : + {NAME_HONORIFIC_PREFIX, NAME_FIRST, NAME_MIDDLE, NAME_LAST_FIRST, + NAME_LAST_CONJUNCTION, NAME_LAST_SECOND, NAME_LAST, NAME_FULL, + NAME_FULL_WITH_HONORIFIC_PREFIX}) { + profile->SetRawInfoWithVerificationStatusInt( + type, s.ColumnString16(index), s.ColumnInt(index + 1)); + index += 2; + } } else { // If structured components are not enabled, only use the legacy // structure. @@ -380,121 +790,102 @@ bool AddAutofillProfileNamesToProfile(sql::Database* db, bool AddAutofillProfileAddressesToProfile(sql::Database* db, AutofillProfile* profile) { - if (base::FeatureList::IsEnabled( + if (!base::FeatureList::IsEnabled( features::kAutofillEnableSupportForMoreStructureInAddresses)) { - sql::Statement s(db->GetUniqueStatement( - "SELECT " - "guid, " - "street_address, street_address_status, " - "street_name, street_name_status, " - "dependent_street_name, dependent_street_name_status, " - "house_number, house_number_status, " - "subpremise, subpremise_status, " - "premise_name, premise_name_status, " - "dependent_locality, dependent_locality_status, " - "city, city_status, " - "state, state_status, " - "zip_code, zip_code_status, " - "sorting_code, sorting_code_status, " - "country_code, country_code_status, " - "apartment_number, apartment_number_status, " - "floor, floor_status " - "FROM autofill_profile_addresses " - "WHERE guid=? " - "LIMIT 1")); - s.BindString(0, profile->guid()); - - if (!s.is_valid()) - return false; - - if (s.Step()) { - DCHECK_EQ(profile->guid(), s.ColumnString(0)); - std::u16string street_address = s.ColumnString16(1); - std::u16string dependent_locality = s.ColumnString16(13); - std::u16string city = s.ColumnString16(15); - std::u16string state = s.ColumnString16(17); - std::u16string zip_code = s.ColumnString16(19); - std::u16string sorting_code = s.ColumnString16(21); - std::u16string country = s.ColumnString16(23); - - std::u16string street_address_legacy = - profile->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS); - std::u16string dependent_locality_legacy = - profile->GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY); - std::u16string city_legacy = profile->GetRawInfo(ADDRESS_HOME_CITY); - std::u16string state_legacy = profile->GetRawInfo(ADDRESS_HOME_STATE); - std::u16string zip_code_legacy = profile->GetRawInfo(ADDRESS_HOME_ZIP); - std::u16string sorting_code_legacy = - profile->GetRawInfo(ADDRESS_HOME_SORTING_CODE); - std::u16string country_legacy = profile->GetRawInfo(ADDRESS_HOME_COUNTRY); - - // At this stage, the unstructured address was already written to - // the profile. If the address was changed by a legacy client, the - // information diverged from the one in this table that is only written by - // new clients. In this case remove the corresponding row from this table. - // Otherwise, read the new structured tokens and set the verification - // statuses for all tokens. - if (street_address == street_address_legacy && - dependent_locality == dependent_locality_legacy && - city == city_legacy && state == state_legacy && - zip_code == zip_code_legacy && sorting_code == sorting_code_legacy && - country == country_legacy) { - profile->SetRawInfoWithVerificationStatusInt( - ADDRESS_HOME_STREET_ADDRESS, street_address, s.ColumnInt(2)); - profile->SetRawInfoWithVerificationStatusInt( - ADDRESS_HOME_STREET_NAME, s.ColumnString16(3), s.ColumnInt(4)); - profile->SetRawInfoWithVerificationStatusInt( - ADDRESS_HOME_DEPENDENT_STREET_NAME, s.ColumnString16(5), - s.ColumnInt(6)); - profile->SetRawInfoWithVerificationStatusInt( - ADDRESS_HOME_HOUSE_NUMBER, s.ColumnString16(7), s.ColumnInt(8)); - profile->SetRawInfoWithVerificationStatusInt( - ADDRESS_HOME_SUBPREMISE, s.ColumnString16(9), s.ColumnInt(10)); - profile->SetRawInfoWithVerificationStatusInt( - ADDRESS_HOME_PREMISE_NAME, s.ColumnString16(11), s.ColumnInt(12)); - profile->SetRawInfoWithVerificationStatusInt( - ADDRESS_HOME_DEPENDENT_LOCALITY, dependent_locality, - s.ColumnInt(14)); - profile->SetRawInfoWithVerificationStatusInt(ADDRESS_HOME_CITY, city, - s.ColumnInt(16)); - profile->SetRawInfoWithVerificationStatusInt(ADDRESS_HOME_STATE, state, - s.ColumnInt(18)); - profile->SetRawInfoWithVerificationStatusInt(ADDRESS_HOME_ZIP, zip_code, - s.ColumnInt(20)); - profile->SetRawInfoWithVerificationStatusInt( - ADDRESS_HOME_SORTING_CODE, sorting_code, s.ColumnInt(22)); - profile->SetRawInfoWithVerificationStatusInt(ADDRESS_HOME_COUNTRY, - country, s.ColumnInt(24)); - profile->SetRawInfoWithVerificationStatusInt( - ADDRESS_HOME_APT_NUM, s.ColumnString16(25), s.ColumnInt(26)); + return true; + } + sql::Statement s; + if (SelectByGuid(db, s, kAutofillProfileAddressesTable, + {kGuid, + kStreetAddress, + kStreetAddressStatus, + kStreetName, + kStreetNameStatus, + kDependentStreetName, + kDependentStreetNameStatus, + kHouseNumber, + kHouseNumberStatus, + kSubpremise, + kSubpremiseStatus, + kPremiseName, + kPremiseNameStatus, + kDependentLocality, + kDependentLocalityStatus, + kCity, + kCityStatus, + kState, + kStateStatus, + kZipCode, + kZipCodeStatus, + kSortingCode, + kSortingCodeStatus, + kCountryCode, + kCountryCodeStatus, + kApartmentNumber, + kApartmentNumberStatus, + kFloor, + kFloorStatus}, + profile->guid())) { + DCHECK_EQ(profile->guid(), s.ColumnString(0)); + std::u16string street_address = s.ColumnString16(1); + std::u16string dependent_locality = s.ColumnString16(13); + std::u16string city = s.ColumnString16(15); + std::u16string state = s.ColumnString16(17); + std::u16string zip_code = s.ColumnString16(19); + std::u16string sorting_code = s.ColumnString16(21); + std::u16string country = s.ColumnString16(23); + + std::u16string street_address_legacy = + profile->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS); + std::u16string dependent_locality_legacy = + profile->GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY); + std::u16string city_legacy = profile->GetRawInfo(ADDRESS_HOME_CITY); + std::u16string state_legacy = profile->GetRawInfo(ADDRESS_HOME_STATE); + std::u16string zip_code_legacy = profile->GetRawInfo(ADDRESS_HOME_ZIP); + std::u16string sorting_code_legacy = + profile->GetRawInfo(ADDRESS_HOME_SORTING_CODE); + std::u16string country_legacy = profile->GetRawInfo(ADDRESS_HOME_COUNTRY); + + // At this stage, the unstructured address was already written to + // the profile. If the address was changed by a legacy client, the + // information diverged from the one in this table that is only written by + // new clients. In this case remove the corresponding row from this table. + // Otherwise, read the new structured tokens and set the verification + // statuses for all tokens. + if (street_address == street_address_legacy && + dependent_locality == dependent_locality_legacy && + city == city_legacy && state == state_legacy && + zip_code == zip_code_legacy && sorting_code == sorting_code_legacy && + country == country_legacy) { + int index = 1; + for (ServerFieldType type : + {ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_STREET_NAME, + ADDRESS_HOME_DEPENDENT_STREET_NAME, ADDRESS_HOME_HOUSE_NUMBER, + ADDRESS_HOME_SUBPREMISE, ADDRESS_HOME_PREMISE_NAME, + ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY, + ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, + ADDRESS_HOME_COUNTRY, ADDRESS_HOME_APT_NUM, ADDRESS_HOME_FLOOR}) { profile->SetRawInfoWithVerificationStatusInt( - ADDRESS_HOME_FLOOR, s.ColumnString16(27), s.ColumnInt(28)); - } else { - // Remove the structured information from the table for - // eventual deletion consistency. - sql::Statement s1(db->GetUniqueStatement( - "DELETE FROM autofill_profile_addresses WHERE guid = ?")); - s1.BindString(0, profile->guid()); - s1.Run(); + type, s.ColumnString16(index), s.ColumnInt(index + 1)); + index += 2; } + } else { + // Remove the structured information from the table for + // eventual deletion consistency. + DeleteWhereColumnEq(db, kAutofillProfileAddressesTable, kGuid, + profile->guid()); } - return s.Succeeded(); } - return true; + return s.Succeeded(); } bool AddAutofillProfileEmailsToProfile(sql::Database* db, AutofillProfile* profile) { // TODO(estade): update schema so that multiple emails are not associated // per unique profile guid. Please refer https://crbug.com/497934. - sql::Statement s(db->GetUniqueStatement( - "SELECT guid, email FROM autofill_profile_emails WHERE guid=? LIMIT 1")); - s.BindString(0, profile->guid()); - - if (!s.is_valid()) - return false; - - if (s.Step()) { + sql::Statement s; + if (SelectByGuid(db, s, kAutofillProfileEmailsTable, {kGuid, kEmail}, + profile->guid())) { DCHECK_EQ(profile->guid(), s.ColumnString(0)); profile->SetRawInfo(EMAIL_ADDRESS, s.ColumnString16(1)); } @@ -506,14 +897,9 @@ bool AddAutofillProfilePhonesToProfile(sql::Database* db, // TODO(estade): update schema so that multiple phone numbers are not // associated per unique profile guid. Please refer // https://crbug.com/497934. - sql::Statement s(db->GetUniqueStatement( - "SELECT guid, number FROM autofill_profile_phones WHERE guid=? LIMIT 1")); - s.BindString(0, profile->guid()); - - if (!s.is_valid()) - return false; - - if (s.Step()) { + sql::Statement s; + if (SelectByGuid(db, s, kAutofillProfilePhonesTable, {kGuid, kNumber}, + profile->guid())) { DCHECK_EQ(profile->guid(), s.ColumnString(0)); profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(1)); } @@ -522,24 +908,13 @@ bool AddAutofillProfilePhonesToProfile(sql::Database* db, bool AddAutofillProfileBirthdateToProfile(sql::Database* db, AutofillProfile* profile) { - if (!base::FeatureList::IsEnabled( - features::kAutofillEnableCompatibilitySupportForBirthdates)) { - return true; - } - - sql::Statement s(db->GetUniqueStatement( - "SELECT guid, day, month, year FROM autofill_profile_birthdates WHERE " - "guid=? LIMIT 1")); - s.BindString(0, profile->guid()); - - if (!s.is_valid()) - return false; - - if (s.Step()) { + sql::Statement s; + if (SelectByGuid(db, s, kAutofillProfileBirthdatesTable, + {kGuid, kDay, kMonth, kYear}, profile->guid())) { DCHECK_EQ(profile->guid(), s.ColumnString(0)); profile->SetRawInfoAsInt(BIRTHDATE_DAY, s.ColumnInt(1)); profile->SetRawInfoAsInt(BIRTHDATE_MONTH, s.ColumnInt(2)); - profile->SetRawInfoAsInt(BIRTHDATE_YEAR_4_DIGITS, s.ColumnInt(3)); + profile->SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, s.ColumnInt(3)); } return s.Succeeded(); } @@ -547,8 +922,8 @@ bool AddAutofillProfileBirthdateToProfile(sql::Database* db, bool AddAutofillProfileEmails(const AutofillProfile& profile, sql::Database* db) { // Add the new email. - sql::Statement s(db->GetUniqueStatement( - "INSERT INTO autofill_profile_emails (guid, email) VALUES (?,?)")); + sql::Statement s; + InsertBuilder(db, s, kAutofillProfileEmailsTable, {kGuid, kEmail}); s.BindString(0, profile.guid()); s.BindString16(1, profile.GetRawInfo(EMAIL_ADDRESS)); @@ -558,8 +933,8 @@ bool AddAutofillProfileEmails(const AutofillProfile& profile, bool AddAutofillProfilePhones(const AutofillProfile& profile, sql::Database* db) { // Add the new number. - sql::Statement s(db->GetUniqueStatement( - "INSERT INTO autofill_profile_phones (guid, number) VALUES (?,?)")); + sql::Statement s; + InsertBuilder(db, s, kAutofillProfilePhonesTable, {kGuid, kNumber}); s.BindString(0, profile.guid()); s.BindString16(1, profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); @@ -568,77 +943,33 @@ bool AddAutofillProfilePhones(const AutofillProfile& profile, bool AddAutofillProfileBirthdate(const AutofillProfile& profile, sql::Database* db) { - if (!base::FeatureList::IsEnabled( - features::kAutofillEnableCompatibilitySupportForBirthdates)) { - return true; - } - // Add the new birthdate. - sql::Statement s( - db->GetUniqueStatement("INSERT INTO autofill_profile_birthdates (guid, " - "day, month, year) VALUES (?,?,?,?)")); + sql::Statement s; + InsertBuilder(db, s, kAutofillProfileBirthdatesTable, + {kGuid, kDay, kMonth, kYear}); s.BindString(0, profile.guid()); s.BindInt(1, profile.GetRawInfoAsInt(BIRTHDATE_DAY)); s.BindInt(2, profile.GetRawInfoAsInt(BIRTHDATE_MONTH)); - s.BindInt(3, profile.GetRawInfoAsInt(BIRTHDATE_YEAR_4_DIGITS)); + s.BindInt(3, profile.GetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR)); return s.Run(); } bool AddAutofillProfilePieces(const AutofillProfile& profile, sql::Database* db) { - if (!AddAutofillProfileNames(profile, db)) - return false; - - if (!AddAutofillProfileEmails(profile, db)) - return false; - - if (!AddAutofillProfilePhones(profile, db)) - return false; - - if (!AddAutofillProfileAddresses(profile, db)) - return false; - - if (!AddAutofillProfileBirthdate(profile, db)) - return false; - - return true; + return AddAutofillProfileNames(profile, db) && + AddAutofillProfileEmails(profile, db) && + AddAutofillProfilePhones(profile, db) && + AddAutofillProfileAddresses(profile, db) && + AddAutofillProfileBirthdate(profile, db); } bool RemoveAutofillProfilePieces(const std::string& guid, sql::Database* db) { - sql::Statement s1(db->GetUniqueStatement( - "DELETE FROM autofill_profile_names WHERE guid = ?")); - s1.BindString(0, guid); - - if (!s1.Run()) - return false; - - sql::Statement s2(db->GetUniqueStatement( - "DELETE FROM autofill_profile_emails WHERE guid = ?")); - s2.BindString(0, guid); - - if (!s2.Run()) - return false; - - sql::Statement s3(db->GetUniqueStatement( - "DELETE FROM autofill_profile_phones WHERE guid = ?")); - s3.BindString(0, guid); - - if (!s3.Run()) - return false; - - sql::Statement s4(db->GetUniqueStatement( - "DELETE FROM autofill_profile_addresses WHERE guid = ?")); - s4.BindString(0, guid); - - if (!s4.Run()) - return false; - - sql::Statement s5(db->GetUniqueStatement( - "DELETE FROM autofill_profile_birthdates WHERE guid = ?")); - s5.BindString(0, guid); - - return s5.Run(); + return DeleteWhereColumnEq(db, kAutofillProfileNamesTable, kGuid, guid) && + DeleteWhereColumnEq(db, kAutofillProfileEmailsTable, kGuid, guid) && + DeleteWhereColumnEq(db, kAutofillProfilePhonesTable, kGuid, guid) && + DeleteWhereColumnEq(db, kAutofillProfileAddressesTable, kGuid, guid) && + DeleteWhereColumnEq(db, kAutofillProfileBirthdatesTable, kGuid, guid); } WebDatabaseTable::TypeKey GetKey() { @@ -699,16 +1030,17 @@ WebDatabaseTable::TypeKey AutofillTable::GetTypeKey() const { } bool AutofillTable::CreateTablesIfNecessary() { - return (InitMainTable() && InitCreditCardsTable() && InitProfilesTable() && - InitProfileAddressesTable() && InitProfileNamesTable() && - InitProfileEmailsTable() && InitProfilePhonesTable() && - InitProfileBirthdatesTable() && InitMaskedCreditCardsTable() && - InitUnmaskedCreditCardsTable() && InitServerCardMetadataTable() && - InitServerAddressesTable() && InitServerAddressMetadataTable() && - InitAutofillSyncMetadataTable() && InitModelTypeStateTable() && - InitPaymentsCustomerDataTable() && InitPaymentsUPIVPATable() && - InitServerCreditCardCloudTokenDataTable() && InitOfferDataTable() && - InitOfferEligibleInstrumentTable() && InitOfferMerchantDomainTable()); + return InitMainTable() && InitCreditCardsTable() && InitIBANsTable() && + InitProfilesTable() && InitProfileAddressesTable() && + InitProfileNamesTable() && InitProfileEmailsTable() && + InitProfilePhonesTable() && InitProfileBirthdatesTable() && + InitMaskedCreditCardsTable() && InitUnmaskedCreditCardsTable() && + InitServerCardMetadataTable() && InitServerAddressesTable() && + InitServerAddressMetadataTable() && InitAutofillSyncMetadataTable() && + InitModelTypeStateTable() && InitPaymentsCustomerDataTable() && + InitPaymentsUPIVPATable() && + InitServerCreditCardCloudTokenDataTable() && InitOfferDataTable() && + InitOfferEligibleInstrumentTable() && InitOfferMerchantDomainTable(); } bool AutofillTable::IsSyncable() { @@ -719,69 +1051,6 @@ bool AutofillTable::MigrateToVersion(int version, bool* update_compatible_version) { // Migrate if necessary. switch (version) { - case 54: - *update_compatible_version = true; - return MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields(); - case 55: - *update_compatible_version = true; - return MigrateToVersion55MergeAutofillDatesTable(); - case 56: - *update_compatible_version = true; - return MigrateToVersion56AddProfileLanguageCodeForFormatting(); - case 57: - *update_compatible_version = true; - return MigrateToVersion57AddFullNameField(); - case 60: - *update_compatible_version = false; - return MigrateToVersion60AddServerCards(); - case 61: - *update_compatible_version = false; - return MigrateToVersion61AddUsageStats(); - case 62: - *update_compatible_version = false; - return MigrateToVersion62AddUsageStatsForUnmaskedCards(); - case 63: - *update_compatible_version = false; - return MigrateToVersion63AddServerRecipientName(); - case 64: - *update_compatible_version = false; - return MigrateToVersion64AddUnmaskDate(); - case 65: - *update_compatible_version = false; - return MigrateToVersion65AddServerMetadataTables(); - case 66: - *update_compatible_version = false; - return MigrateToVersion66AddCardBillingAddress(); - case 67: - *update_compatible_version = false; - return MigrateToVersion67AddMaskedCardBillingAddress(); - case 70: - *update_compatible_version = false; - return MigrateToVersion70AddSyncMetadata(); - case 71: - *update_compatible_version = true; - return MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata(); - case 72: - *update_compatible_version = true; - return MigrateToVersion72RenameCardTypeToIssuerNetwork(); - case 73: - *update_compatible_version = false; - return MigrateToVersion73AddMaskedCardBankName(); - case 74: - *update_compatible_version = false; - return MigrateToVersion74AddServerCardTypeColumn(); - case 75: - *update_compatible_version = false; - return MigrateToVersion75AddProfileValidityBitfieldColumn(); - case 78: - *update_compatible_version = true; - return MigrateToVersion78AddModelTypeColumns(); - case 80: - *update_compatible_version = true; - return MigrateToVersion80AddIsClientValidityStatesUpdatedColumn(); - case 81: - *update_compatible_version = true; - return MigrateToVersion81CleanUpWrongModelTypeData(); case 83: *update_compatible_version = true; return MigrateToVersion83RemoveServerCardTypeColumn(); @@ -844,6 +1113,9 @@ bool AutofillTable::MigrateToVersion(int version, case 104: *update_compatible_version = false; return MigrateToVersion104AddProductDescriptionColumn(); + case 105: + *update_compatible_version = false; + return MigrateToVersion105AddAutofillIBANTable(); } return true; } @@ -869,10 +1141,9 @@ bool AutofillTable::GetFormValuesForElementName( if (prefix.empty()) { sql::Statement s; - s.Assign(db_->GetUniqueStatement( - "SELECT name, value, date_created, date_last_used FROM autofill " - "WHERE name = ? " - "ORDER BY count DESC LIMIT ?")); + SelectBuilder(db_, s, kAutofillTable, + {kName, kValue, kDateCreated, kDateLastUsed}, + "WHERE name = ? ORDER BY count DESC LIMIT ?"); s.BindString16(0, name); s.BindInt(1, limit); @@ -892,13 +1163,13 @@ bool AutofillTable::GetFormValuesForElementName( next_prefix.back()++; sql::Statement s1; - s1.Assign(db_->GetUniqueStatement( - "SELECT name, value, date_created, date_last_used FROM autofill " - "WHERE name = ? AND " - "value_lower >= ? AND " - "value_lower < ? " - "ORDER BY count DESC " - "LIMIT ?")); + SelectBuilder(db_, s1, kAutofillTable, + {kName, kValue, kDateCreated, kDateLastUsed}, + "WHERE name = ? AND " + "value_lower >= ? AND " + "value_lower < ? " + "ORDER BY count DESC " + "LIMIT ?"); s1.BindString16(0, name); s1.BindString16(1, prefix_lower); s1.BindString16(2, next_prefix); @@ -917,17 +1188,17 @@ bool AutofillTable::GetFormValuesForElementName( if (IsFeatureSubstringMatchEnabled()) { sql::Statement s2; - s2.Assign(db_->GetUniqueStatement( - "SELECT name, value, date_created, date_last_used FROM autofill " - "WHERE name = ? AND (" - " value LIKE '% ' || :prefix || '%' ESCAPE '!' OR " - " value LIKE '%.' || :prefix || '%' ESCAPE '!' OR " - " value LIKE '%,' || :prefix || '%' ESCAPE '!' OR " - " value LIKE '%-' || :prefix || '%' ESCAPE '!' OR " - " value LIKE '%@' || :prefix || '%' ESCAPE '!' OR " - " value LIKE '%!_' || :prefix || '%' ESCAPE '!' ) " - "ORDER BY count DESC " - "LIMIT ?")); + SelectBuilder(db_, s2, kAutofillTable, + {kName, kValue, kDateCreated, kDateLastUsed}, + "WHERE name = ? AND (" + " value LIKE '% ' || :prefix || '%' ESCAPE '!' OR " + " value LIKE '%.' || :prefix || '%' ESCAPE '!' OR " + " value LIKE '%,' || :prefix || '%' ESCAPE '!' OR " + " value LIKE '%-' || :prefix || '%' ESCAPE '!' OR " + " value LIKE '%@' || :prefix || '%' ESCAPE '!' OR " + " value LIKE '%!_' || :prefix || '%' ESCAPE '!' ) " + "ORDER BY count DESC " + "LIMIT ?"); s2.BindString16(0, name); // escaper as L'!' -> 0x21. @@ -957,10 +1228,11 @@ bool AutofillTable::RemoveFormElementsAddedBetween( // Query for the name, value, count, and access dates of all form elements // that were used between the given times. - sql::Statement s(db_->GetUniqueStatement( - "SELECT name, value, count, date_created, date_last_used FROM autofill " - "WHERE (date_created >= ? AND date_created < ?) OR " - " (date_last_used >= ? AND date_last_used < ?)")); + sql::Statement s; + SelectBuilder(db_, s, kAutofillTable, + {kName, kValue, kCount, kDateCreated, kDateLastUsed}, + "WHERE (date_created >= ? AND date_created < ?) OR " + " (date_last_used >= ? AND date_last_used < ?)"); s.BindInt64(0, delete_begin_time_t); s.BindInt64(1, delete_end_time_t); s.BindInt64(2, delete_begin_time_t); @@ -1024,8 +1296,9 @@ bool AutofillTable::RemoveFormElementsAddedBetween( return false; // As a single transaction, remove or update the elements appropriately. - sql::Statement s_delete(db_->GetUniqueStatement( - "DELETE FROM autofill WHERE date_created >= ? AND date_last_used < ?")); + sql::Statement s_delete; + DeleteBuilder(db_, s_delete, kAutofillTable, + "date_created >= ? AND date_last_used < ?"); s_delete.BindInt64(0, delete_begin_time_t); s_delete.BindInt64(1, delete_end_time_t); sql::Transaction transaction(db_); @@ -1061,8 +1334,9 @@ bool AutofillTable::RemoveExpiredFormElements( // Query for the name and value of all form elements that were last used // before the |expiration_time|. - sql::Statement select_for_delete(db_->GetUniqueStatement( - "SELECT name, value FROM autofill WHERE date_last_used < ?")); + sql::Statement select_for_delete; + SelectBuilder(db_, select_for_delete, kAutofillTable, {kName, kValue}, + "WHERE date_last_used < ?"); select_for_delete.BindInt64(0, expiration_time.ToTimeT()); std::vector<AutofillChange> tentative_changes; while (select_for_delete.Step()) { @@ -1074,8 +1348,9 @@ bool AutofillTable::RemoveExpiredFormElements( if (!select_for_delete.Succeeded()) return false; - sql::Statement delete_data_statement( - db_->GetUniqueStatement("DELETE FROM autofill WHERE date_last_used < ?")); + sql::Statement delete_data_statement; + DeleteBuilder(db_, delete_data_statement, kAutofillTable, + "date_last_used < ?"); delete_data_statement.BindInt64(0, expiration_time.ToTimeT()); if (!delete_data_statement.Run()) return false; @@ -1086,8 +1361,8 @@ bool AutofillTable::RemoveExpiredFormElements( bool AutofillTable::RemoveFormElement(const std::u16string& name, const std::u16string& value) { - sql::Statement s(db_->GetUniqueStatement( - "DELETE FROM autofill WHERE name = ? AND value= ?")); + sql::Statement s; + DeleteBuilder(db_, s, kAutofillTable, "name = ? AND value= ?"); s.BindString16(0, name); s.BindString16(1, value); return s.Run(); @@ -1116,8 +1391,9 @@ int AutofillTable::GetCountOfValuesContainedBetween(const base::Time& begin, } bool AutofillTable::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) { - sql::Statement s(db_->GetUniqueStatement( - "SELECT name, value, date_created, date_last_used FROM autofill")); + sql::Statement s; + SelectBuilder(db_, s, kAutofillTable, + {kName, kValue, kDateCreated, kDateLastUsed}); while (s.Step()) { std::u16string name = s.ColumnString16(0); @@ -1135,9 +1411,9 @@ bool AutofillTable::GetAutofillTimestamps(const std::u16string& name, const std::u16string& value, base::Time* date_created, base::Time* date_last_used) { - sql::Statement s(db_->GetUniqueStatement( - "SELECT date_created, date_last_used FROM autofill " - "WHERE name = ? AND value = ?")); + sql::Statement s; + SelectBuilder(db_, s, kAutofillTable, {kDateCreated, kDateLastUsed}, + "WHERE name = ? AND value = ?"); s.BindString16(0, name); s.BindString16(1, value); if (!s.Step()) @@ -1157,8 +1433,8 @@ bool AutofillTable::UpdateAutofillEntries( // Remove all existing entries. for (const auto& entry : entries) { - sql::Statement s(db_->GetUniqueStatement( - "DELETE FROM autofill WHERE name = ? AND value = ?")); + sql::Statement s; + DeleteBuilder(db_, s, kAutofillTable, "name = ? AND value = ?"); s.BindString16(0, entry.key().name()); s.BindString16(1, entry.key().value()); if (!s.Run()) @@ -1175,13 +1451,12 @@ bool AutofillTable::UpdateAutofillEntries( } bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) { - sql::Statement s(db_->GetUniqueStatement( - "INSERT INTO autofill_profiles" - "(guid, company_name, street_address, dependent_locality, city, state," - " zipcode, sorting_code, country_code, use_count, use_date, " - " date_modified, origin, language_code, " - " label, disallow_settings_visible_updates) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); + sql::Statement s; + InsertBuilder( + db_, s, kAutofillProfilesTable, + {kGuid, kCompanyName, kStreetAddress, kDependentLocality, kCity, kState, + kZipcode, kSortingCode, kCountryCode, kUseCount, kUseDate, kDateModified, + kOrigin, kLanguageCode, kLabel, kDisallowSettingsVisibleUpdates}); BindAutofillProfileToStatement(profile, AutofillClock::Now(), &s); if (!s.Run()) @@ -1228,26 +1503,20 @@ bool AutofillTable::UpdateAutofillProfile(const AutofillProfile& profile) { bool AutofillTable::RemoveAutofillProfile(const std::string& guid) { DCHECK(base::IsValidGUID(guid)); - sql::Statement s( - db_->GetUniqueStatement("DELETE FROM autofill_profiles WHERE guid = ?")); - s.BindString(0, guid); - - if (!s.Run()) - return false; - - return RemoveAutofillProfilePieces(guid, db_); + return DeleteWhereColumnEq(db_, kAutofillProfilesTable, kGuid, guid) && + RemoveAutofillProfilePieces(guid, db_); } std::unique_ptr<AutofillProfile> AutofillTable::GetAutofillProfile( const std::string& guid) { DCHECK(base::IsValidGUID(guid)); - sql::Statement s(db_->GetUniqueStatement( - "SELECT guid, company_name, street_address, dependent_locality, city," - " state, zipcode, sorting_code, country_code, use_count, use_date," - " date_modified, origin, language_code, label," - " disallow_settings_visible_updates " - "FROM autofill_profiles " - "WHERE guid=?")); + sql::Statement s; + SelectBuilder( + db_, s, kAutofillProfilesTable, + {kGuid, kCompanyName, kStreetAddress, kDependentLocality, kCity, kState, + kZipcode, kSortingCode, kCountryCode, kUseCount, kUseDate, kDateModified, + kOrigin, kLanguageCode, kLabel, kDisallowSettingsVisibleUpdates}, + "WHERE guid=?"); s.BindString(0, guid); if (!s.Step()) @@ -1290,8 +1559,9 @@ bool AutofillTable::GetAutofillProfiles( DCHECK(profiles); profiles->clear(); - sql::Statement s(db_->GetUniqueStatement( - "SELECT guid FROM autofill_profiles ORDER BY date_modified DESC, guid")); + sql::Statement s; + SelectBuilder(db_, s, kAutofillProfilesTable, {kGuid}, + "ORDER BY date_modified DESC, guid"); while (s.Step()) { std::string guid = s.ColumnString(0); @@ -1308,26 +1578,20 @@ bool AutofillTable::GetServerProfiles( std::vector<std::unique_ptr<AutofillProfile>>* profiles) const { profiles->clear(); - sql::Statement s(db_->GetUniqueStatement( - "SELECT " - "id," - "use_count," - "use_date," - "recipient_name," - "company_name," - "street_address," - "address_1," // ADDRESS_HOME_STATE - "address_2," // ADDRESS_HOME_CITY - "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY - "address_4," // Not supported in AutofillProfile yet. - "postal_code," // ADDRESS_HOME_ZIP - "sorting_code," // ADDRESS_HOME_SORTING_CODE - "country_code," // ADDRESS_HOME_COUNTRY - "phone_number," // PHONE_HOME_WHOLE_NUMBER - "language_code, " - "has_converted " - "FROM server_addresses addresses " - "LEFT OUTER JOIN server_address_metadata USING (id)")); + sql::Statement s; + SelectBuilder( + db_, s, kServerAddressesTable, + {kId, kUseCount, kUseDate, kRecipientName, kCompanyName, kStreetAddress, + kAddress1, // ADDRESS_HOME_STATE + kAddress2, // ADDRESS_HOME_CITY + kAddress3, // ADDRESS_HOME_DEPENDENT_LOCALITY + kAddress4, // Not supported in AutofillProfile yet. + kPostalCode, // ADDRESS_HOME_ZIP + kSortingCode, // ADDRESS_HOME_SORTING_CODE + kCountryCode, // ADDRESS_HOME_COUNTRY + kPhoneNumber, // PHONE_HOME_WHOLE_NUMBER + kLanguageCode, kHasConverted}, + "LEFT OUTER JOIN server_address_metadata USING (id)"); while (s.Step()) { int index = 0; @@ -1342,16 +1606,16 @@ bool AutofillTable::GetServerProfiles( profile->set_modification_date(base::Time()); std::u16string recipient_name = s.ColumnString16(index++); - profile->SetRawInfo(COMPANY_NAME, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, - s.ColumnString16(index++)); + for (ServerFieldType type : + {COMPANY_NAME, ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_STATE, + ADDRESS_HOME_CITY, ADDRESS_HOME_DEPENDENT_LOCALITY}) { + profile->SetRawInfo(type, s.ColumnString16(index++)); + } index++; // Skip address_4 which we haven't added to AutofillProfile yet. - profile->SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_SORTING_CODE, s.ColumnString16(index++)); - profile->SetRawInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(index++)); + for (ServerFieldType type : + {ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY}) { + profile->SetRawInfo(type, s.ColumnString16(index++)); + } std::u16string phone_number = s.ColumnString16(index++); profile->set_language_code(s.ColumnString(index++)); profile->set_has_converted(s.ColumnBool(index++)); @@ -1372,76 +1636,148 @@ bool AutofillTable::GetServerProfiles( return s.Succeeded(); } -void AutofillTable::SetServerProfiles( - const std::vector<AutofillProfile>& profiles) { +void AutofillTable::SetServerProfilesAndMetadata( + const std::vector<AutofillProfile>& profiles, + bool update_metadata) { sql::Transaction transaction(db_); if (!transaction.Begin()) return; // Delete all old ones first. - sql::Statement delete_old( - db_->GetUniqueStatement("DELETE FROM server_addresses")); - delete_old.Run(); - - sql::Statement insert(db_->GetUniqueStatement( - "INSERT INTO server_addresses(" - "id," - "recipient_name," - "company_name," - "street_address," - "address_1," // ADDRESS_HOME_STATE - "address_2," // ADDRESS_HOME_CITY - "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY - "address_4," // Not supported in AutofillProfile yet. - "postal_code," // ADDRESS_HOME_ZIP - "sorting_code," // ADDRESS_HOME_SORTING_CODE - "country_code," // ADDRESS_HOME_COUNTRY - "phone_number," // PHONE_HOME_WHOLE_NUMBER - "language_code) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")); + Delete(db_, kServerAddressesTable); + + sql::Statement insert; + InsertBuilder(db_, insert, kServerAddressesTable, + {kId, kRecipientName, kCompanyName, kStreetAddress, + kAddress1, // ADDRESS_HOME_STATE + kAddress2, // ADDRESS_HOME_CITY + kAddress3, // ADDRESS_HOME_DEPENDENT_LOCALITY + kAddress4, // Not supported in AutofillProfile yet. + kPostalCode, // ADDRESS_HOME_ZIP + kSortingCode, // ADDRESS_HOME_SORTING_CODE + kCountryCode, // ADDRESS_HOME_COUNTRY + kPhoneNumber, // PHONE_HOME_WHOLE_NUMBER + kLanguageCode}); for (const auto& profile : profiles) { DCHECK(profile.record_type() == AutofillProfile::SERVER_PROFILE); int index = 0; insert.BindString(index++, profile.server_id()); - insert.BindString16(index++, profile.GetRawInfo(NAME_FULL)); - insert.BindString16(index++, profile.GetRawInfo(COMPANY_NAME)); - insert.BindString16(index++, - profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_STATE)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_CITY)); - insert.BindString16(index++, - profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY)); - index++; // SKip address_4 which we haven't added to AutofillProfile yet. - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_ZIP)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_COUNTRY)); - insert.BindString16(index++, profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); + for (ServerFieldType type : + {NAME_FULL, COMPANY_NAME, ADDRESS_HOME_STREET_ADDRESS, + ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, + ADDRESS_HOME_DEPENDENT_LOCALITY}) { + insert.BindString16(index++, profile.GetRawInfo(type)); + } + index++; // Skip address_4 which we haven't added to AutofillProfile yet. + for (ServerFieldType type : + {ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY, + PHONE_HOME_WHOLE_NUMBER}) { + insert.BindString16(index++, profile.GetRawInfo(type)); + } insert.BindString(index++, profile.language_code()); insert.Run(); insert.Reset(true); - // Save the use count and use date of the profile. - UpdateServerAddressMetadata(profile); + if (update_metadata) { + // Save the use count and use date of the profile. + UpdateServerAddressMetadata(profile); + } } - // Delete metadata that's no longer relevant. - sql::Statement metadata_delete(db_->GetUniqueStatement( - "DELETE FROM server_address_metadata WHERE id NOT IN " - "(SELECT id FROM server_addresses)")); - metadata_delete.Run(); + if (update_metadata) { + // Delete metadata that's no longer relevant. + Delete(db_, kServerAddressMetadataTable, + "id NOT IN (SELECT id FROM server_addresses)"); + } transaction.Commit(); } -bool AutofillTable::AddCreditCard(const CreditCard& credit_card) { +void AutofillTable::SetServerProfiles( + const std::vector<AutofillProfile>& profiles) { + SetServerProfilesAndMetadata(profiles, /*update_metadata=*/true); +} + +bool AutofillTable::AddIBAN(const IBAN& iban) { + sql::Statement s; + InsertBuilder(db_, s, kIBANsTable, + {kGuid, kUseCount, kUseDate, kValue, kNickname}); + BindIBANToStatement(iban, &s, *autofill_table_encryptor_); + if (!s.Run()) + return false; + + DCHECK_GT(db_->GetLastChangeCount(), 0); + return true; +} + +bool AutofillTable::UpdateIBAN(const IBAN& iban) { + DCHECK(base::IsValidGUID(iban.guid())); + + std::unique_ptr<IBAN> old_iban = GetIBAN(iban.guid()); + if (!old_iban) { + return false; + } + + if (*old_iban == iban) { + return true; + } + sql::Statement s(db_->GetUniqueStatement( - "INSERT INTO credit_cards" - "(guid, name_on_card, expiration_month, expiration_year, " - " card_number_encrypted, use_count, use_date, date_modified, origin," - " billing_address_id, nickname)" - "VALUES (?,?,?,?,?,?,?,?,?,?,?)")); + "UPDATE ibans " + "SET guid=?, use_count=?, use_date=?, value=?, nickname=? " + "WHERE guid=?1")); + BindIBANToStatement(iban, &s, *autofill_table_encryptor_); + + bool result = s.Run(); + DCHECK_GT(db_->GetLastChangeCount(), 0); + return result; +} + +bool AutofillTable::RemoveIBAN(const std::string& guid) { + DCHECK(base::IsValidGUID(guid)); + return DeleteWhereColumnEq(db_, kIBANsTable, kGuid, guid); +} + +std::unique_ptr<IBAN> AutofillTable::GetIBAN(const std::string& guid) { + DCHECK(base::IsValidGUID(guid)); + sql::Statement s; + SelectBuilder(db_, s, kIBANsTable, + {kGuid, kUseCount, kUseDate, kValue, kNickname}, + "WHERE guid = ?"); + s.BindString(0, guid); + + if (!s.Step()) + return nullptr; + + return IBANFromStatement(s, *autofill_table_encryptor_); +} + +bool AutofillTable::GetIBANs(std::vector<std::unique_ptr<IBAN>>* ibans) { + DCHECK(ibans); + ibans->clear(); + + sql::Statement s; + SelectBuilder(db_, s, kIBANsTable, {kGuid}, "ORDER BY use_date DESC, guid"); + + while (s.Step()) { + std::string guid = s.ColumnString(0); + std::unique_ptr<IBAN> iban = GetIBAN(guid); + if (!iban) + return false; + ibans->push_back(std::move(iban)); + } + + return s.Succeeded(); +} + +bool AutofillTable::AddCreditCard(const CreditCard& credit_card) { + sql::Statement s; + InsertBuilder(db_, s, kCreditCardsTable, + {kGuid, kNameOnCard, kExpirationMonth, kExpirationYear, + kCardNumberEncrypted, kUseCount, kUseDate, kDateModified, + kOrigin, kBillingAddressId, kNickname}); BindCreditCardToStatement(credit_card, AutofillClock::Now(), &s, *autofill_table_encryptor_); @@ -1481,11 +1817,7 @@ bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) { bool AutofillTable::RemoveCreditCard(const std::string& guid) { DCHECK(base::IsValidGUID(guid)); - sql::Statement s( - db_->GetUniqueStatement("DELETE FROM credit_cards WHERE guid = ?")); - s.BindString(0, guid); - - return s.Run(); + return DeleteWhereColumnEq(db_, kCreditCardsTable, kGuid, guid); } bool AutofillTable::AddFullServerCreditCard(const CreditCard& credit_card) { @@ -1518,12 +1850,12 @@ bool AutofillTable::AddFullServerCreditCard(const CreditCard& credit_card) { std::unique_ptr<CreditCard> AutofillTable::GetCreditCard( const std::string& guid) { DCHECK(base::IsValidGUID(guid)); - sql::Statement s(db_->GetUniqueStatement( - "SELECT guid, name_on_card, expiration_month, expiration_year, " - "card_number_encrypted, use_count, use_date, date_modified, " - "origin, billing_address_id, nickname " - "FROM credit_cards " - "WHERE guid = ?")); + sql::Statement s; + SelectBuilder(db_, s, kCreditCardsTable, + {kGuid, kNameOnCard, kExpirationMonth, kExpirationYear, + kCardNumberEncrypted, kUseCount, kUseDate, kDateModified, + kOrigin, kBillingAddressId, kNickname}, + "WHERE guid = ?"); s.BindString(0, guid); if (!s.Step()) @@ -1537,8 +1869,9 @@ bool AutofillTable::GetCreditCards( DCHECK(credit_cards); credit_cards->clear(); - sql::Statement s(db_->GetUniqueStatement( - "SELECT guid FROM credit_cards ORDER BY date_modified DESC, guid")); + sql::Statement s; + SelectBuilder(db_, s, kCreditCardsTable, {kGuid}, + "ORDER BY date_modified DESC, guid"); while (s.Step()) { std::string guid = s.ColumnString(0); @@ -1555,28 +1888,17 @@ bool AutofillTable::GetServerCreditCards( std::vector<std::unique_ptr<CreditCard>>* credit_cards) const { credit_cards->clear(); - sql::Statement s(db_->GetUniqueStatement( - "SELECT " - "card_number_encrypted, " // 0 - "last_four," // 1 - "masked.id," // 2 - "metadata.use_count," // 3 - "metadata.use_date," // 4 - "network," // 5 - "name_on_card," // 6 - "exp_month," // 7 - "exp_year," // 8 - "metadata.billing_address_id," // 9 - "bank_name," // 10 - "nickname," // 11 - "card_issuer," // 12 - "instrument_id, " // 13 - "virtual_card_enrollment_state, " // 14 - "card_art_url, " // 15 - "product_description " // 16 - "FROM masked_credit_cards masked " + sql::Statement s; + SelectBuilder( + db_, s, base::StrCat({kMaskedCreditCardsTable, " AS masked"}), + {kCardNumberEncrypted, kLastFour, base::StrCat({"masked.", kId}), + base::StrCat({"metadata.", kUseCount}), + base::StrCat({"metadata.", kUseDate}), kNetwork, kNameOnCard, kExpMonth, + kExpYear, base::StrCat({"metadata.", kBillingAddressId}), kBankName, + kNickname, kCardIssuer, kInstrumentId, kVirtualCardEnrollmentState, + kCardArtUrl, kProductDescription}, "LEFT OUTER JOIN unmasked_credit_cards USING (id) " - "LEFT OUTER JOIN server_card_metadata metadata USING (id)")); + "LEFT OUTER JOIN server_card_metadata AS metadata USING (id)"); while (s.Step()) { int index = 0; @@ -1636,22 +1958,16 @@ void AutofillTable::SetServerCreditCards( return; // Delete all old values. - sql::Statement masked_delete( - db_->GetUniqueStatement("DELETE FROM masked_credit_cards")); - masked_delete.Run(); + Delete(db_, kMaskedCreditCardsTable); AddMaskedCreditCards(credit_cards); // Delete all items in the unmasked table that aren't in the new set. - sql::Statement unmasked_delete(db_->GetUniqueStatement( - "DELETE FROM unmasked_credit_cards WHERE id NOT IN " - "(SELECT id FROM masked_credit_cards)")); - unmasked_delete.Run(); + Delete(db_, kUnmaskedCreditCardsTable, + "id NOT IN (SELECT id FROM masked_credit_cards)"); // Do the same for metadata. - sql::Statement metadata_delete(db_->GetUniqueStatement( - "DELETE FROM server_card_metadata WHERE id NOT IN " - "(SELECT id FROM masked_credit_cards)")); - metadata_delete.Run(); + Delete(db_, kServerCardMetadataTable, + "id NOT IN (SELECT id FROM masked_credit_cards)"); transaction.Commit(); } @@ -1684,10 +2000,9 @@ bool AutofillTable::MaskServerCreditCard(const std::string& id) { bool AutofillTable::AddServerCardMetadata( const AutofillMetadata& card_metadata) { - sql::Statement s( - db_->GetUniqueStatement("INSERT INTO server_card_metadata(use_count, " - "use_date, billing_address_id, id)" - "VALUES (?,?,?,?)")); + sql::Statement s; + InsertBuilder(db_, s, kServerCardMetadataTable, + {kUseCount, kUseDate, kBillingAddressId, kId}); s.BindInt64(0, card_metadata.use_count); s.BindInt64(1, card_metadata.use_date.ToInternalValue()); s.BindString(2, card_metadata.billing_address_id); @@ -1700,15 +2015,12 @@ bool AutofillTable::AddServerCardMetadata( bool AutofillTable::UpdateServerCardMetadata(const CreditCard& credit_card) { DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); - sql::Statement remove( - db_->GetUniqueStatement("DELETE FROM server_card_metadata WHERE id = ?")); - remove.BindString(0, credit_card.server_id()); - remove.Run(); + DeleteWhereColumnEq(db_, kServerCardMetadataTable, kId, + credit_card.server_id()); - sql::Statement s( - db_->GetUniqueStatement("INSERT INTO server_card_metadata(use_count, " - "use_date, billing_address_id, id)" - "VALUES (?,?,?,?)")); + sql::Statement s; + InsertBuilder(db_, s, kServerCardMetadataTable, + {kUseCount, kUseDate, kBillingAddressId, kId}); s.BindInt64(0, credit_card.use_count()); s.BindInt64(1, credit_card.use_date().ToInternalValue()); s.BindString(2, credit_card.billing_address_id()); @@ -1723,10 +2035,9 @@ bool AutofillTable::UpdateServerCardMetadata( // Do not check if there was a record that got deleted. Inserting a new one is // also fine. RemoveServerCardMetadata(card_metadata.id); - sql::Statement s( - db_->GetUniqueStatement("INSERT INTO server_card_metadata(use_count, " - "use_date, billing_address_id, id)" - "VALUES (?,?,?,?)")); + sql::Statement s; + InsertBuilder(db_, s, kServerCardMetadataTable, + {kUseCount, kUseDate, kBillingAddressId, kId}); s.BindInt64(0, card_metadata.use_count); s.BindInt64(1, card_metadata.use_date.ToInternalValue()); s.BindString(2, card_metadata.billing_address_id); @@ -1737,11 +2048,7 @@ bool AutofillTable::UpdateServerCardMetadata( } bool AutofillTable::RemoveServerCardMetadata(const std::string& id) { - sql::Statement remove( - db_->GetUniqueStatement("DELETE FROM server_card_metadata WHERE id = ?")); - remove.BindString(0, id); - remove.Run(); - + DeleteWhereColumnEq(db_, kServerCardMetadataTable, kId, id); return db_->GetLastChangeCount() > 0; } @@ -1749,9 +2056,9 @@ bool AutofillTable::GetServerCardsMetadata( std::map<std::string, AutofillMetadata>* cards_metadata) const { cards_metadata->clear(); - sql::Statement s( - db_->GetUniqueStatement("SELECT id, use_count, use_date, " - "billing_address_id FROM server_card_metadata")); + sql::Statement s; + SelectBuilder(db_, s, kServerCardMetadataTable, + {kId, kUseCount, kUseDate, kBillingAddressId}); while (s.Step()) { int index = 0; @@ -1769,10 +2076,9 @@ bool AutofillTable::GetServerCardsMetadata( bool AutofillTable::AddServerAddressMetadata( const AutofillMetadata& address_metadata) { - sql::Statement s( - db_->GetUniqueStatement("INSERT INTO server_address_metadata(use_count, " - "use_date, has_converted, id)" - "VALUES (?,?,?,?)")); + sql::Statement s; + InsertBuilder(db_, s, kServerAddressMetadataTable, + {kUseCount, kUseDate, kHasConverted, kId}); s.BindInt64(0, address_metadata.use_count); s.BindInt64(1, address_metadata.use_date.ToInternalValue()); s.BindBool(2, address_metadata.has_converted); @@ -1792,15 +2098,13 @@ bool AutofillTable::UpdateServerAddressMetadata( if (!transaction.Begin()) return false; - sql::Statement remove(db_->GetUniqueStatement( - "DELETE FROM server_address_metadata WHERE id = ?")); - remove.BindString(0, profile.server_id()); - remove.Run(); + DeleteWhereColumnEq(db_, kServerAddressMetadataTable, kId, + profile.server_id()); + + sql::Statement s; + InsertBuilder(db_, s, kServerAddressMetadataTable, + {kUseCount, kUseDate, kHasConverted, kId}); - sql::Statement s( - db_->GetUniqueStatement("INSERT INTO server_address_metadata(use_count, " - "use_date, has_converted, id)" - "VALUES (?,?,?,?)")); s.BindInt64(0, profile.use_count()); s.BindInt64(1, profile.use_date().ToInternalValue()); s.BindBool(2, profile.has_converted()); @@ -1817,10 +2121,9 @@ bool AutofillTable::UpdateServerAddressMetadata( // Do not check if there was a record that got deleted. Inserting a new one is // also fine. RemoveServerAddressMetadata(address_metadata.id); - sql::Statement s( - db_->GetUniqueStatement("INSERT INTO server_address_metadata(use_count, " - "use_date, has_converted, id)" - "VALUES (?,?,?,?)")); + sql::Statement s; + InsertBuilder(db_, s, kServerAddressMetadataTable, + {kUseCount, kUseDate, kHasConverted, kId}); s.BindInt64(0, address_metadata.use_count); s.BindInt64(1, address_metadata.use_date.ToInternalValue()); s.BindBool(2, address_metadata.has_converted); @@ -1831,11 +2134,7 @@ bool AutofillTable::UpdateServerAddressMetadata( } bool AutofillTable::RemoveServerAddressMetadata(const std::string& id) { - sql::Statement remove(db_->GetUniqueStatement( - "DELETE FROM server_address_metadata WHERE id = ?")); - remove.BindString(0, id); - remove.Run(); - + DeleteWhereColumnEq(db_, kServerAddressMetadataTable, kId, id); return db_->GetLastChangeCount() > 0; } @@ -1843,9 +2142,9 @@ bool AutofillTable::GetServerAddressesMetadata( std::map<std::string, AutofillMetadata>* addresses_metadata) const { addresses_metadata->clear(); - sql::Statement s( - db_->GetUniqueStatement("SELECT id, use_count, use_date, has_converted " - "FROM server_address_metadata")); + sql::Statement s; + SelectBuilder(db_, s, kServerAddressMetadataTable, + {kId, kUseCount, kUseDate, kHasConverted}); while (s.Step()) { int index = 0; @@ -1867,27 +2166,16 @@ void AutofillTable::SetServerCardsData( return; // Delete all old values. - sql::Statement masked_delete( - db_->GetUniqueStatement("DELETE FROM masked_credit_cards")); - masked_delete.Run(); + Delete(db_, kMaskedCreditCardsTable); // Add all the masked cards. - sql::Statement masked_insert( - db_->GetUniqueStatement("INSERT INTO masked_credit_cards(" - "id," // 0 - "network," // 1 - "name_on_card," // 2 - "last_four," // 3 - "exp_month," // 4 - "exp_year," // 5 - "bank_name," // 6 - "nickname," // 7 - "card_issuer," // 8 - "instrument_id," // 9 - "virtual_card_enrollment_state," // 10 - "card_art_url," // 11 - "product_description) " // 12 - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")); + sql::Statement masked_insert; + InsertBuilder( + db_, masked_insert, kMaskedCreditCardsTable, + {kId, kNetwork, kNameOnCard, kLastFour, kExpMonth, kExpYear, kBankName, + kNickname, kCardIssuer, kInstrumentId, kVirtualCardEnrollmentState, + kCardArtUrl, kProductDescription}); + int index; for (const CreditCard& card : credit_cards) { DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type()); @@ -1912,67 +2200,15 @@ void AutofillTable::SetServerCardsData( } // Delete all items in the unmasked table that aren't in the new set. - sql::Statement unmasked_delete(db_->GetUniqueStatement( - "DELETE FROM unmasked_credit_cards WHERE id NOT IN " - "(SELECT id FROM masked_credit_cards)")); - unmasked_delete.Run(); + Delete(db_, kUnmaskedCreditCardsTable, + "id NOT IN (SELECT id FROM masked_credit_cards)"); transaction.Commit(); } void AutofillTable::SetServerAddressesData( const std::vector<AutofillProfile>& profiles) { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return; - - // Delete existing server addresses. - sql::Statement delete_old( - db_->GetUniqueStatement("DELETE FROM server_addresses")); - delete_old.Run(); - - // Add the new server addresses. - sql::Statement insert(db_->GetUniqueStatement( - "INSERT INTO server_addresses(" - "id," - "recipient_name," - "company_name," - "street_address," - "address_1," // ADDRESS_HOME_STATE - "address_2," // ADDRESS_HOME_CITY - "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY - "address_4," // Not supported in AutofillProfile yet. - "postal_code," // ADDRESS_HOME_ZIP - "sorting_code," // ADDRESS_HOME_SORTING_CODE - "country_code," // ADDRESS_HOME_COUNTRY - "phone_number," // PHONE_HOME_WHOLE_NUMBER - "language_code) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")); - for (const auto& profile : profiles) { - DCHECK(profile.record_type() == AutofillProfile::SERVER_PROFILE); - - int index = 0; - insert.BindString(index++, profile.server_id()); - insert.BindString16(index++, profile.GetRawInfo(NAME_FULL)); - insert.BindString16(index++, profile.GetRawInfo(COMPANY_NAME)); - insert.BindString16(index++, - profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_STATE)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_CITY)); - insert.BindString16(index++, - profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY)); - index++; // SKip address_4 which we haven't added to AutofillProfile yet. - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_ZIP)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_COUNTRY)); - insert.BindString16(index++, profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); - insert.BindString(index++, profile.language_code()); - - insert.Run(); - insert.Reset(true); - } - - transaction.Commit(); + SetServerProfilesAndMetadata(profiles, /*update_metadata=*/false); } void AutofillTable::SetCreditCardCloudTokenData( @@ -1982,20 +2218,13 @@ void AutofillTable::SetCreditCardCloudTokenData( return; // Deletes all old values. - sql::Statement delete_cloud_token( - db_->GetUniqueStatement("DELETE FROM server_card_cloud_token_data")); - delete_cloud_token.Run(); + Delete(db_, kServerCardCloudTokenDataTable); // Inserts new values. - sql::Statement insert_cloud_token( - db_->GetUniqueStatement("INSERT INTO server_card_cloud_token_data(" - "id," // 0 - "suffix," // 1 - "exp_month," // 2 - "exp_year," // 3 - "card_art_url," // 4 - "instrument_token) " // 5 - "VALUES (?,?,?,?,?,?)")); + sql::Statement insert_cloud_token; + InsertBuilder( + db_, insert_cloud_token, kServerCardCloudTokenDataTable, + {kId, kSuffix, kExpMonth, kExpYear, kCardArtUrl, kInstrumentToken}); for (const CreditCardCloudTokenData& data : credit_card_cloud_token_data) { insert_cloud_token.BindString(0, data.masked_card_id); @@ -2015,15 +2244,10 @@ bool AutofillTable::GetCreditCardCloudTokenData( credit_card_cloud_token_data) { credit_card_cloud_token_data->clear(); - sql::Statement s( - db_->GetUniqueStatement("SELECT " - "id, " // 0 - "suffix, " // 1 - "exp_month, " // 2 - "exp_year, " // 3 - "card_art_url, " // 4 - "instrument_token " // 5 - "FROM server_card_cloud_token_data")); + sql::Statement s; + SelectBuilder( + db_, s, kServerCardCloudTokenDataTable, + {kId, kSuffix, kExpMonth, kExpYear, kCardArtUrl, kInstrumentToken}); while (s.Step()) { int index = 0; @@ -2048,13 +2272,12 @@ void AutofillTable::SetPaymentsCustomerData( return; // Delete all old values. - sql::Statement customer_data_delete( - db_->GetUniqueStatement("DELETE FROM payments_customer_data")); - customer_data_delete.Run(); + Delete(db_, kPaymentsCustomerDataTable); if (customer_data) { - sql::Statement insert_customer_data(db_->GetUniqueStatement( - "INSERT INTO payments_customer_data (customer_id) VALUES (?)")); + sql::Statement insert_customer_data; + InsertBuilder(db_, insert_customer_data, kPaymentsCustomerDataTable, + {kCustomerId}); insert_customer_data.BindString(0, customer_data->customer_id); insert_customer_data.Run(); } @@ -2064,8 +2287,8 @@ void AutofillTable::SetPaymentsCustomerData( bool AutofillTable::GetPaymentsCustomerData( std::unique_ptr<PaymentsCustomerData>* customer_data) const { - sql::Statement s(db_->GetUniqueStatement( - "SELECT customer_id FROM payments_customer_data")); + sql::Statement s; + SelectBuilder(db_, s, kPaymentsCustomerDataTable, {kCustomerId}); if (s.Step()) { *customer_data = std::make_unique<PaymentsCustomerData>( /*customer_id=*/s.ColumnString(0)); @@ -2081,28 +2304,16 @@ void AutofillTable::SetAutofillOffers( return; // Delete all old values. - sql::Statement delete_offers( - db_->GetUniqueStatement("DELETE FROM offer_data")); - sql::Statement delete_offer_eligible_instruments( - db_->GetUniqueStatement("DELETE FROM offer_eligible_instrument")); - sql::Statement delete_offer_merchant_domains( - db_->GetUniqueStatement("DELETE FROM offer_merchant_domain")); - delete_offers.Run(); - delete_offer_eligible_instruments.Run(); - delete_offer_merchant_domains.Run(); + Delete(db_, kOfferDataTable); + Delete(db_, kOfferEligibleInstrumentTable); + Delete(db_, kOfferMerchantDomainTable); // Insert new values. - sql::Statement insert_offers( - db_->GetUniqueStatement("INSERT INTO offer_data(" - "offer_id, " // 0 - "offer_reward_amount, " // 1 - "expiry, " // 2 - "offer_details_url, " // 3 - "promo_code, " // 4 - "value_prop_text, " // 5 - "see_details_text, " // 6 - "usage_instructions_text) " // 7 - "VALUES (?,?,?,?,?,?,?,?)")); + sql::Statement insert_offers; + InsertBuilder( + db_, insert_offers, kOfferDataTable, + {kOfferId, kOfferRewardAmount, kExpiry, kOfferDetailsUrl, kPromoCode, + kValuePropText, kSeeDetailsText, kUsageInstructionsText}); for (const AutofillOfferData& data : autofill_offer_data) { insert_offers.BindInt64(0, data.GetOfferId()); @@ -2120,28 +2331,22 @@ void AutofillTable::SetAutofillOffers( for (const int64_t instrument_id : data.GetEligibleInstrumentIds()) { // Insert new offer_eligible_instrument values. - sql::Statement insert_offer_eligible_instruments( - db_->GetUniqueStatement("INSERT INTO offer_eligible_instrument(" - "offer_id, " // 0 - "instrument_id) " // 1 - "VALUES (?,?)")); + sql::Statement insert_offer_eligible_instruments; + InsertBuilder(db_, insert_offer_eligible_instruments, + kOfferEligibleInstrumentTable, {kOfferId, kInstrumentId}); insert_offer_eligible_instruments.BindInt64(0, data.GetOfferId()); insert_offer_eligible_instruments.BindInt64(1, instrument_id); insert_offer_eligible_instruments.Run(); - insert_offer_eligible_instruments.Reset(true); } for (const GURL& merchant_origin : data.GetMerchantOrigins()) { // Insert new offer_merchant_domain values. - sql::Statement insert_offer_merchant_domains( - db_->GetUniqueStatement("INSERT INTO offer_merchant_domain(" - "offer_id, " // 0 - "merchant_domain) " // 1 - "VALUES (?,?)")); + sql::Statement insert_offer_merchant_domains; + InsertBuilder(db_, insert_offer_merchant_domains, + kOfferMerchantDomainTable, {kOfferId, kMerchantDomain}); insert_offer_merchant_domains.BindInt64(0, data.GetOfferId()); insert_offer_merchant_domains.BindString(1, merchant_origin.spec()); insert_offer_merchant_domains.Run(); - insert_offer_merchant_domains.Reset(true); } } transaction.Commit(); @@ -2151,17 +2356,11 @@ bool AutofillTable::GetAutofillOffers( std::vector<std::unique_ptr<AutofillOfferData>>* autofill_offer_data) { autofill_offer_data->clear(); - sql::Statement s( - db_->GetUniqueStatement("SELECT " - "offer_id, " // 0 - "offer_reward_amount, " // 1 - "expiry, " // 2 - "offer_details_url, " // 3 - "promo_code, " // 4 - "value_prop_text, " // 5 - "see_details_text, " // 6 - "usage_instructions_text " // 7 - "FROM offer_data")); + sql::Statement s; + SelectBuilder( + db_, s, kOfferDataTable, + {kOfferId, kOfferRewardAmount, kExpiry, kOfferDetailsUrl, kPromoCode, + kValuePropText, kSeeDetailsText, kUsageInstructionsText}); while (s.Step()) { int index = 0; @@ -2179,12 +2378,10 @@ bool AutofillTable::GetAutofillOffers( std::vector<int64_t> eligible_instrument_id; std::vector<GURL> merchant_origins; - sql::Statement s_offer_eligible_instrument( - db_->GetUniqueStatement("SELECT " - "offer_id, " // 0 - "instrument_id " // 1 - "FROM offer_eligible_instrument " - "WHERE offer_id = ?")); + sql::Statement s_offer_eligible_instrument; + SelectBuilder(db_, s_offer_eligible_instrument, + kOfferEligibleInstrumentTable, {kOfferId, kInstrumentId}, + "WHERE offer_id = ?"); s_offer_eligible_instrument.BindInt64(0, offer_id); while (s_offer_eligible_instrument.Step()) { const int64_t instrument_id = s_offer_eligible_instrument.ColumnInt64(1); @@ -2193,12 +2390,9 @@ bool AutofillTable::GetAutofillOffers( } } - sql::Statement s_offer_merchant_domain( - db_->GetUniqueStatement("SELECT " - "offer_id, " // 0 - "merchant_domain " // 1 - "FROM offer_merchant_domain " - "WHERE offer_id = ?")); + sql::Statement s_offer_merchant_domain; + SelectBuilder(db_, s_offer_merchant_domain, kOfferMerchantDomainTable, + {kOfferId, kMerchantDomain}, "WHERE offer_id = ?"); s_offer_merchant_domain.BindInt64(0, offer_id); while (s_offer_merchant_domain.Step()) { const std::string merchant_domain = @@ -2230,8 +2424,8 @@ bool AutofillTable::InsertUpiId(const std::string& upi_id) { if (!transaction.Begin()) return false; - sql::Statement insert_upi_id_statement( - db_->GetUniqueStatement("INSERT INTO payments_upi_vpa (vpa) VALUES (?)")); + sql::Statement insert_upi_id_statement; + InsertBuilder(db_, insert_upi_id_statement, kPaymentsUpiVpaTable, {kVpa}); insert_upi_id_statement.BindString(0, upi_id); insert_upi_id_statement.Run(); @@ -2241,8 +2435,8 @@ bool AutofillTable::InsertUpiId(const std::string& upi_id) { } std::vector<std::string> AutofillTable::GetAllUpiIds() { - sql::Statement select_upi_id_statement( - db_->GetUniqueStatement("SELECT vpa FROM payments_upi_vpa")); + sql::Statement select_upi_id_statement; + SelectBuilder(db_, select_upi_id_statement, kPaymentsUpiVpaTable, {kVpa}); std::vector<std::string> upi_ids; while (select_upi_id_statement.Step()) { @@ -2256,55 +2450,16 @@ bool AutofillTable::ClearAllServerData() { if (!transaction.Begin()) return false; // Some error, nothing was changed. - sql::Statement masked( - db_->GetUniqueStatement("DELETE FROM masked_credit_cards")); - masked.Run(); - bool changed = db_->GetLastChangeCount() > 0; - - sql::Statement unmasked( - db_->GetUniqueStatement("DELETE FROM unmasked_credit_cards")); - unmasked.Run(); - changed |= db_->GetLastChangeCount() > 0; - - sql::Statement addresses( - db_->GetUniqueStatement("DELETE FROM server_addresses")); - addresses.Run(); - changed |= db_->GetLastChangeCount() > 0; - - sql::Statement card_metadata( - db_->GetUniqueStatement("DELETE FROM server_card_metadata")); - card_metadata.Run(); - changed |= db_->GetLastChangeCount() > 0; - - sql::Statement address_metadata( - db_->GetUniqueStatement("DELETE FROM server_address_metadata")); - address_metadata.Run(); - changed |= db_->GetLastChangeCount() > 0; - - sql::Statement customer_data( - db_->GetUniqueStatement("DELETE FROM payments_customer_data")); - customer_data.Run(); - changed |= db_->GetLastChangeCount() > 0; - - sql::Statement cloud_token_data( - db_->GetUniqueStatement("DELETE FROM server_card_cloud_token_data")); - cloud_token_data.Run(); - changed |= db_->GetLastChangeCount() > 0; - - sql::Statement autofill_offer_data( - db_->GetUniqueStatement("DELETE FROM offer_data")); - autofill_offer_data.Run(); - changed |= db_->GetLastChangeCount() > 0; - - sql::Statement autofill_offer_eligible_instrument( - db_->GetUniqueStatement("DELETE FROM offer_eligible_instrument")); - autofill_offer_eligible_instrument.Run(); - changed |= db_->GetLastChangeCount() > 0; - - sql::Statement autofill_offer_merchant_domain( - db_->GetUniqueStatement("DELETE FROM offer_merchant_domain")); - autofill_offer_merchant_domain.Run(); - changed |= db_->GetLastChangeCount() > 0; + bool changed = false; + for (base::StringPiece table_name : + {kMaskedCreditCardsTable, kUnmaskedCreditCardsTable, + kServerAddressesTable, kServerCardMetadataTable, + kServerAddressMetadataTable, kPaymentsCustomerDataTable, + kServerCardCloudTokenDataTable, kOfferDataTable, + kOfferEligibleInstrumentTable, kOfferMerchantDomainTable}) { + Delete(db_, table_name); + changed |= db_->GetLastChangeCount() > 0; + } transaction.Commit(); return changed; @@ -2335,11 +2490,9 @@ bool AutofillTable::RemoveAutofillDataModifiedBetween( time_t delete_end_t = GetEndTime(delete_end); // Remember Autofill profiles in the time range. - sql::Statement s_profiles_get(db_->GetUniqueStatement( - "SELECT guid FROM autofill_profiles " - "WHERE date_modified >= ? AND date_modified < ?")); - s_profiles_get.BindInt64(0, delete_begin_t); - s_profiles_get.BindInt64(1, delete_end_t); + sql::Statement s_profiles_get; + SelectBetween(db_, s_profiles_get, kAutofillProfilesTable, {kGuid}, + kDateModified, delete_begin_t, delete_end_t); profiles->clear(); while (s_profiles_get.Step()) { @@ -2359,9 +2512,9 @@ bool AutofillTable::RemoveAutofillDataModifiedBetween( } // Remove Autofill profiles in the time range. - sql::Statement s_profiles(db_->GetUniqueStatement( - "DELETE FROM autofill_profiles " - "WHERE date_modified >= ? AND date_modified < ?")); + sql::Statement s_profiles; + DeleteBuilder(db_, s_profiles, kAutofillProfilesTable, + "date_modified >= ? AND date_modified < ?"); s_profiles.BindInt64(0, delete_begin_t); s_profiles.BindInt64(1, delete_end_t); @@ -2369,11 +2522,9 @@ bool AutofillTable::RemoveAutofillDataModifiedBetween( return false; // Remember Autofill credit cards in the time range. - sql::Statement s_credit_cards_get(db_->GetUniqueStatement( - "SELECT guid FROM credit_cards " - "WHERE date_modified >= ? AND date_modified < ?")); - s_credit_cards_get.BindInt64(0, delete_begin_t); - s_credit_cards_get.BindInt64(1, delete_end_t); + sql::Statement s_credit_cards_get; + SelectBetween(db_, s_credit_cards_get, kCreditCardsTable, {kGuid}, + kDateModified, delete_begin_t, delete_end_t); credit_cards->clear(); while (s_credit_cards_get.Step()) { @@ -2387,18 +2538,18 @@ bool AutofillTable::RemoveAutofillDataModifiedBetween( return false; // Remove Autofill credit cards in the time range. - sql::Statement s_credit_cards(db_->GetUniqueStatement( - "DELETE FROM credit_cards " - "WHERE date_modified >= ? AND date_modified < ?")); + sql::Statement s_credit_cards; + DeleteBuilder(db_, s_credit_cards, kCreditCardsTable, + "date_modified >= ? AND date_modified < ?"); s_credit_cards.BindInt64(0, delete_begin_t); s_credit_cards.BindInt64(1, delete_end_t); if (!s_credit_cards.Run()) return false; // Remove unmasked credit cards in the time range. - sql::Statement s_unmasked_cards( - db_->GetUniqueStatement("DELETE FROM unmasked_credit_cards " - "WHERE unmask_date >= ? AND unmask_date < ?")); + sql::Statement s_unmasked_cards; + DeleteBuilder(db_, s_unmasked_cards, kUnmaskedCreditCardsTable, + "unmask_date >= ? AND unmask_date < ?"); s_unmasked_cards.BindInt64(0, delete_begin.ToInternalValue()); s_unmasked_cards.BindInt64(1, delete_end.ToInternalValue()); return s_unmasked_cards.Run(); @@ -2414,11 +2565,9 @@ bool AutofillTable::RemoveOriginURLsModifiedBetween( time_t delete_end_t = GetEndTime(delete_end); // Remember Autofill profiles with URL origins in the time range. - sql::Statement s_profiles_get(db_->GetUniqueStatement( - "SELECT guid, origin FROM autofill_profiles " - "WHERE date_modified >= ? AND date_modified < ?")); - s_profiles_get.BindInt64(0, delete_begin_t); - s_profiles_get.BindInt64(1, delete_end_t); + sql::Statement s_profiles_get; + SelectBetween(db_, s_profiles_get, kAutofillProfilesTable, {kGuid, kOrigin}, + kDateModified, delete_begin_t, delete_end_t); std::vector<std::string> profile_guids; while (s_profiles_get.Step()) { @@ -2446,11 +2595,9 @@ bool AutofillTable::RemoveOriginURLsModifiedBetween( } // Remember Autofill credit cards with URL origins in the time range. - sql::Statement s_credit_cards_get(db_->GetUniqueStatement( - "SELECT guid, origin FROM credit_cards " - "WHERE date_modified >= ? AND date_modified < ?")); - s_credit_cards_get.BindInt64(0, delete_begin_t); - s_credit_cards_get.BindInt64(1, delete_end_t); + sql::Statement s_credit_cards_get; + SelectBetween(db_, s_credit_cards_get, kCreditCardsTable, {kGuid, kOrigin}, + kDateModified, delete_begin_t, delete_end_t); std::vector<std::string> credit_card_guids; while (s_credit_cards_get.Step()) { @@ -2475,44 +2622,16 @@ bool AutofillTable::RemoveOriginURLsModifiedBetween( } bool AutofillTable::ClearAutofillProfiles() { - sql::Statement s1(db_->GetUniqueStatement("DELETE FROM autofill_profiles")); - - if (!s1.Run()) - return false; - - sql::Statement s2( - db_->GetUniqueStatement("DELETE FROM autofill_profile_names")); - - if (!s2.Run()) - return false; - - sql::Statement s3( - db_->GetUniqueStatement("DELETE FROM autofill_profile_emails")); - - if (!s3.Run()) - return false; - - sql::Statement s4( - db_->GetUniqueStatement("DELETE FROM autofill_profile_addresses")); - - if (!s4.Run()) - return false; - - sql::Statement s5( - db_->GetUniqueStatement("DELETE FROM autofill_profile_phones")); - - if (!s5.Run()) - return false; - - sql::Statement s6( - db_->GetUniqueStatement("DELETE FROM autofill_profile_birthdates")); - - return s6.Run(); + return Delete(db_, kAutofillProfilesTable) && + Delete(db_, kAutofillProfileNamesTable) && + Delete(db_, kAutofillProfileEmailsTable) && + Delete(db_, kAutofillProfileAddressesTable) && + Delete(db_, kAutofillProfilePhonesTable) && + Delete(db_, kAutofillProfileBirthdatesTable); } bool AutofillTable::ClearCreditCards() { - sql::Statement s1(db_->GetUniqueStatement("DELETE FROM credit_cards")); - return s1.Run(); + return Delete(db_, kCreditCardsTable); } bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type, @@ -2539,9 +2658,10 @@ bool AutofillTable::UpdateSyncMetadata( DCHECK(SupportsMetadataForModelType(model_type)) << "Model type " << model_type << " not supported for metadata"; - sql::Statement s(db_->GetUniqueStatement( - "INSERT OR REPLACE INTO autofill_sync_metadata " - "(model_type, storage_key, value) VALUES(?, ?, ?)")); + sql::Statement s; + InsertBuilder(db_, s, kAutofillSyncMetadataTable, + {kModelType, kStorageKey, kValue}, + /*or_replace=*/true); s.BindInt(0, GetKeyValueForModelType(model_type)); s.BindString(1, storage_key); s.BindString(2, metadata.SerializeAsString()); @@ -2554,9 +2674,9 @@ bool AutofillTable::ClearSyncMetadata(syncer::ModelType model_type, DCHECK(SupportsMetadataForModelType(model_type)) << "Model type " << model_type << " not supported for metadata"; - sql::Statement s( - db_->GetUniqueStatement("DELETE FROM autofill_sync_metadata WHERE " - "model_type=? AND storage_key=?")); + sql::Statement s; + DeleteBuilder(db_, s, kAutofillSyncMetadataTable, + "model_type=? AND storage_key=?"); s.BindInt(0, GetKeyValueForModelType(model_type)); s.BindString(1, storage_key); @@ -2571,9 +2691,9 @@ bool AutofillTable::UpdateModelTypeState( // Hardcode the id to force a collision, ensuring that there remains only a // single entry. - sql::Statement s(db_->GetUniqueStatement( - "INSERT OR REPLACE INTO autofill_model_type_state (model_type, value) " - "VALUES(?,?)")); + sql::Statement s; + InsertBuilder(db_, s, kAutofillModelTypeStateTable, {kModelType, kValue}, + /*or_replace=*/true); s.BindInt(0, GetKeyValueForModelType(model_type)); s.BindString(1, model_type_state.SerializeAsString()); @@ -2584,8 +2704,8 @@ bool AutofillTable::ClearModelTypeState(syncer::ModelType model_type) { DCHECK(SupportsMetadataForModelType(model_type)) << "Model type " << model_type << " not supported for metadata"; - sql::Statement s(db_->GetUniqueStatement( - "DELETE FROM autofill_model_type_state WHERE model_type=?")); + sql::Statement s; + DeleteBuilder(db_, s, kAutofillModelTypeStateTable, "model_type=?"); s.BindInt(0, GetKeyValueForModelType(model_type)); return s.Run(); @@ -2619,747 +2739,63 @@ bool AutofillTable::RemoveOrphanAutofillTableRows() { return true; } -bool AutofillTable::MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields() { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - // Test the existence of the |address_line_1| column as an indication that a - // migration is needed. It is possible that the new |autofill_profile_phones| - // schema is in place because the table was newly created when migrating from - // a pre-version-23 database. - if (db_->DoesColumnExist("autofill_profiles", "address_line_1")) { - // Create a temporary copy of the autofill_profiles table in the (newer) - // version 54 format. This table - // (a) adds columns for street_address, dependent_locality, and - // sorting_code, - // (b) removes the address_line_1 and address_line_2 columns, which are - // replaced by the street_address column, and - // (c) removes the country column, which was long deprecated. - if (db_->DoesTableExist("autofill_profiles_temp") || - !db_->Execute("CREATE TABLE autofill_profiles_temp ( " - "guid VARCHAR PRIMARY KEY, " - "company_name VARCHAR, " - "street_address VARCHAR, " - "dependent_locality VARCHAR, " - "city VARCHAR, " - "state VARCHAR, " - "zipcode VARCHAR, " - "sorting_code VARCHAR, " - "country_code VARCHAR, " - "date_modified INTEGER NOT NULL DEFAULT 0, " - "origin VARCHAR DEFAULT '')")) { - return false; - } - - // Copy over the data from the autofill_profiles table, taking care to merge - // the address lines 1 and 2 into the new street_address column. - if (!db_->Execute("INSERT INTO autofill_profiles_temp " - "SELECT guid, company_name, '', '', city, state, zipcode," - " '', country_code, date_modified, origin " - "FROM autofill_profiles")) { - return false; - } - sql::Statement s(db_->GetUniqueStatement( - "SELECT guid, address_line_1, address_line_2 FROM autofill_profiles")); - while (s.Step()) { - std::string guid = s.ColumnString(0); - std::u16string line1 = s.ColumnString16(1); - std::u16string line2 = s.ColumnString16(2); - std::u16string street_address = line1; - if (!line2.empty()) - street_address += u"\n" + line2; - - sql::Statement s_update(db_->GetUniqueStatement( - "UPDATE autofill_profiles_temp SET street_address=? WHERE guid=?")); - s_update.BindString16(0, street_address); - s_update.BindString(1, guid); - if (!s_update.Run()) - return false; - } - if (!s.Succeeded()) - return false; - - // Delete the existing (version 53) table and replace it with the contents - // of the temporary table. - if (!db_->Execute("DROP TABLE autofill_profiles") || - !db_->Execute("ALTER TABLE autofill_profiles_temp " - "RENAME TO autofill_profiles")) { - return false; - } - } - - // Test the existence of the |type| column as an indication that a migration - // is needed. It is possible that the new |autofill_profile_phones| schema is - // in place because the table was newly created when migrating from a - // pre-version-23 database. - if (db_->DoesColumnExist("autofill_profile_phones", "type")) { - // Create a temporary copy of the autofill_profile_phones table in the - // (newer) version 54 format. This table removes the deprecated |type| - // column. - if (db_->DoesTableExist("autofill_profile_phones_temp") || - !db_->Execute("CREATE TABLE autofill_profile_phones_temp ( " - "guid VARCHAR, " - "number VARCHAR)")) { - return false; - } - - // Copy over the data from the autofill_profile_phones table. - if (!db_->Execute("INSERT INTO autofill_profile_phones_temp " - "SELECT guid, number FROM autofill_profile_phones")) { - return false; - } - - // Delete the existing (version 53) table and replace it with the contents - // of the temporary table. - if (!db_->Execute("DROP TABLE autofill_profile_phones")) - return false; - if (!db_->Execute("ALTER TABLE autofill_profile_phones_temp " - "RENAME TO autofill_profile_phones")) { - return false; - } - } - - return transaction.Commit(); -} - -bool AutofillTable::MigrateToVersion55MergeAutofillDatesTable() { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - if (db_->DoesTableExist("autofill_temp") || - !db_->Execute("CREATE TABLE autofill_temp (" - "name VARCHAR, " - "value VARCHAR, " - "value_lower VARCHAR, " - "date_created INTEGER DEFAULT 0, " - "date_last_used INTEGER DEFAULT 0, " - "count INTEGER DEFAULT 1, " - "PRIMARY KEY (name, value))")) { - return false; - } - - // Slurp up the data from the existing table and write it to the new table. - sql::Statement s(db_->GetUniqueStatement( - "SELECT name, value, value_lower, count, MIN(date_created)," - " MAX(date_created) " - "FROM autofill a JOIN autofill_dates ad ON a.pair_id=ad.pair_id " - "GROUP BY name, value, value_lower, count")); - while (s.Step()) { - sql::Statement s_insert(db_->GetUniqueStatement( - "INSERT INTO autofill_temp " - "(name, value, value_lower, count, date_created, date_last_used) " - "VALUES (?, ?, ?, ?, ?, ?)")); - s_insert.BindString16(0, s.ColumnString16(0)); - s_insert.BindString16(1, s.ColumnString16(1)); - s_insert.BindString16(2, s.ColumnString16(2)); - s_insert.BindInt(3, s.ColumnInt(3)); - s_insert.BindInt64(4, s.ColumnInt64(4)); - s_insert.BindInt64(5, s.ColumnInt64(5)); - if (!s_insert.Run()) - return false; - } - - if (!s.Succeeded()) - return false; - - // Delete the existing (version 54) tables and replace them with the contents - // of the temporary table. - if (!db_->Execute("DROP TABLE autofill") || - !db_->Execute("DROP TABLE autofill_dates") || - !db_->Execute("ALTER TABLE autofill_temp " - "RENAME TO autofill")) { - return false; - } - - // Create indices on the new table, for fast lookups. - if (!db_->Execute("CREATE INDEX autofill_name ON autofill (name)") || - !db_->Execute("CREATE INDEX autofill_name_value_lower ON " - "autofill (name, value_lower)")) { - return false; - } - - return transaction.Commit(); -} - -bool AutofillTable::MigrateToVersion56AddProfileLanguageCodeForFormatting() { - return db_->Execute( - "ALTER TABLE autofill_profiles ADD COLUMN language_code VARCHAR"); -} - -bool AutofillTable::MigrateToVersion57AddFullNameField() { - return db_->Execute( - "ALTER TABLE autofill_profile_names ADD COLUMN full_name VARCHAR"); -} - -bool AutofillTable::MigrateToVersion60AddServerCards() { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - if (!db_->DoesTableExist("masked_credit_cards") && - !db_->Execute("CREATE TABLE masked_credit_cards (" - "id VARCHAR," - "status VARCHAR," - "name_on_card VARCHAR," - "type VARCHAR," - "last_four VARCHAR," - "exp_month INTEGER DEFAULT 0," - "exp_year INTEGER DEFAULT 0)")) { - return false; - } - - if (!db_->DoesTableExist("unmasked_credit_cards") && - !db_->Execute("CREATE TABLE unmasked_credit_cards (" - "id VARCHAR," - "card_number_encrypted VARCHAR)")) { - return false; - } - - if (!db_->DoesTableExist("server_addresses") && - !db_->Execute("CREATE TABLE server_addresses (" - "id VARCHAR," - "company_name VARCHAR," - "street_address VARCHAR," - "address_1 VARCHAR," - "address_2 VARCHAR," - "address_3 VARCHAR," - "address_4 VARCHAR," - "postal_code VARCHAR," - "sorting_code VARCHAR," - "country_code VARCHAR," - "language_code VARCHAR)")) { - return false; - } - - return transaction.Commit(); -} - -bool AutofillTable::MigrateToVersion61AddUsageStats() { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - // Add use_count to autofill_profiles. - if (!db_->DoesColumnExist("autofill_profiles", "use_count") && - !db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN " - "use_count INTEGER NOT NULL DEFAULT 0")) { - return false; - } - - // Add use_date to autofill_profiles. - if (!db_->DoesColumnExist("autofill_profiles", "use_date") && - !db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN " - "use_date INTEGER NOT NULL DEFAULT 0")) { - return false; - } - - // Add use_count to credit_cards. - if (!db_->DoesColumnExist("credit_cards", "use_count") && - !db_->Execute("ALTER TABLE credit_cards ADD COLUMN " - "use_count INTEGER NOT NULL DEFAULT 0")) { - return false; - } - - // Add use_date to credit_cards. - if (!db_->DoesColumnExist("credit_cards", "use_date") && - !db_->Execute("ALTER TABLE credit_cards ADD COLUMN " - "use_date INTEGER NOT NULL DEFAULT 0")) { - return false; - } - - return transaction.Commit(); -} - -bool AutofillTable::MigrateToVersion62AddUsageStatsForUnmaskedCards() { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - // Add use_count to unmasked_credit_cards. - if (!db_->DoesColumnExist("unmasked_credit_cards", "use_count") && - !db_->Execute("ALTER TABLE unmasked_credit_cards ADD COLUMN " - "use_count INTEGER NOT NULL DEFAULT 0")) { - return false; - } - - // Add use_date to unmasked_credit_cards. - if (!db_->DoesColumnExist("unmasked_credit_cards", "use_date") && - !db_->Execute("ALTER TABLE unmasked_credit_cards ADD COLUMN " - "use_date INTEGER NOT NULL DEFAULT 0")) { - return false; - } - - return transaction.Commit(); -} - -bool AutofillTable::MigrateToVersion63AddServerRecipientName() { - if (!db_->DoesColumnExist("server_addresses", "recipient_name") && - !db_->Execute("ALTER TABLE server_addresses ADD COLUMN " - "recipient_name VARCHAR")) { - return false; - } - return true; -} - -bool AutofillTable::MigrateToVersion64AddUnmaskDate() { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - if (!db_->DoesColumnExist("unmasked_credit_cards", "unmask_date") && - !db_->Execute("ALTER TABLE unmasked_credit_cards ADD COLUMN " - "unmask_date INTEGER NOT NULL DEFAULT 0")) { - return false; - } - if (!db_->DoesColumnExist("server_addresses", "phone_number") && - !db_->Execute("ALTER TABLE server_addresses ADD COLUMN " - "phone_number VARCHAR")) { - return false; - } - - return transaction.Commit(); -} - -bool AutofillTable::MigrateToVersion65AddServerMetadataTables() { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - if (!db_->DoesTableExist("server_card_metadata") && - !db_->Execute("CREATE TABLE server_card_metadata (" - "id VARCHAR NOT NULL," - "use_count INTEGER NOT NULL DEFAULT 0, " - "use_date INTEGER NOT NULL DEFAULT 0)")) { - return false; - } - - // This clobbers existing usage metadata, which is not synced and only - // applies to unmasked cards. Trying to migrate the usage metadata would be - // tricky as multiple devices for the same user get DB upgrades. - if (!db_->Execute("UPDATE unmasked_credit_cards " - "SET use_count=0, use_date=0")) { - return false; - } - - if (!db_->DoesTableExist("server_address_metadata") && - !db_->Execute("CREATE TABLE server_address_metadata (" - "id VARCHAR NOT NULL," - "use_count INTEGER NOT NULL DEFAULT 0, " - "use_date INTEGER NOT NULL DEFAULT 0)")) { - return false; - } - - // Get existing server addresses and generate IDs for them. - sql::Statement s(db_->GetUniqueStatement( - "SELECT " - "id," - "recipient_name," - "company_name," - "street_address," - "address_1," // ADDRESS_HOME_STATE - "address_2," // ADDRESS_HOME_CITY - "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY - "address_4," // Not supported in AutofillProfile yet. - "postal_code," // ADDRESS_HOME_ZIP - "sorting_code," // ADDRESS_HOME_SORTING_CODE - "country_code," // ADDRESS_HOME_COUNTRY - "phone_number," // PHONE_HOME_WHOLE_NUMBER - "language_code " - "FROM server_addresses addresses")); - std::vector<AutofillProfile> profiles; - while (s.Step()) { - int index = 0; - AutofillProfile profile(AutofillProfile::SERVER_PROFILE, - s.ColumnString(index++)); - - std::u16string recipient_name = s.ColumnString16(index++); - profile.SetRawInfo(COMPANY_NAME, s.ColumnString16(index++)); - profile.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, s.ColumnString16(index++)); - profile.SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(index++)); - profile.SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(index++)); - profile.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, - s.ColumnString16(index++)); - index++; // Skip address_4 which we haven't added to AutofillProfile yet. - profile.SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(index++)); - profile.SetRawInfo(ADDRESS_HOME_SORTING_CODE, s.ColumnString16(index++)); - profile.SetRawInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(index++)); - std::u16string phone_number = s.ColumnString16(index++); - profile.set_language_code(s.ColumnString(index++)); - profile.SetInfo(NAME_FULL, recipient_name, profile.language_code()); - profile.SetInfo(PHONE_HOME_WHOLE_NUMBER, phone_number, - profile.language_code()); - profile.GenerateServerProfileIdentifier(); - profiles.push_back(profile); - } - - // Reinsert with the generated IDs. - sql::Statement delete_old( - db_->GetUniqueStatement("DELETE FROM server_addresses")); - delete_old.Run(); - - sql::Statement insert(db_->GetUniqueStatement( - "INSERT INTO server_addresses(" - "id," - "recipient_name," - "company_name," - "street_address," - "address_1," // ADDRESS_HOME_STATE - "address_2," // ADDRESS_HOME_CITY - "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY - "address_4," // Not supported in AutofillProfile yet. - "postal_code," // ADDRESS_HOME_ZIP - "sorting_code," // ADDRESS_HOME_SORTING_CODE - "country_code," // ADDRESS_HOME_COUNTRY - "phone_number," // PHONE_HOME_WHOLE_NUMBER - "language_code) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")); - for (const AutofillProfile& profile : profiles) { - int index = 0; - insert.BindString(index++, profile.server_id()); - insert.BindString16(index++, profile.GetRawInfo(NAME_FULL)); - insert.BindString16(index++, profile.GetRawInfo(COMPANY_NAME)); - insert.BindString16(index++, - profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_STATE)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_CITY)); - insert.BindString16(index++, - profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY)); - index++; // SKip address_4 which we haven't added to AutofillProfile yet. - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_ZIP)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE)); - insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_COUNTRY)); - insert.BindString16(index++, profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); - insert.BindString(index++, profile.language_code()); - insert.Run(); - insert.Reset(true); - } - - return transaction.Commit(); -} - -bool AutofillTable::MigrateToVersion66AddCardBillingAddress() { - // The default value for this column is null, but Connection::ColumnString() - // returns an empty string for that. - return db_->Execute( - "ALTER TABLE credit_cards ADD COLUMN billing_address_id VARCHAR"); -} - -bool AutofillTable::MigrateToVersion67AddMaskedCardBillingAddress() { - // The default value for this column is null, but Connection::ColumnString() - // returns an empty string for that. - return db_->Execute( - "ALTER TABLE masked_credit_cards ADD COLUMN billing_address_id VARCHAR"); -} - -bool AutofillTable::MigrateToVersion70AddSyncMetadata() { - if (!db_->Execute("CREATE TABLE autofill_sync_metadata (" - "storage_key VARCHAR PRIMARY KEY NOT NULL," - "value BLOB)")) { - return false; - } - return db_->Execute( - "CREATE TABLE autofill_model_type_state (id INTEGER PRIMARY KEY, value " - "BLOB)"); -} - -bool AutofillTable:: - MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata() { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - // Add the new has_converted column to the server_address_metadata table. - if (!db_->DoesColumnExist("server_address_metadata", "has_converted") && - !db_->Execute("ALTER TABLE server_address_metadata ADD COLUMN " - "has_converted BOOL NOT NULL DEFAULT FALSE")) { - return false; - } - - // Add the new billing_address_id column to the server_card_metadata table. - if (!db_->DoesColumnExist("server_card_metadata", "billing_address_id") && - !db_->Execute("ALTER TABLE server_card_metadata ADD COLUMN " - "billing_address_id VARCHAR")) { - return false; - } - - // Copy over the billing_address_id from the masked_server_cards to - // server_card_metadata. - if (!db_->Execute("UPDATE server_card_metadata " - "SET billing_address_id = " - "(SELECT billing_address_id " - "FROM masked_credit_cards " - "WHERE id = server_card_metadata.id)")) { - return false; - } - - if (db_->DoesTableExist("masked_credit_cards_temp") && - !db_->Execute("DROP TABLE masked_credit_cards_temp")) { - return false; - } - - // Remove the billing_address_id column from the masked_credit_cards table. - // Create a temporary table that is a copy of masked_credit_cards but without - // the billing_address_id column. - if (!db_->Execute("CREATE TABLE masked_credit_cards_temp (" - "id VARCHAR," - "status VARCHAR," - "name_on_card VARCHAR," - "type VARCHAR," - "last_four VARCHAR," - "exp_month INTEGER DEFAULT 0," - "exp_year INTEGER DEFAULT 0)")) { - return false; - } - // Copy over the data from the original masked_credit_cards table. - if (!db_->Execute("INSERT INTO masked_credit_cards_temp " - "SELECT id, status, name_on_card, type, last_four, " - "exp_month, exp_year " - "FROM masked_credit_cards")) { - return false; - } - // Delete the existing table and replace it with the contents of the - // temporary table. - if (!db_->Execute("DROP TABLE masked_credit_cards") || - !db_->Execute("ALTER TABLE masked_credit_cards_temp " - "RENAME TO masked_credit_cards")) { - return false; - } - - return transaction.Commit(); -} - -bool AutofillTable::MigrateToVersion72RenameCardTypeToIssuerNetwork() { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - if (db_->DoesTableExist("masked_credit_cards_temp") && - !db_->Execute("DROP TABLE masked_credit_cards_temp")) { - return false; - } - - return db_->Execute( - "CREATE TABLE masked_credit_cards_temp (" - "id VARCHAR," - "status VARCHAR," - "name_on_card VARCHAR," - "network VARCHAR," - "last_four VARCHAR," - "exp_month INTEGER DEFAULT 0," - "exp_year INTEGER DEFAULT 0)") && - db_->Execute( - "INSERT INTO masked_credit_cards_temp (" - "id, status, name_on_card, network, last_four, exp_month, exp_year" - ") SELECT " - "id, status, name_on_card, type, last_four, exp_month, exp_year" - " FROM masked_credit_cards") && - db_->Execute("DROP TABLE masked_credit_cards") && - db_->Execute( - "ALTER TABLE masked_credit_cards_temp " - "RENAME TO masked_credit_cards") && - transaction.Commit(); -} - -bool AutofillTable::MigrateToVersion73AddMaskedCardBankName() { - // Add the new bank_name column to the masked_credit_cards table. - return db_->DoesColumnExist("masked_credit_cards", "bank_name") || - db_->Execute( - "ALTER TABLE masked_credit_cards ADD COLUMN bank_name VARCHAR"); -} - -bool AutofillTable::MigrateToVersion74AddServerCardTypeColumn() { - // Version 73 was actually used by two different schemas; an attempt to add - // the "type" column (as in this version 74) was landed and reverted, and then - // the "bank_name" column was added (and stuck). Some clients may have been - // upgraded to one and some the other. Figure out which is the case. - const bool added_type_column_in_v73 = - db_->DoesColumnExist("masked_credit_cards", "type"); - - // If we previously added the "type" column, then it's already present with - // the correct semantics, but we now need to run the "bank_name" migration. - // Otherwise, we need to add "type" now. - return added_type_column_in_v73 ? MigrateToVersion73AddMaskedCardBankName() - : db_->Execute( - "ALTER TABLE masked_credit_cards ADD " - "COLUMN type INTEGER DEFAULT 0"); -} - -bool AutofillTable::MigrateToVersion75AddProfileValidityBitfieldColumn() { - // Add the new valdity_bitfield column to the autofill_profiles table. - return db_->Execute( - "ALTER TABLE autofill_profiles ADD COLUMN validity_bitfield UNSIGNED NOT " - "NULL DEFAULT 0"); -} - -bool AutofillTable::MigrateToVersion78AddModelTypeColumns() { - // Add the new model type columns to the autofill metadata tables. - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - if (db_->DoesTableExist("autofill_sync_metadata_temp") && - !db_->Execute("DROP TABLE autofill_sync_metadata_temp")) { - return false; - } - - if (db_->DoesTableExist("autofill_model_type_state_temp") && - !db_->Execute("DROP TABLE autofill_model_type_state_temp")) { - return false; - } - - if (!db_->Execute("CREATE TABLE autofill_sync_metadata_temp (" - "model_type INTEGER NOT NULL, " - "storage_key VARCHAR NOT NULL, " - "value BLOB, " - "PRIMARY KEY (model_type, storage_key))") || - !db_->Execute("CREATE TABLE autofill_model_type_state_temp (" - "model_type INTEGER NOT NULL PRIMARY KEY, value BLOB)")) { - return false; - } - - sql::Statement insert_metadata( - db_->GetUniqueStatement("INSERT INTO autofill_sync_metadata_temp " - "(model_type, storage_key, value) " - "SELECT ?, storage_key, value " - "FROM autofill_sync_metadata")); - // Note: This uses the *wrong* ID for the ModelType - instead of - // |syncer::ModelTypeHistogramValue|, this should be |GetKeyValueForModelType| - // aka |syncer::ModelTypeToStableIdentifier|. But at this point, fixing it - // here would just make an even bigger mess. Instead, we clean this up in the - // migration to version 81. See also crbug.com/895826. - insert_metadata.BindInt( - 0, static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL))); - - // Prior to this migration, the table was a singleton, containing only one - // entry with id being hard-coded to 1. - sql::Statement insert_state( - db_->GetUniqueStatement("INSERT INTO autofill_model_type_state_temp " - "(model_type, value) SELECT ?, value " - "FROM autofill_model_type_state WHERE id=1")); - // Note: Like above, this uses the *wrong* ID for the ModelType. - insert_state.BindInt( - 0, static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL))); - - if (!insert_metadata.Run() || !insert_state.Run()) { - return false; - } - - return db_->Execute("DROP TABLE autofill_sync_metadata") && - db_->Execute( - "ALTER TABLE autofill_sync_metadata_temp " - "RENAME TO autofill_sync_metadata") && - db_->Execute("DROP TABLE autofill_model_type_state") && - db_->Execute( - "ALTER TABLE autofill_model_type_state_temp " - "RENAME TO autofill_model_type_state") && - transaction.Commit(); -} - -bool AutofillTable::MigrateToVersion80AddIsClientValidityStatesUpdatedColumn() { - // Add the client validity states updated flag column to the autofill_profiles - // table. - return db_->Execute( - "ALTER TABLE autofill_profiles ADD COLUMN " - "is_client_validity_states_updated BOOL NOT " - "NULL DEFAULT FALSE"); -} - -bool AutofillTable::MigrateToVersion81CleanUpWrongModelTypeData() { - // The migration to version 78 inserted Sync data with wrong values in the - // model_type column of the autofill_model_type_state and - // autofill_sync_metadata tables. Here we just delete the bad data - no point - // in trying to recover anything, since by now it'll have been redownloaded - // anyway. - const int bad_model_type_id = - static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL)); - DCHECK_NE(bad_model_type_id, GetKeyValueForModelType(syncer::AUTOFILL)); - - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - sql::Statement delete_bad_model_type_state(db_->GetUniqueStatement( - "DELETE FROM autofill_model_type_state WHERE model_type = ?;")); - delete_bad_model_type_state.BindInt(0, bad_model_type_id); - - sql::Statement delete_bad_sync_metadata(db_->GetUniqueStatement( - "DELETE FROM autofill_sync_metadata WHERE model_type = ?;")); - delete_bad_sync_metadata.BindInt(0, bad_model_type_id); - - return delete_bad_model_type_state.Run() && delete_bad_sync_metadata.Run() && - transaction.Commit(); -} - bool AutofillTable::MigrateToVersion83RemoveServerCardTypeColumn() { // Sqlite does not support "alter table drop column" syntax, so it has be done // manually. + constexpr base::StringPiece kMaskedCreditCardsTempTable = + "masked_credit_cards_temp"; sql::Transaction transaction(db_); return transaction.Begin() && - db_->Execute( - "CREATE TABLE masked_credit_cards_temp (" - "id VARCHAR," - "status VARCHAR," - "name_on_card VARCHAR," - "network VARCHAR," - "last_four VARCHAR," - "exp_month INTEGER DEFAULT 0," - "exp_year INTEGER DEFAULT 0, " - "bank_name VARCHAR)") && + CreateTable(db_, kMaskedCreditCardsTempTable, + {{kId, "VARCHAR"}, + {kStatus, "VARCHAR"}, + {kNameOnCard, "VARCHAR"}, + {kNetwork, "VARCHAR"}, + {kLastFour, "VARCHAR"}, + {kExpMonth, "INTEGER DEFAULT 0"}, + {kExpYear, "INTEGER DEFAULT 0"}, + {kBankName, "VARCHAR"}}) && db_->Execute( "INSERT INTO masked_credit_cards_temp " "SELECT id, status, name_on_card, network, last_four, exp_month," "exp_year, bank_name " "FROM masked_credit_cards") && - db_->Execute("DROP TABLE masked_credit_cards") && - db_->Execute( - "ALTER TABLE masked_credit_cards_temp " - "RENAME TO masked_credit_cards") && + DropTable(db_, kMaskedCreditCardsTable) && + RenameTable(db_, kMaskedCreditCardsTempTable, + kMaskedCreditCardsTable) && transaction.Commit(); } bool AutofillTable::MigrateToVersion84AddNicknameColumn() { // Add the nickname column to the masked_credit_cards table. - return db_->DoesColumnExist("masked_credit_cards", "nickname") || - db_->Execute( - "ALTER TABLE masked_credit_cards ADD COLUMN nickname VARCHAR"); + return AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kNickname, + "VARCHAR"); } bool AutofillTable::MigrateToVersion85AddCardIssuerColumnToMaskedCreditCard() { // Add the new card_issuer column to the masked_credit_cards table and set the // default value to ISSUER_UNKNOWN. - return db_->DoesColumnExist("masked_credit_cards", "card_issuer") || - db_->Execute( - "ALTER TABLE masked_credit_cards " - "ADD COLUMN card_issuer INTEGER " - "DEFAULT 0"); + return AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kCardIssuer, + "INTEGER DEFAULT 0"); } bool AutofillTable::MigrateToVersion88AddNewNameColumns() { - for (const char* column : {"honorific_prefix", "first_last_name", - "conjunction_last_name", "second_last_name"}) { - if (!db_->DoesColumnExist("autofill_profile_names", column) && - !db_->Execute( - base::StrCat({"ALTER TABLE autofill_profile_names ADD COLUMN ", - column, " VARCHAR"}) - .c_str())) { + for (base::StringPiece column : {kHonorificPrefix, kFirstLastName, + kConjunctionLastName, kSecondLastName}) { + if (!AddColumnIfNotExists(db_, kAutofillProfileNamesTable, column, + "VARCHAR")) { return false; } } - for (const char* column : - {"honorific_prefix_status", "first_name_status", "middle_name_status", - "last_name_status", "first_last_name_status", - "conjunction_last_name_status", "second_last_name_status", - "full_name_status"}) { + for (base::StringPiece column : + {kHonorificPrefixStatus, kFirstNameStatus, kMiddleNameStatus, + kLastNameStatus, kFirstLastNameStatus, kConjunctionLastNameStatus, + kSecondLastNameStatus, kFullNameStatus}) { // The default value of 0 corresponds to the verification status // |kNoStatus|. - if (!db_->DoesColumnExist("autofill_profile_names", column) && - !db_->Execute( - base::StrCat({"ALTER TABLE autofill_profile_names ADD COLUMN ", - column, " INTEGER DEFAULT 0"}) - .c_str())) { + if (!AddColumnIfNotExists(db_, kAutofillProfileNamesTable, column, + "INTEGER DEFAULT 0")) { return false; } } @@ -3367,78 +2803,58 @@ bool AutofillTable::MigrateToVersion88AddNewNameColumns() { } bool AutofillTable::MigrateToVersion92AddNewPrefixedNameColumn() { - if (!db_->DoesColumnExist("autofill_profile_names", - "full_name_with_honorific_prefix") && - !db_->Execute( - base::StrCat({"ALTER TABLE autofill_profile_names ADD COLUMN ", - "full_name_with_honorific_prefix", " VARCHAR"}) - .c_str())) { - return false; - } - if (!db_->DoesColumnExist("autofill_profile_names", - "full_name_with_honorific_prefix_status") && - !db_->Execute( - base::StrCat({"ALTER TABLE autofill_profile_names ADD COLUMN ", - "full_name_with_honorific_prefix_status", - " INTEGER DEFAULT 0"}) - .c_str())) { - return false; - } - return true; + return AddColumnIfNotExists(db_, kAutofillProfileNamesTable, + kFullNameWithHonorificPrefix, "VARCHAR") && + AddColumnIfNotExists(db_, kAutofillProfileNamesTable, + kFullNameWithHonorificPrefixStatus, + "INTEGER DEFAULT 0"); } bool AutofillTable::MigrateToVersion86RemoveUnmaskedCreditCardsUseColumns() { // Sqlite does not support "alter table drop column" syntax, so it has be // done manually. + constexpr base::StringPiece kUnmaskedCreditCardsTempTable = + "unmasked_credit_cards_temp"; sql::Transaction transaction(db_); return transaction.Begin() && - db_->Execute( - "CREATE TABLE unmasked_credit_cards_temp (" - "id VARCHAR," - "card_number_encrypted VARCHAR," - "unmask_date INTEGER NOT NULL DEFAULT 0)") && + CreateTable(db_, kUnmaskedCreditCardsTempTable, + {{kId, "VARCHAR"}, + {kCardNumberEncrypted, "VARCHAR"}, + {kUnmaskDate, "INTEGER NOT NULL DEFAULT 0"}}) && db_->Execute( "INSERT INTO unmasked_credit_cards_temp " "SELECT id, card_number_encrypted, unmask_date " "FROM unmasked_credit_cards") && - db_->Execute("DROP TABLE unmasked_credit_cards") && - db_->Execute( - "ALTER TABLE unmasked_credit_cards_temp " - "RENAME TO unmasked_credit_cards") && + DropTable(db_, kUnmaskedCreditCardsTable) && + RenameTable(db_, kUnmaskedCreditCardsTempTable, + kUnmaskedCreditCardsTable) && transaction.Commit(); } bool AutofillTable::MigrateToVersion87AddCreditCardNicknameColumn() { // Add the nickname column to the credit_card table. - return db_->DoesColumnExist("credit_cards", "nickname") || - db_->Execute("ALTER TABLE credit_cards ADD COLUMN nickname VARCHAR"); + return AddColumnIfNotExists(db_, kCreditCardsTable, kNickname, "VARCHAR"); } bool AutofillTable::MigrateToVersion90AddNewStructuredAddressColumns() { if (!db_->DoesTableExist("autofill_profile_addresses")) InitProfileAddressesTable(); - for (const char* column : {"dependent_locality", "city", "state", "zip_code", - "sorting_code", "country_code"}) { - if (!db_->DoesColumnExist("autofill_profile_addresses", column) && - !db_->Execute( - base::StrCat({"ALTER TABLE autofill_profile_addresses ADD COLUMN ", - column, " VARCHAR"}) - .c_str())) { + for (base::StringPiece column : {kDependentLocality, kCity, kState, kZipCode, + kSortingCode, kCountryCode}) { + if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column, + "VARCHAR")) { return false; } } - for (const char* column : - {"dependent_locality_status", "city_status", "state_status", - "zip_code_status", "sorting_code_status", "country_code_status"}) { + for (base::StringPiece column : + {kDependentLocalityStatus, kCityStatus, kStateStatus, kZipCodeStatus, + kSortingCodeStatus, kCountryCodeStatus}) { // The default value of 0 corresponds to the verification status // |kNoStatus|. - if (!db_->DoesColumnExist("autofill_profile_addresses", column) && - !db_->Execute( - base::StrCat({"ALTER TABLE autofill_profile_addresses ADD COLUMN ", - column, " INTEGER DEFAULT 0"}) - .c_str())) { + if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column, + "INTEGER DEFAULT 0")) { return false; } } @@ -3446,27 +2862,21 @@ bool AutofillTable::MigrateToVersion90AddNewStructuredAddressColumns() { } bool AutofillTable::MigrateToVersion91AddMoreStructuredAddressColumns() { - if (!db_->DoesTableExist("autofill_profile_addresses")) + if (!db_->DoesTableExist(kAutofillProfileAddressesTable)) InitProfileAddressesTable(); - for (const char* column : {"apartment_number", "floor"}) { - if (!db_->DoesColumnExist("autofill_profile_addresses", column) && - !db_->Execute( - base::StrCat({"ALTER TABLE autofill_profile_addresses ADD COLUMN ", - column, " VARCHAR"}) - .c_str())) { + for (base::StringPiece column : {kApartmentNumber, kFloor}) { + if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column, + "VARCHAR")) { return false; } } - for (const char* column : {"apartment_number_status", "floor_status"}) { + for (base::StringPiece column : {kApartmentNumberStatus, kFloorStatus}) { // The default value of 0 corresponds to the verification status // |kNoStatus|. - if (!db_->DoesColumnExist("autofill_profile_addresses", column) && - !db_->Execute( - base::StrCat({"ALTER TABLE autofill_profile_addresses ADD COLUMN ", - column, " INTEGER DEFAULT 0"}) - .c_str())) { + if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column, + "INTEGER DEFAULT 0")) { return false; } } @@ -3474,34 +2884,28 @@ bool AutofillTable::MigrateToVersion91AddMoreStructuredAddressColumns() { } bool AutofillTable::MigrateToVersion93AddAutofillProfileLabelColumn() { - if (!db_->DoesTableExist("autofill_profiles")) + if (!db_->DoesTableExist(kAutofillProfilesTable)) InitProfileAddressesTable(); - return db_->DoesColumnExist("autofill_profiles", "label") || - db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN label VARCHAR"); + return AddColumnIfNotExists(db_, kAutofillProfilesTable, kLabel, "VARCHAR"); } bool AutofillTable:: MigrateToVersion96AddAutofillProfileDisallowConfirmableMergesColumn() { - if (!db_->DoesTableExist("autofill_profiles")) + if (!db_->DoesTableExist(kAutofillProfilesTable)) InitProfileAddressesTable(); - return db_->DoesColumnExist("autofill_profiles", - "disallow_settings_visible_updates") || - db_->Execute( - "ALTER TABLE autofill_profiles ADD COLUMN " - "disallow_settings_visible_updates INTEGER NOT NULL DEFAULT 0"); + return AddColumnIfNotExists(db_, kAutofillProfilesTable, + kDisallowSettingsVisibleUpdates, + "INTEGER NOT NULL DEFAULT 0"); } bool AutofillTable:: MigrateToVersion89AddInstrumentIdColumnToMaskedCreditCard() { // Add the new instrument_id column to the masked_credit_cards table and set // the default value to 0. - return db_->DoesColumnExist("masked_credit_cards", "instrument_id") || - db_->Execute( - "ALTER TABLE masked_credit_cards " - "ADD COLUMN instrument_id INTEGER " - "DEFAULT 0"); + return AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kInstrumentId, + "INTEGER DEFAULT 0"); } bool AutofillTable::MigrateToVersion94AddPromoCodeColumnsToOfferData() { @@ -3509,17 +2913,14 @@ bool AutofillTable::MigrateToVersion94AddPromoCodeColumnsToOfferData() { if (!transaction.Begin()) return false; - if (!db_->DoesTableExist("offer_data")) + if (!db_->DoesTableExist(kOfferDataTable)) InitOfferDataTable(); // Add the new promo_code and DisplayStrings text columns to the offer_data // table. - for (const char* column : {"promo_code", "value_prop_text", - "see_details_text", "usage_instructions_text"}) { - if (!db_->DoesColumnExist("offer_data", column) && - !db_->Execute(base::StrCat({"ALTER TABLE offer_data ADD COLUMN ", - column, " VARCHAR"}) - .c_str())) { + for (base::StringPiece column : + {kPromoCode, kValuePropText, kSeeDetailsText, kUsageInstructionsText}) { + if (!AddColumnIfNotExists(db_, kOfferDataTable, column, "VARCHAR")) { return false; } } @@ -3531,21 +2932,18 @@ bool AutofillTable::MigrateToVersion95AddVirtualCardMetadata() { if (!transaction.Begin()) return false; - if (!db_->DoesTableExist("masked_credit_cards")) + if (!db_->DoesTableExist(kMaskedCreditCardsTable)) InitMaskedCreditCardsTable(); // Add virtual_card_enrollment_state to masked_credit_cards. - if (!db_->DoesColumnExist("masked_credit_cards", - "virtual_card_enrollment_state") && - !db_->Execute("ALTER TABLE masked_credit_cards ADD COLUMN " - "virtual_card_enrollment_state INTEGER DEFAULT 0")) { + if (!AddColumnIfNotExists(db_, kMaskedCreditCardsTable, + kVirtualCardEnrollmentState, "INTEGER DEFAULT 0")) { return false; } // Add card_art_url to masked_credit_cards. - if (!db_->DoesColumnExist("masked_credit_cards", "card_art_url") && - !db_->Execute("ALTER TABLE masked_credit_cards ADD COLUMN " - "card_art_url VARCHAR")) { + if (!AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kCardArtUrl, + "VARCHAR")) { return false; } @@ -3555,39 +2953,38 @@ bool AutofillTable::MigrateToVersion95AddVirtualCardMetadata() { bool AutofillTable::MigrateToVersion98RemoveStatusColumnMaskedCreditCards() { // Sqlite does not support "alter table drop column" syntax, so it has be done // manually. + constexpr base::StringPiece kMaskedCreditCardsTempTable = + "masked_credit_cards_temp"; sql::Transaction transaction(db_); return transaction.Begin() && - db_->Execute( - "CREATE TABLE masked_credit_cards_temp (" - "id VARCHAR," - "name_on_card VARCHAR," - "network VARCHAR," - "last_four VARCHAR," - "exp_month INTEGER DEFAULT 0," - "exp_year INTEGER DEFAULT 0, " - "bank_name VARCHAR, " - "nickname VARCHAR, " - "card_issuer INTEGER DEFAULT 0, " - "instrument_id INTEGER DEFAULT 0, " - "virtual_card_enrollment_state INTEGER DEFAULT 0, " - "card_art_url VARCHAR)") && + CreateTable(db_, kMaskedCreditCardsTempTable, + {{kId, "VARCHAR"}, + {kNameOnCard, "VARCHAR"}, + {kNetwork, "VARCHAR"}, + {kLastFour, "VARCHAR"}, + {kExpMonth, "INTEGER DEFAULT 0"}, + {kExpYear, "INTEGER DEFAULT 0"}, + {kBankName, "VARCHAR"}, + {kNickname, "VARCHAR"}, + {kCardIssuer, "INTEGER DEFAULT 0"}, + {kInstrumentId, "INTEGER DEFAULT 0"}, + {kVirtualCardEnrollmentState, "INTEGER DEFAULT 0"}, + {kCardArtUrl, "VARCHAR"}}) && db_->Execute( "INSERT INTO masked_credit_cards_temp " "SELECT id, name_on_card, network, last_four, exp_month, " "exp_year, bank_name, nickname, card_issuer, instrument_id, " "virtual_card_enrollment_state, card_art_url " "FROM masked_credit_cards") && - db_->Execute("DROP TABLE masked_credit_cards") && - db_->Execute( - "ALTER TABLE masked_credit_cards_temp " - "RENAME TO masked_credit_cards") && + DropTable(db_, kMaskedCreditCardsTable) && + RenameTable(db_, kMaskedCreditCardsTempTable, + kMaskedCreditCardsTable) && transaction.Commit(); } bool AutofillTable::MigrateToVersion99RemoveAutofillProfilesTrashTable() { sql::Transaction transaction(db_); - return transaction.Begin() && - db_->Execute("DROP TABLE autofill_profiles_trash") && + return transaction.Begin() && DropTable(db_, "autofill_profiles_trash") && transaction.Commit(); } @@ -3597,24 +2994,24 @@ bool AutofillTable::MigrateToVersion100RemoveProfileValidityBitfieldColumn() { sql::Transaction transaction(db_); return transaction.Begin() && - db_->Execute( - "CREATE TABLE autofill_profiles_tmp ( " - "guid VARCHAR PRIMARY KEY, " - "company_name VARCHAR, " - "street_address VARCHAR, " - "dependent_locality VARCHAR, " - "city VARCHAR, " - "state VARCHAR, " - "zipcode VARCHAR, " - "sorting_code VARCHAR, " - "country_code VARCHAR, " - "date_modified INTEGER NOT NULL DEFAULT 0, " - "origin VARCHAR DEFAULT '', " - "language_code VARCHAR, " - "use_count INTEGER NOT NULL DEFAULT 0, " - "use_date INTEGER NOT NULL DEFAULT 0, " - "label VARCHAR, " - "disallow_settings_visible_updates INTEGER NOT NULL DEFAULT 0)") && + CreateTable(db_, "autofill_profiles_tmp", + {{kGuid, "VARCHAR PRIMARY KEY"}, + {kCompanyName, "VARCHAR"}, + {kStreetAddress, "VARCHAR"}, + {kDependentLocality, "VARCHAR"}, + {kCity, "VARCHAR"}, + {kState, "VARCHAR"}, + {kZipcode, "VARCHAR"}, + {kSortingCode, "VARCHAR"}, + {kCountryCode, "VARCHAR"}, + {kDateModified, "INTEGER NOT NULL DEFAULT 0"}, + {kOrigin, "VARCHAR DEFAULT ''"}, + {kLanguageCode, "VARCHAR"}, + {kUseCount, "INTEGER NOT NULL DEFAULT 0"}, + {kUseDate, "INTEGER NOT NULL DEFAULT 0"}, + {kLabel, "VARCHAR"}, + {kDisallowSettingsVisibleUpdates, + "INTEGER NOT NULL DEFAULT 0"}}) && db_->Execute( "INSERT INTO autofill_profiles_tmp " "SELECT guid, company_name, street_address, dependent_locality, " @@ -3622,10 +3019,8 @@ bool AutofillTable::MigrateToVersion100RemoveProfileValidityBitfieldColumn() { "origin, language_code, use_count, use_date, label, " "disallow_settings_visible_updates " " FROM autofill_profiles") && - db_->Execute("DROP TABLE autofill_profiles") && - db_->Execute( - "ALTER TABLE autofill_profiles_tmp " - "RENAME TO autofill_profiles") && + DropTable(db_, kAutofillProfilesTable) && + RenameTable(db_, "autofill_profiles_tmp", kAutofillProfilesTable) && transaction.Commit(); } @@ -3639,12 +3034,11 @@ bool AutofillTable::MigrateToVersion101RemoveCreditCardArtImageTable() { bool AutofillTable::MigrateToVersion102AddAutofillBirthdatesTable() { sql::Transaction transaction(db_); return transaction.Begin() && - db_->Execute( - "CREATE TABLE autofill_profile_birthdates ( " - "guid VARCHAR, " - "day INTEGER DEFAULT 0, " - "month INTEGER DEFAULT 0, " - "year INTEGER DEFAULT 0)") && + CreateTable(db_, kAutofillProfileBirthdatesTable, + {{kGuid, "VARCHAR"}, + {kDay, "INTEGER DEFAULT 0"}, + {kMonth, "INTEGER DEFAULT 0"}, + {kYear, "INTEGER DEFAULT 0"}}) && transaction.Commit(); } @@ -3653,19 +3047,30 @@ bool AutofillTable::MigrateToVersion104AddProductDescriptionColumn() { if (!transaction.Begin()) return false; - if (!db_->DoesTableExist("masked_credit_cards")) + if (!db_->DoesTableExist(kMaskedCreditCardsTable)) InitMaskedCreditCardsTable(); // Add product_description to masked_credit_cards. - if (!db_->DoesColumnExist("masked_credit_cards", "product_description") && - !db_->Execute("ALTER TABLE masked_credit_cards ADD COLUMN " - "product_description VARCHAR")) { + if (!AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kProductDescription, + "VARCHAR")) { return false; } return transaction.Commit(); } +bool AutofillTable::MigrateToVersion105AddAutofillIBANTable() { + sql::Transaction transaction(db_); + return transaction.Begin() && + CreateTable(db_, kIBANsTable, + {{kGuid, "VARCHAR"}, + {kUseCount, "INTEGER NOT NULL DEFAULT 0"}, + {kUseDate, "INTEGER NOT NULL DEFAULT 0"}, + {kValue, "VARCHAR"}, + {kNickname, "VARCHAR"}}) && + transaction.Commit(); +} + bool AutofillTable::AddFormFieldValuesTime( const std::vector<FormFieldData>& elements, std::vector<AutofillChange>* changes, @@ -3709,10 +3114,10 @@ bool AutofillTable::AddFormFieldValueTime(const FormFieldData& element, return false; } else { time_t time_as_time_t = time.ToTimeT(); - sql::Statement s(db_->GetUniqueStatement( - "INSERT INTO autofill " - "(name, value, value_lower, date_created, date_last_used, count) " - "VALUES (?, ?, ?, ?, ?, ?)")); + sql::Statement s; + InsertBuilder( + db_, s, kAutofillTable, + {kName, kValue, kValueLower, kDateCreated, kDateLastUsed, kCount}); s.BindString16(0, element.name); s.BindString16(1, element.value); s.BindString16(2, base::i18n::ToLower(element.value)); @@ -3750,9 +3155,9 @@ bool AutofillTable::GetAllSyncEntityMetadata( << "Model type " << model_type << " not supported for metadata"; DCHECK(metadata_batch); - sql::Statement s( - db_->GetUniqueStatement("SELECT storage_key, value FROM " - "autofill_sync_metadata WHERE model_type=?")); + sql::Statement s; + SelectBuilder(db_, s, kAutofillSyncMetadataTable, {kStorageKey, kValue}, + "WHERE model_type=?"); s.BindInt(0, GetKeyValueForModelType(model_type)); while (s.Step()) { @@ -3775,8 +3180,9 @@ bool AutofillTable::GetModelTypeState(syncer::ModelType model_type, DCHECK(SupportsMetadataForModelType(model_type)) << "Model type " << model_type << " not supported for metadata"; - sql::Statement s(db_->GetUniqueStatement( - "SELECT value FROM autofill_model_type_state WHERE model_type=?")); + sql::Statement s; + SelectBuilder(db_, s, kAutofillModelTypeStateTable, {kValue}, + "WHERE model_type=?"); s.BindInt(0, GetKeyValueForModelType(model_type)); if (!s.Step()) { @@ -3788,11 +3194,10 @@ bool AutofillTable::GetModelTypeState(syncer::ModelType model_type, } bool AutofillTable::InsertAutofillEntry(const AutofillEntry& entry) { - std::string sql = - "INSERT INTO autofill " - "(name, value, value_lower, date_created, date_last_used, count) " - "VALUES (?, ?, ?, ?, ?, ?)"; - sql::Statement s(db_->GetUniqueStatement(sql.c_str())); + sql::Statement s; + InsertBuilder( + db_, s, kAutofillTable, + {kName, kValue, kValueLower, kDateCreated, kDateLastUsed, kCount}); s.BindString16(0, entry.key().name()); s.BindString16(1, entry.key().value()); s.BindString16(2, base::i18n::ToLower(entry.key().value())); @@ -3809,22 +3214,13 @@ bool AutofillTable::InsertAutofillEntry(const AutofillEntry& entry) { void AutofillTable::AddMaskedCreditCards( const std::vector<CreditCard>& credit_cards) { DCHECK_GT(db_->transaction_nesting(), 0); - sql::Statement masked_insert( - db_->GetUniqueStatement("INSERT INTO masked_credit_cards(" - "id," // 0 - "network," // 1 - "name_on_card," // 2 - "last_four," // 3 - "exp_month," // 4 - "exp_year," // 5 - "bank_name," // 6 - "nickname," // 7 - "card_issuer," // 8 - "instrument_id," // 9 - "virtual_card_enrollment_state, " // 10 - "card_art_url, " // 11 - "product_description )" // 12 - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")); + sql::Statement masked_insert; + InsertBuilder( + db_, masked_insert, kMaskedCreditCardsTable, + {kId, kNetwork, kNameOnCard, kLastFour, kExpMonth, kExpYear, kBankName, + kNickname, kCardIssuer, kInstrumentId, kVirtualCardEnrollmentState, + kCardArtUrl, kProductDescription}); + int index; for (const CreditCard& card : credit_cards) { DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type()); @@ -3853,12 +3249,9 @@ void AutofillTable::AddMaskedCreditCards( void AutofillTable::AddUnmaskedCreditCard(const std::string& id, const std::u16string& full_number) { - sql::Statement s( - db_->GetUniqueStatement("INSERT INTO unmasked_credit_cards(" - "id," - "card_number_encrypted," - "unmask_date)" - "VALUES (?,?,?)")); + sql::Statement s; + InsertBuilder(db_, s, kUnmaskedCreditCardsTable, + {kId, kCardNumberEncrypted, kUnmaskDate}); s.BindString(0, id); std::string encrypted_data; @@ -3870,394 +3263,271 @@ void AutofillTable::AddUnmaskedCreditCard(const std::string& id, } bool AutofillTable::DeleteFromMaskedCreditCards(const std::string& id) { - sql::Statement s( - db_->GetUniqueStatement("DELETE FROM masked_credit_cards WHERE id = ?")); - s.BindString(0, id); - s.Run(); + DeleteWhereColumnEq(db_, kMaskedCreditCardsTable, kId, id); return db_->GetLastChangeCount() > 0; } bool AutofillTable::DeleteFromUnmaskedCreditCards(const std::string& id) { - sql::Statement s(db_->GetUniqueStatement( - "DELETE FROM unmasked_credit_cards WHERE id = ?")); - s.BindString(0, id); - s.Run(); + DeleteWhereColumnEq(db_, kUnmaskedCreditCardsTable, kId, id); return db_->GetLastChangeCount() > 0; } bool AutofillTable::InitMainTable() { - if (!db_->DoesTableExist("autofill")) { - if (!db_->Execute("CREATE TABLE autofill (" - "name VARCHAR, " - "value VARCHAR, " - "value_lower VARCHAR, " - "date_created INTEGER DEFAULT 0, " - "date_last_used INTEGER DEFAULT 0, " - "count INTEGER DEFAULT 1, " - "PRIMARY KEY (name, value))") || - !db_->Execute("CREATE INDEX autofill_name ON autofill (name)") || - !db_->Execute("CREATE INDEX autofill_name_value_lower ON " - "autofill (name, value_lower)")) { - NOTREACHED(); - return false; - } + if (!db_->DoesTableExist(kAutofillTable)) { + return CreateTable(db_, kAutofillTable, + {{kName, "VARCHAR"}, + {kValue, "VARCHAR"}, + {kValueLower, "VARCHAR"}, + {kDateCreated, "INTEGER DEFAULT 0"}, + {kDateLastUsed, "INTEGER DEFAULT 0"}, + {kCount, "INTEGER DEFAULT 1"}}, + {kName, kValue}) && + CreateIndex(db_, kAutofillTable, {kName}) && + CreateIndex(db_, kAutofillTable, {kName, kValueLower}); } return true; } bool AutofillTable::InitCreditCardsTable() { - if (!db_->DoesTableExist("credit_cards")) { - if (!db_->Execute("CREATE TABLE credit_cards ( " - "guid VARCHAR PRIMARY KEY, " - "name_on_card VARCHAR, " - "expiration_month INTEGER, " - "expiration_year INTEGER, " - "card_number_encrypted BLOB, " - "date_modified INTEGER NOT NULL DEFAULT 0, " - "origin VARCHAR DEFAULT '', " - "use_count INTEGER NOT NULL DEFAULT 0, " - "use_date INTEGER NOT NULL DEFAULT 0, " - "billing_address_id VARCHAR, " - "nickname VARCHAR)")) { - NOTREACHED(); - return false; - } - } - - return true; + return CreateTableIfNotExists(db_, kCreditCardsTable, + {{kGuid, "VARCHAR PRIMARY KEY"}, + {kNameOnCard, "VARCHAR"}, + {kExpirationMonth, "INTEGER"}, + {kExpirationYear, "INTEGER"}, + {kCardNumberEncrypted, "BLOB"}, + {kDateModified, "INTEGER NOT NULL DEFAULT 0"}, + {kOrigin, "VARCHAR DEFAULT ''"}, + {kUseCount, "INTEGER NOT NULL DEFAULT 0"}, + {kUseDate, "INTEGER NOT NULL DEFAULT 0"}, + {kBillingAddressId, "VARCHAR"}, + {kNickname, "VARCHAR"}}); +} + +bool AutofillTable::InitIBANsTable() { + return CreateTableIfNotExists(db_, kIBANsTable, + {{kGuid, "VARCHAR PRIMARY KEY"}, + {kUseCount, "INTEGER NOT NULL DEFAULT 0"}, + {kUseDate, "INTEGER NOT NULL DEFAULT 0"}, + {kValue, "VARCHAR"}, + {kNickname, "VARCHAR"}}); } bool AutofillTable::InitProfilesTable() { - if (!db_->DoesTableExist("autofill_profiles")) { - if (!db_->Execute( - "CREATE TABLE autofill_profiles ( " - "guid VARCHAR PRIMARY KEY, " - "company_name VARCHAR, " - "street_address VARCHAR, " - "dependent_locality VARCHAR, " - "city VARCHAR, " - "state VARCHAR, " - "zipcode VARCHAR, " - "sorting_code VARCHAR, " - "country_code VARCHAR, " - "date_modified INTEGER NOT NULL DEFAULT 0, " - "origin VARCHAR DEFAULT '', " - "language_code VARCHAR, " - "use_count INTEGER NOT NULL DEFAULT 0, " - "use_date INTEGER NOT NULL DEFAULT 0, " - "label VARCHAR, " - "disallow_settings_visible_updates INTEGER NOT NULL DEFAULT 0)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists( + db_, kAutofillProfilesTable, + {{kGuid, "VARCHAR PRIMARY KEY"}, + {kCompanyName, "VARCHAR"}, + {kStreetAddress, "VARCHAR"}, + {kDependentLocality, "VARCHAR"}, + {kCity, "VARCHAR"}, + {kState, "VARCHAR"}, + {kZipcode, "VARCHAR"}, + {kSortingCode, "VARCHAR"}, + {kCountryCode, "VARCHAR"}, + {kDateModified, "INTEGER NOT NULL DEFAULT 0"}, + {kOrigin, "VARCHAR DEFAULT ''"}, + {kLanguageCode, "VARCHAR"}, + {kUseCount, "INTEGER NOT NULL DEFAULT 0"}, + {kUseDate, "INTEGER NOT NULL DEFAULT 0"}, + {kLabel, "VARCHAR"}, + {kDisallowSettingsVisibleUpdates, "INTEGER NOT NULL DEFAULT 0"}}); } bool AutofillTable::InitProfileNamesTable() { - if (!db_->DoesTableExist("autofill_profile_names")) { - // The default value of 0 corresponds to the verification status - // |kNoStatus|. - if (!db_->Execute( - "CREATE TABLE autofill_profile_names ( " - "guid VARCHAR, " - "first_name VARCHAR, " - "middle_name VARCHAR, " - "last_name VARCHAR, " - "full_name VARCHAR, " - "honorific_prefix VARCHAR, " - "first_last_name VARCHAR, " - "conjunction_last_name VARCHAR, " - "second_last_name VARCHAR, " - "honorific_prefix_status INTEGER DEFAULT 0, " - "first_name_status INTEGER DEFAULT 0, " - "middle_name_status INTEGER DEFAULT 0, " - "last_name_status INTEGER DEFAULT 0, " - "first_last_name_status INTEGER DEFAULT 0, " - "conjunction_last_name_status INTEGER DEFAULT 0, " - "second_last_name_status INTEGER DEFAULT 0, " - "full_name_status INTEGER DEFAULT 0, " - "full_name_with_honorific_prefix VARCHAR, " - "full_name_with_honorific_prefix_status INTEGER DEFAULT 0)")) { - NOTREACHED(); - return false; - } - } - return true; + // The default value of 0 corresponds to the verification status + // |kNoStatus|. + return CreateTableIfNotExists( + db_, kAutofillProfileNamesTable, + {{kGuid, "VARCHAR"}, + {kFirstName, "VARCHAR"}, + {kMiddleName, "VARCHAR"}, + {kLastName, "VARCHAR"}, + {kFullName, "VARCHAR"}, + {kHonorificPrefix, "VARCHAR"}, + {kFirstLastName, "VARCHAR"}, + {kConjunctionLastName, "VARCHAR"}, + {kSecondLastName, "VARCHAR"}, + {kHonorificPrefixStatus, "INTEGER DEFAULT 0"}, + {kFirstNameStatus, "INTEGER DEFAULT 0"}, + {kMiddleNameStatus, "INTEGER DEFAULT 0"}, + {kLastNameStatus, "INTEGER DEFAULT 0"}, + {kFirstLastNameStatus, "INTEGER DEFAULT 0"}, + {kConjunctionLastNameStatus, "INTEGER DEFAULT 0"}, + {kSecondLastNameStatus, "INTEGER DEFAULT 0"}, + {kFullNameStatus, "INTEGER DEFAULT 0"}, + {kFullNameWithHonorificPrefix, "VARCHAR"}, + {kFullNameWithHonorificPrefixStatus, "INTEGER DEFAULT 0"}}); } bool AutofillTable::InitProfileAddressesTable() { - if (!db_->DoesTableExist("autofill_profile_addresses")) { - // The default value of 0 corresponds to the verification status - // |kNoStatus|. - if (!db_->Execute("CREATE TABLE autofill_profile_addresses ( " - "guid VARCHAR, " - "street_address VARCHAR, " - "street_name VARCHAR, " - "dependent_street_name VARCHAR, " - "house_number VARCHAR, " - "subpremise VARCHAR, " - "premise_name VARCHAR, " - "street_address_status INTEGER DEFAULT 0, " - "street_name_status INTEGER DEFAULT 0, " - "dependent_street_name_status INTEGER DEFAULT 0, " - "house_number_status INTEGER DEFAULT 0, " - "subpremise_status INTEGER DEFAULT 0, " - "premise_name_status INTEGER DEFAULT 0, " - "dependent_locality VARCHAR, " - "city VARCHAR, " - "state VARCHAR, " - "zip_code VARCHAR, " - "sorting_code VARCHAR, " - "country_code VARCHAR, " - "dependent_locality_status INTEGER DEFAULT 0, " - "city_status INTEGER DEFAULT 0, " - "state_status INTEGER DEFAULT 0, " - "zip_code_status INTEGER DEFAULT 0, " - "sorting_code_status INTEGER DEFAULT 0, " - "country_code_status INTEGER DEFAULT 0, " - "apartment_number VARCHAR, " - "floor VARCHAR, " - "apartment_number_status INTEGER DEFAULT 0, " - "floor_status INTEGER DEFAULT 0)")) { - NOTREACHED(); - return false; - } - } - return true; + // The default value of 0 corresponds to the verification status + // |kNoStatus|. + return CreateTableIfNotExists( + db_, kAutofillProfileAddressesTable, + {{kGuid, "VARCHAR"}, + {kStreetAddress, "VARCHAR"}, + {kStreetName, "VARCHAR"}, + {kDependentStreetName, "VARCHAR"}, + {kHouseNumber, "VARCHAR"}, + {kSubpremise, "VARCHAR"}, + {kPremiseName, "VARCHAR"}, + {kStreetAddressStatus, "INTEGER DEFAULT 0"}, + {kStreetNameStatus, "INTEGER DEFAULT 0"}, + {kDependentStreetNameStatus, "INTEGER DEFAULT 0"}, + {kHouseNumberStatus, "INTEGER DEFAULT 0"}, + {kSubpremiseStatus, "INTEGER DEFAULT 0"}, + {kPremiseNameStatus, "INTEGER DEFAULT 0"}, + {kDependentLocality, "VARCHAR"}, + {kCity, "VARCHAR"}, + {kState, "VARCHAR"}, + {kZipCode, "VARCHAR"}, + {kSortingCode, "VARCHAR"}, + {kCountryCode, "VARCHAR"}, + {kDependentLocalityStatus, "INTEGER DEFAULT 0"}, + {kCityStatus, "INTEGER DEFAULT 0"}, + {kStateStatus, "INTEGER DEFAULT 0"}, + {kZipCodeStatus, "INTEGER DEFAULT 0"}, + {kSortingCodeStatus, "INTEGER DEFAULT 0"}, + {kCountryCodeStatus, "INTEGER DEFAULT 0"}, + {kApartmentNumber, "VARCHAR"}, + {kFloor, "VARCHAR"}, + {kApartmentNumberStatus, "INTEGER DEFAULT 0"}, + {kFloorStatus, "INTEGER DEFAULT 0"}}); } bool AutofillTable::InitProfileEmailsTable() { - if (!db_->DoesTableExist("autofill_profile_emails")) { - if (!db_->Execute("CREATE TABLE autofill_profile_emails ( " - "guid VARCHAR, " - "email VARCHAR)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kAutofillProfileEmailsTable, + {{kGuid, "VARCHAR"}, {kEmail, "VARCHAR"}}); } bool AutofillTable::InitProfilePhonesTable() { - if (!db_->DoesTableExist("autofill_profile_phones")) { - if (!db_->Execute("CREATE TABLE autofill_profile_phones ( " - "guid VARCHAR, " - "number VARCHAR)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kAutofillProfilePhonesTable, + {{kGuid, "VARCHAR"}, {kNumber, "VARCHAR"}}); } bool AutofillTable::InitProfileBirthdatesTable() { - if (!db_->DoesTableExist("autofill_profile_birthdates")) { - if (!db_->Execute("CREATE TABLE autofill_profile_birthdates ( " - "guid VARCHAR, " - "day INTEGER DEFAULT 0, " - "month INTEGER DEFAULT 0, " - "year INTEGER DEFAULT 0)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kAutofillProfileBirthdatesTable, + {{kGuid, "VARCHAR"}, + {kDay, "INTEGER DEFAULT 0"}, + {kMonth, "INTEGER DEFAULT 0"}, + {kYear, "INTEGER DEFAULT 0"}}); } bool AutofillTable::InitMaskedCreditCardsTable() { - if (!db_->DoesTableExist("masked_credit_cards")) { - if (!db_->Execute("CREATE TABLE masked_credit_cards (" - "id VARCHAR," - "name_on_card VARCHAR," - "network VARCHAR," - "last_four VARCHAR," - "exp_month INTEGER DEFAULT 0," - "exp_year INTEGER DEFAULT 0, " - "bank_name VARCHAR, " - "nickname VARCHAR, " - "card_issuer INTEGER DEFAULT 0, " - "instrument_id INTEGER DEFAULT 0, " - "virtual_card_enrollment_state INTEGER DEFAULT 0, " - "card_art_url VARCHAR, " - "product_description VARCHAR)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists( + db_, kMaskedCreditCardsTable, + {{kId, "VARCHAR"}, + {kNameOnCard, "VARCHAR"}, + {kNetwork, "VARCHAR"}, + {kLastFour, "VARCHAR"}, + {kExpMonth, "INTEGER DEFAULT 0"}, + {kExpYear, "INTEGER DEFAULT 0"}, + {kBankName, "VARCHAR"}, + {kNickname, "VARCHAR"}, + {kCardIssuer, "INTEGER DEFAULT 0"}, + {kInstrumentId, "INTEGER DEFAULT 0"}, + {kVirtualCardEnrollmentState, "INTEGER DEFAULT 0"}, + {kCardArtUrl, "VARCHAR"}, + {kProductDescription, "VARCHAR"}}); } bool AutofillTable::InitUnmaskedCreditCardsTable() { - if (!db_->DoesTableExist("unmasked_credit_cards")) { - if (!db_->Execute("CREATE TABLE unmasked_credit_cards (" - "id VARCHAR," - "card_number_encrypted VARCHAR," - "unmask_date INTEGER NOT NULL DEFAULT 0)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kUnmaskedCreditCardsTable, + {{kId, "VARCHAR"}, + {kCardNumberEncrypted, "VARCHAR"}, + {kUnmaskDate, "INTEGER NOT NULL DEFAULT 0"}}); } bool AutofillTable::InitServerCardMetadataTable() { - if (!db_->DoesTableExist("server_card_metadata")) { - if (!db_->Execute("CREATE TABLE server_card_metadata (" - "id VARCHAR NOT NULL," - "use_count INTEGER NOT NULL DEFAULT 0, " - "use_date INTEGER NOT NULL DEFAULT 0, " - "billing_address_id VARCHAR)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kServerCardMetadataTable, + {{kId, "VARCHAR NOT NULL"}, + {kUseCount, "INTEGER NOT NULL DEFAULT 0"}, + {kUseDate, "INTEGER NOT NULL DEFAULT 0"}, + {kBillingAddressId, "VARCHAR"}}); } bool AutofillTable::InitServerAddressesTable() { - if (!db_->DoesTableExist("server_addresses")) { - // The space after language_code is necessary to match what sqlite does - // when it appends the column in migration. - if (!db_->Execute("CREATE TABLE server_addresses (" - "id VARCHAR," - "company_name VARCHAR," - "street_address VARCHAR," - "address_1 VARCHAR," - "address_2 VARCHAR," - "address_3 VARCHAR," - "address_4 VARCHAR," - "postal_code VARCHAR," - "sorting_code VARCHAR," - "country_code VARCHAR," - "language_code VARCHAR, " // Space required. - "recipient_name VARCHAR, " // Ditto. - "phone_number VARCHAR)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kServerAddressesTable, + {{kId, "VARCHAR"}, + {kCompanyName, "VARCHAR"}, + {kStreetAddress, "VARCHAR"}, + {kAddress1, "VARCHAR"}, + {kAddress2, "VARCHAR"}, + {kAddress3, "VARCHAR"}, + {kAddress4, "VARCHAR"}, + {kPostalCode, "VARCHAR"}, + {kSortingCode, "VARCHAR"}, + {kCountryCode, "VARCHAR"}, + {kLanguageCode, "VARCHAR"}, + {kRecipientName, "VARCHAR"}, + {kPhoneNumber, "VARCHAR"}}); } bool AutofillTable::InitServerAddressMetadataTable() { - if (!db_->DoesTableExist("server_address_metadata")) { - if (!db_->Execute("CREATE TABLE server_address_metadata (" - "id VARCHAR NOT NULL," - "use_count INTEGER NOT NULL DEFAULT 0, " - "use_date INTEGER NOT NULL DEFAULT 0, " - "has_converted BOOL NOT NULL DEFAULT FALSE)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists( + db_, kServerAddressMetadataTable, + {{kId, "VARCHAR NOT NULL"}, + {kUseCount, "INTEGER NOT NULL DEFAULT 0"}, + {kUseDate, "INTEGER NOT NULL DEFAULT 0"}, + {kHasConverted, "BOOL NOT NULL DEFAULT FALSE"}}); } bool AutofillTable::InitAutofillSyncMetadataTable() { - if (!db_->DoesTableExist("autofill_sync_metadata")) { - if (!db_->Execute("CREATE TABLE autofill_sync_metadata (" - "model_type INTEGER NOT NULL, " - "storage_key VARCHAR NOT NULL, " - "value BLOB, " - "PRIMARY KEY (model_type, storage_key))")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kAutofillSyncMetadataTable, + {{kModelType, "INTEGER NOT NULL"}, + {kStorageKey, "VARCHAR NOT NULL"}, + {kValue, "BLOB"}}, + {kModelType, kStorageKey}); } bool AutofillTable::InitModelTypeStateTable() { - if (!db_->DoesTableExist("autofill_model_type_state")) { - if (!db_->Execute("CREATE TABLE autofill_model_type_state (" - "model_type INTEGER NOT NULL PRIMARY KEY, value BLOB)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists( + db_, kAutofillModelTypeStateTable, + {{kModelType, "INTEGER NOT NULL PRIMARY KEY"}, {kValue, "BLOB"}}); } bool AutofillTable::InitPaymentsCustomerDataTable() { - if (!db_->DoesTableExist("payments_customer_data")) { - if (!db_->Execute("CREATE TABLE payments_customer_data " - "(customer_id VARCHAR)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kPaymentsCustomerDataTable, + {{kCustomerId, "VARCHAR"}}); } bool AutofillTable::InitPaymentsUPIVPATable() { - if (!db_->DoesTableExist("payments_upi_vpa")) { - if (!db_->Execute("CREATE TABLE payments_upi_vpa (" - "vpa VARCHAR)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kPaymentsUpiVpaTable, {{kVpa, "VARCHAR"}}); } bool AutofillTable::InitServerCreditCardCloudTokenDataTable() { - if (!db_->DoesTableExist("server_card_cloud_token_data")) { - if (!db_->Execute("CREATE TABLE server_card_cloud_token_data ( " - "id VARCHAR, " - "suffix VARCHAR, " - "exp_month INTEGER DEFAULT 0, " - "exp_year INTEGER DEFAULT 0, " - "card_art_url VARCHAR, " - "instrument_token VARCHAR)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kServerCardCloudTokenDataTable, + {{kId, "VARCHAR"}, + {kSuffix, "VARCHAR"}, + {kExpMonth, "INTEGER DEFAULT 0"}, + {kExpYear, "INTEGER DEFAULT 0"}, + {kCardArtUrl, "VARCHAR"}, + {kInstrumentToken, "VARCHAR"}}); } bool AutofillTable::InitOfferDataTable() { - if (!db_->DoesTableExist("offer_data")) { - if (!db_->Execute("CREATE TABLE offer_data ( " - "offer_id UNSIGNED LONG, " - "offer_reward_amount VARCHAR, " - "expiry UNSIGNED LONG, " - "offer_details_url VARCHAR, " - "merchant_domain VARCHAR, " - "promo_code VARCHAR, " - "value_prop_text VARCHAR, " - "see_details_text VARCHAR, " - "usage_instructions_text VARCHAR)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists(db_, kOfferDataTable, + {{kOfferId, "UNSIGNED LONG"}, + {kOfferRewardAmount, "VARCHAR"}, + {kExpiry, "UNSIGNED LONG"}, + {kOfferDetailsUrl, "VARCHAR"}, + {kMerchantDomain, "VARCHAR"}, + {kPromoCode, "VARCHAR"}, + {kValuePropText, "VARCHAR"}, + {kSeeDetailsText, "VARCHAR"}, + {kUsageInstructionsText, "VARCHAR"}}); } bool AutofillTable::InitOfferEligibleInstrumentTable() { - if (!db_->DoesTableExist("offer_eligible_instrument")) { - if (!db_->Execute("CREATE TABLE offer_eligible_instrument ( " - "offer_id UNSIGNED LONG," - "instrument_id UNSIGNED LONG)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists( + db_, kOfferEligibleInstrumentTable, + {{kOfferId, "UNSIGNED LONG"}, {kInstrumentId, "UNSIGNED LONG"}}); } bool AutofillTable::InitOfferMerchantDomainTable() { - if (!db_->DoesTableExist("offer_merchant_domain")) { - if (!db_->Execute("CREATE TABLE offer_merchant_domain ( " - "offer_id UNSIGNED LONG," - "merchant_domain VARCHAR)")) { - NOTREACHED(); - return false; - } - } - return true; + return CreateTableIfNotExists( + db_, kOfferMerchantDomainTable, + {{kOfferId, "UNSIGNED LONG"}, {kMerchantDomain, "VARCHAR"}}); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.h b/chromium/components/autofill/core/browser/webdata/autofill_table.h index 351bc47b789..dbc504ef185 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h @@ -39,6 +39,7 @@ class AutofillTableTest; class CreditCard; struct CreditCardCloudTokenData; struct FormFieldData; +class IBAN; struct PaymentsCustomerData; // This class manages the various Autofill tables within the SQLite database @@ -180,6 +181,7 @@ struct PaymentsCustomerData; // first_last_name_status // conjunction_last_name_status // second_last_name_status +// full_name_status // full_name_with_honorific_prefix_status // Each token of the names has an additional validation // status that indicates if Autofill parsed the value out @@ -322,6 +324,19 @@ struct PaymentsCustomerData; // database, but always returned as an empty string in // CreditCard. Added in version 71. // +// ibans This table contains International Bank Account +// Number(IBAN) data added by the user. The columns are +// standard entries in an Iban form. +// +// guid A guid string to uniquely identify the IBAN. +// use_count The number of times this IBAN has been used to fill +// a form. +// use_date The date this IBAN was last used to fill a form, +// in time_t. +// value Actual value of the IBAN (the bank account number). +// nickname A nickname for the IBAN, entered by the user. +// +// // server_addresses This table contains Autofill address data synced from // the wallet server. It's basically the same as the // autofill_profiles table but locally immutable. @@ -390,7 +405,7 @@ struct PaymentsCustomerData; // payments_upi_vpa Contains saved UPI/VPA payment data. // https://en.wikipedia.org/wiki/Unified_Payments_Interface // -// vpa_id A string representing the UPI ID (a.k.a. VPA) value. +// vpa A string representing the UPI ID (a.k.a. VPA) value. // // offer_data The data for Autofill offers which will be presented in // payments autofill flows. @@ -532,6 +547,22 @@ class AutofillTable : public WebDatabaseTable, // the given ones. void SetServerProfiles(const std::vector<AutofillProfile>& profiles); + // Records a single IBAN in the iban table. + bool AddIBAN(const IBAN& iban); + + // Updates the database values for the specified IBAN. + bool UpdateIBAN(const IBAN& iban); + + // Removes a row from the ibans table. |guid| is the identifier of the + // IBAN to remove. + bool RemoveIBAN(const std::string& guid); + + // Retrieves an IBAN with the given |guid|. + std::unique_ptr<IBAN> GetIBAN(const std::string& guid); + + // Retrieves the local IBANs in the database. + bool GetIBANs(std::vector<std::unique_ptr<IBAN>>* ibans); + // Records a single credit card in the credit_cards table. bool AddCreditCard(const CreditCard& credit_card); @@ -679,27 +710,6 @@ class AutofillTable : public WebDatabaseTable, // functions in this class. The implementation of a function such as // GetCreditCard may change over time, but MigrateToVersionXX should never // change. - bool MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields(); - bool MigrateToVersion55MergeAutofillDatesTable(); - bool MigrateToVersion56AddProfileLanguageCodeForFormatting(); - bool MigrateToVersion57AddFullNameField(); - bool MigrateToVersion60AddServerCards(); - bool MigrateToVersion61AddUsageStats(); - bool MigrateToVersion62AddUsageStatsForUnmaskedCards(); - bool MigrateToVersion63AddServerRecipientName(); - bool MigrateToVersion64AddUnmaskDate(); - bool MigrateToVersion65AddServerMetadataTables(); - bool MigrateToVersion66AddCardBillingAddress(); - bool MigrateToVersion67AddMaskedCardBillingAddress(); - bool MigrateToVersion70AddSyncMetadata(); - bool MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata(); - bool MigrateToVersion72RenameCardTypeToIssuerNetwork(); - bool MigrateToVersion73AddMaskedCardBankName(); - bool MigrateToVersion74AddServerCardTypeColumn(); - bool MigrateToVersion75AddProfileValidityBitfieldColumn(); - bool MigrateToVersion78AddModelTypeColumns(); - bool MigrateToVersion80AddIsClientValidityStatesUpdatedColumn(); - bool MigrateToVersion81CleanUpWrongModelTypeData(); bool MigrateToVersion83RemoveServerCardTypeColumn(); bool MigrateToVersion84AddNicknameColumn(); bool MigrateToVersion85AddCardIssuerColumnToMaskedCreditCard(); @@ -720,6 +730,7 @@ class AutofillTable : public WebDatabaseTable, bool MigrateToVersion101RemoveCreditCardArtImageTable(); bool MigrateToVersion102AddAutofillBirthdatesTable(); bool MigrateToVersion104AddProductDescriptionColumn(); + bool MigrateToVersion105AddAutofillIBANTable(); // Max data length saved in the table, AKA the maximum length allowed for // form data. @@ -805,8 +816,15 @@ class AutofillTable : public WebDatabaseTable, bool DeleteFromMaskedCreditCards(const std::string& id); bool DeleteFromUnmaskedCreditCards(const std::string& id); + // Helper function extracting common code between `SetServerProfiles()` and + // `SetServerAddressData()`. + void SetServerProfilesAndMetadata( + const std::vector<AutofillProfile>& profiles, + bool update_metadata); + bool InitMainTable(); bool InitCreditCardsTable(); + bool InitIBANsTable(); bool InitProfilesTable(); bool InitProfileAddressesTable(); bool InitProfileNamesTable(); 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 48029c5ab8e..9d8f77d6dfe 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc @@ -1230,8 +1230,7 @@ TEST_F(AutofillTableTest, TEST_F(AutofillTableTest, AutofillProfile_StructuredNames) { // Enable the structured names and birthdates. scoped_feature_list_.InitWithFeatures( - {features::kAutofillEnableSupportForMoreStructureInNames, - features::kAutofillEnableCompatibilitySupportForBirthdates}, + {features::kAutofillEnableSupportForMoreStructureInNames}, {features::kAutofillEnableSupportForMoreStructureInAddresses}); AutofillProfile home_profile; @@ -1283,7 +1282,7 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredNames) { home_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"18181234567"); home_profile.SetRawInfoAsInt(BIRTHDATE_DAY, 14); home_profile.SetRawInfoAsInt(BIRTHDATE_MONTH, 3); - home_profile.SetRawInfoAsInt(BIRTHDATE_YEAR_4_DIGITS, 1997); + home_profile.SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, 1997); home_profile.set_disallow_settings_visible_updates(true); home_profile.set_language_code("en"); Time pre_creation_time = AutofillClock::Now(); @@ -1380,7 +1379,7 @@ TEST_F(AutofillTableTest, AutofillProfile_StructuredNames) { billing_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"18181230000"); billing_profile.SetRawInfoAsInt(BIRTHDATE_DAY, 4); billing_profile.SetRawInfoAsInt(BIRTHDATE_MONTH, 5); - billing_profile.SetRawInfoAsInt(BIRTHDATE_YEAR_4_DIGITS, 1977); + billing_profile.SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, 1977); Time pre_modification_time_2 = AutofillClock::Now(); EXPECT_TRUE(table_->UpdateAutofillProfile(billing_profile)); @@ -1412,9 +1411,8 @@ TEST_F(AutofillTableTest, AutofillProfile) { // Disable the structured names since this test is only applicable if // structured names are not used. scoped_feature_list_.InitWithFeatures( - {features::kAutofillEnableCompatibilitySupportForBirthdates}, - {features::kAutofillEnableSupportForMoreStructureInAddresses, - features::kAutofillEnableSupportForMoreStructureInNames}); + {}, {features::kAutofillEnableSupportForMoreStructureInAddresses, + features::kAutofillEnableSupportForMoreStructureInNames}); // Add a 'Home' profile with non-default data. The specific values are not // important. @@ -1436,7 +1434,7 @@ TEST_F(AutofillTableTest, AutofillProfile) { home_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"18181234567"); home_profile.SetRawInfoAsInt(BIRTHDATE_DAY, 14); home_profile.SetRawInfoAsInt(BIRTHDATE_MONTH, 3); - home_profile.SetRawInfoAsInt(BIRTHDATE_YEAR_4_DIGITS, 1997); + home_profile.SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, 1997); home_profile.set_language_code("en"); Time pre_creation_time = AutofillClock::Now(); @@ -1518,7 +1516,7 @@ TEST_F(AutofillTableTest, AutofillProfile) { billing_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"18181230000"); billing_profile.SetRawInfoAsInt(BIRTHDATE_DAY, 4); billing_profile.SetRawInfoAsInt(BIRTHDATE_MONTH, 5); - billing_profile.SetRawInfoAsInt(BIRTHDATE_YEAR_4_DIGITS, 1977); + billing_profile.SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, 1977); Time pre_modification_time_2 = AutofillClock::Now(); EXPECT_TRUE(table_->UpdateAutofillProfile(billing_profile)); @@ -1544,6 +1542,71 @@ TEST_F(AutofillTableTest, AutofillProfile) { EXPECT_FALSE(db_profile); } +TEST_F(AutofillTableTest, IBAN) { + // Add a valid IBAN. + IBAN iban; + std::string guid = base::GenerateGUID(); + iban.set_guid(guid); + iban.SetRawInfo(IBAN_VALUE, u"IE12 BOFI 9000 0112 3456 78"); + iban.set_nickname(u"My doctor's IBAN"); + + EXPECT_TRUE(table_->AddIBAN(iban)); + + // Get the inserted Iban. + std::unique_ptr<IBAN> db_iban = table_->GetIBAN(iban.guid()); + ASSERT_TRUE(db_iban); + EXPECT_EQ(guid, db_iban->guid()); + sql::Statement s_work(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT guid, use_count, use_date, " + "value, nickname FROM ibans WHERE guid = ?")); + s_work.BindString(0, iban.guid()); + ASSERT_TRUE(s_work.is_valid()); + ASSERT_TRUE(s_work.Step()); + EXPECT_FALSE(s_work.Step()); + + // Add another valid IBAN. + IBAN another_iban; + std::string another_guid = base::GenerateGUID(); + another_iban.set_guid(another_guid); + another_iban.SetRawInfo(IBAN_VALUE, u"DE91 1000 0000 0123 4567 89"); + another_iban.set_nickname(u"My brother's IBAN"); + + EXPECT_TRUE(table_->AddIBAN(another_iban)); + + db_iban = table_->GetIBAN(another_iban.guid()); + ASSERT_TRUE(db_iban); + + EXPECT_EQ(another_guid, db_iban->guid()); + sql::Statement s_target(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT guid, use_count, use_date, " + "value, nickname FROM ibans WHERE guid = ?")); + s_target.BindString(0, another_iban.guid()); + ASSERT_TRUE(s_target.is_valid()); + ASSERT_TRUE(s_target.Step()); + EXPECT_FALSE(s_target.Step()); + + // Update the another_iban. + another_iban.set_origin("Interactive Autofill dialog"); + another_iban.SetRawInfo(IBAN_VALUE, u"GB98 MIDL 0700 9312 3456 78"); + another_iban.set_nickname(u"My teacher's IBAN"); + EXPECT_TRUE(table_->UpdateIBAN(another_iban)); + db_iban = table_->GetIBAN(another_iban.guid()); + ASSERT_TRUE(db_iban); + EXPECT_EQ(another_guid, db_iban->guid()); + sql::Statement s_target_updated(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT guid, use_count, use_date, " + "value, nickname FROM ibans WHERE guid = ?")); + s_target_updated.BindString(0, another_iban.guid()); + ASSERT_TRUE(s_target_updated.is_valid()); + ASSERT_TRUE(s_target_updated.Step()); + EXPECT_FALSE(s_target_updated.Step()); + + // Remove the 'Target' IBAN. + EXPECT_TRUE(table_->RemoveIBAN(another_iban.guid())); + db_iban = table_->GetIBAN(another_iban.guid()); + EXPECT_FALSE(db_iban); +} + TEST_F(AutofillTableTest, CreditCard) { // Add a 'Work' credit card. CreditCard work_creditcard; @@ -1646,9 +1709,6 @@ TEST_F(AutofillTableTest, AddFullServerCreditCard) { } TEST_F(AutofillTableTest, UpdateAutofillProfile) { - scoped_feature_list_.InitWithFeatures( - {features::kAutofillEnableCompatibilitySupportForBirthdates}, {}); - // Add a profile to the db. AutofillProfile profile; profile.SetRawInfo(NAME_FIRST, u"John"); @@ -1665,7 +1725,7 @@ TEST_F(AutofillTableTest, UpdateAutofillProfile) { profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, u"18181234567"); profile.SetRawInfoAsInt(BIRTHDATE_DAY, 14); profile.SetRawInfoAsInt(BIRTHDATE_MONTH, 3); - profile.SetRawInfoAsInt(BIRTHDATE_YEAR_4_DIGITS, 1997); + profile.SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, 1997); profile.set_language_code("en"); profile.FinalizeAfterImport(); table_->AddAutofillProfile(profile); 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 01ed4da16e9..d17ada8aca2 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 @@ -39,7 +39,7 @@ #include "components/sync/protocol/entity_metadata.pb.h" #include "components/sync/protocol/entity_specifics.pb.h" #include "components/sync/protocol/model_type_state.pb.h" -#include "components/sync/test/model/mock_model_type_change_processor.h" +#include "components/sync/test/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 5aa6ca0f788..88f055352d4 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 @@ -10,6 +10,7 @@ #include "base/logging.h" #include "components/autofill/core/browser/data_model/autofill_offer_data.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" +#include "components/autofill/core/browser/metrics/payments/offers_metrics.h" #include "components/autofill/core/browser/webdata/autofill_sync_bridge_util.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" @@ -181,7 +182,7 @@ void AutofillWalletOfferSyncBridge::MergeRemoteData( if (offer_valid) { offer_data.push_back(AutofillOfferDataFromOfferSpecifics(specifics)); } - AutofillMetrics::LogSyncedOfferDataBeingValid(offer_valid); + autofill_metrics::LogSyncedOfferDataBeingValid(offer_valid); } AutofillTable* table = GetAutofillTable(); 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 9bdf7467cb3..4d6c4bcb529 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 @@ -35,7 +35,7 @@ #include "components/sync/protocol/entity_data.h" #include "components/sync/protocol/entity_specifics.pb.h" #include "components/sync/protocol/model_type_state.pb.h" -#include "components/sync/test/model/mock_model_type_change_processor.h" +#include "components/sync/test/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_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc index 2085d07eb07..4bbc99bb2d8 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 @@ -43,8 +43,8 @@ #include "components/sync/protocol/entity_metadata.pb.h" #include "components/sync/protocol/entity_specifics.pb.h" #include "components/sync/protocol/model_type_state.pb.h" -#include "components/sync/test/model/mock_model_type_change_processor.h" -#include "components/sync/test/model/test_matchers.h" +#include "components/sync/test/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_backend_impl.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc index 141895fece2..92282bc0297 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc @@ -16,6 +16,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/data_model/credit_card_cloud_token_data.h" +#include "components/autofill/core/browser/data_model/iban.h" #include "components/autofill/core/browser/geo/autofill_country.h" #include "components/autofill/core/browser/payments/payments_customer_data.h" #include "components/autofill/core/browser/webdata/autofill_change.h" @@ -513,6 +514,73 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardMetadata( return WebDatabase::COMMIT_NEEDED; } +std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetIBANs( + WebDatabase* db) { + DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); + std::vector<std::unique_ptr<IBAN>> ibans; + AutofillTable::FromWebDatabase(db)->GetIBANs(&ibans); + + return std::make_unique<WDResult<std::vector<std::unique_ptr<IBAN>>>>( + AUTOFILL_IBANS_RESULT, std::move(ibans)); +} + +WebDatabase::State AutofillWebDataBackendImpl::AddIBAN(const IBAN& iban, + WebDatabase* db) { + DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); + if (!AutofillTable::FromWebDatabase(db)->AddIBAN(iban)) { + NOTREACHED(); + return WebDatabase::COMMIT_NOT_NEEDED; + } + + for (auto& db_observer : db_observer_list_) { + db_observer.IBANChanged(IBANChange(IBANChange::ADD, iban.guid(), &iban)); + } + return WebDatabase::COMMIT_NEEDED; +} + +WebDatabase::State AutofillWebDataBackendImpl::UpdateIBAN(const IBAN& iban, + WebDatabase* db) { + DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); + // It is currently valid to try to update a missing IBAN. We simply drop + // the write and the caller will detect this on the next refresh. + std::unique_ptr<IBAN> original_iban = + AutofillTable::FromWebDatabase(db)->GetIBAN(iban.guid()); + if (!original_iban) + return WebDatabase::COMMIT_NOT_NEEDED; + + if (!AutofillTable::FromWebDatabase(db)->UpdateIBAN(iban)) { + NOTREACHED(); + return WebDatabase::COMMIT_NOT_NEEDED; + } + + for (auto& db_observer : db_observer_list_) { + db_observer.IBANChanged(IBANChange(IBANChange::UPDATE, iban.guid(), &iban)); + } + return WebDatabase::COMMIT_NEEDED; +} + +WebDatabase::State AutofillWebDataBackendImpl::RemoveIBAN( + const std::string& guid, + WebDatabase* db) { + DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); + std::unique_ptr<IBAN> iban = + AutofillTable::FromWebDatabase(db)->GetIBAN(guid); + if (!iban) { + NOTREACHED(); + return WebDatabase::COMMIT_NOT_NEEDED; + } + + if (!AutofillTable::FromWebDatabase(db)->RemoveIBAN(guid)) { + NOTREACHED(); + return WebDatabase::COMMIT_NOT_NEEDED; + } + + for (auto& db_observer : db_observer_list_) { + db_observer.IBANChanged(IBANChange(IBANChange::REMOVE, guid, iban.get())); + } + return WebDatabase::COMMIT_NEEDED; +} + WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressMetadata( const AutofillProfile& profile, WebDatabase* db) { diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h index 8e4f8448aad..69813e85a11 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h @@ -31,6 +31,7 @@ namespace autofill { class AutofillProfile; class AutofillWebDataServiceObserverOnDBSequence; class CreditCard; +class IBAN; // Backend implementation for the AutofillWebDataService. This class runs on the // DB sequence, as it handles reads and writes to the WebDatabase, and functions @@ -177,6 +178,18 @@ class AutofillWebDataBackendImpl std::unique_ptr<WDTypedResult> GetCreditCards(WebDatabase* db); std::unique_ptr<WDTypedResult> GetServerCreditCards(WebDatabase* db); + // Returns a vector of local IBANs from the web database. + std::unique_ptr<WDTypedResult> GetIBANs(WebDatabase* db); + + // Adds an IBAN to the web database. Valid only for local IBANs. + WebDatabase::State AddIBAN(const IBAN& iban, WebDatabase* db); + + // Updates an IBAN in the web database. Valid only for local IBANs. + WebDatabase::State UpdateIBAN(const IBAN& iban, WebDatabase* db); + + // Removes an IBAN from the web database. Valid only for local IBANs. + WebDatabase::State RemoveIBAN(const std::string& guid, WebDatabase* db); + // Server credit cards can be masked (only last 4 digits stored) or unmasked // (all data stored). These toggle between the two states. WebDatabase::State UnmaskServerCreditCard(const CreditCard& card, 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 162115635ca..ce6246dc162 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc @@ -13,6 +13,7 @@ #include "components/autofill/core/browser/data_model/autofill_offer_data.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/browser/data_model/iban.h" #include "components/autofill/core/browser/geo/autofill_country.h" #include "components/autofill/core/browser/webdata/autofill_change.h" #include "components/autofill/core/browser/webdata/autofill_entry.h" @@ -213,6 +214,32 @@ void AutofillWebDataService::AddFullServerCreditCard( autofill_backend_, credit_card)); } +void AutofillWebDataService::AddIBAN(const IBAN& iban) { + wdbs_->ScheduleDBTask(FROM_HERE, + base::BindOnce(&AutofillWebDataBackendImpl::AddIBAN, + autofill_backend_, iban)); +} + +WebDataServiceBase::Handle AutofillWebDataService::GetIBANs( + WebDataServiceConsumer* consumer) { + return wdbs_->ScheduleDBTaskWithResult( + FROM_HERE, + base::BindOnce(&AutofillWebDataBackendImpl::GetIBANs, autofill_backend_), + consumer); +} + +void AutofillWebDataService::UpdateIBAN(const IBAN& iban) { + wdbs_->ScheduleDBTask(FROM_HERE, + base::BindOnce(&AutofillWebDataBackendImpl::UpdateIBAN, + autofill_backend_, iban)); +} + +void AutofillWebDataService::RemoveIBAN(const std::string& guid) { + wdbs_->ScheduleDBTask(FROM_HERE, + base::BindOnce(&AutofillWebDataBackendImpl::RemoveIBAN, + autofill_backend_, guid)); +} + WebDataServiceBase::Handle AutofillWebDataService::GetCreditCards( WebDataServiceConsumer* consumer) { return wdbs_->ScheduleDBTaskWithResult( diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h index cb92feeb2dd..5aaf22356b8 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h @@ -35,6 +35,7 @@ class AutofillWebDataBackendImpl; class AutofillWebDataServiceObserverOnDBSequence; class AutofillWebDataServiceObserverOnUISequence; class CreditCard; +class IBAN; // API for Autofill web data. class AutofillWebDataService : public WebDataServiceBase { @@ -114,6 +115,22 @@ class AutofillWebDataService : public WebDataServiceBase { base::RepeatingCallback<void(const AutofillProfileDeepChange&)> change_cb); + // Schedules a task to add IBAN to the web database. + void AddIBAN(const IBAN& iban); + + // Initiates the request for local IBANs. The method + // OnWebDataServiceRequestDone of |consumer| gets called when the request is + // finished, with the IBAN included in the argument |result|. The consumer + // owns the IBAN. + WebDataServiceBase::Handle GetIBANs(WebDataServiceConsumer* consumer); + + // Schedules a task to update iban in the web database. + void UpdateIBAN(const IBAN& iban); + + // Schedules a task to remove an IBAN from the web database. + // |guid| is the identifier of the IBAN to remove. + void RemoveIBAN(const std::string& guid); + // Schedules a task to add credit card to the web database. void AddCreditCard(const CreditCard& credit_card); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h index 60820bb1a51..6c81abdb5e1 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h @@ -23,6 +23,10 @@ class AutofillWebDataServiceObserverOnDBSequence { // the WebDatabase. virtual void CreditCardChanged(const CreditCardChange& change) {} + // Called on DB sequence when an IBAN has been added/removed/updated in + // the WebDatabase. + virtual void IBANChanged(const IBANChange& change) {} + protected: virtual ~AutofillWebDataServiceObserverOnDBSequence() {} }; diff --git a/chromium/components/autofill/core/common/BUILD.gn b/chromium/components/autofill/core/common/BUILD.gn index 0660e3adc27..f4a0c89ee8e 100644 --- a/chromium/components/autofill/core/common/BUILD.gn +++ b/chromium/components/autofill/core/common/BUILD.gn @@ -5,6 +5,8 @@ static_library("common") { sources = [ "aliases.h", + "autocomplete_parsing_util.cc", + "autocomplete_parsing_util.h", "autofill_clock.cc", "autofill_clock.h", "autofill_constants.cc", @@ -21,6 +23,9 @@ static_library("common") { "autofill_payments_features.h", "autofill_prefs.cc", "autofill_prefs.h", + "autofill_regex_constants.h", + "autofill_regexes.cc", + "autofill_regexes.h", "autofill_switches.cc", "autofill_switches.h", "autofill_tick_clock.cc", @@ -40,9 +45,12 @@ static_library("common") { "form_field_data_predictions.h", "gaia_id_hash.cc", "gaia_id_hash.h", + "html_field_types.cc", + "html_field_types.h", "language_code.h", "logging/log_buffer.cc", "logging/log_buffer.h", + "logging/log_macros.h", "password_form_fill_data.cc", "password_form_fill_data.h", "password_form_generation_data.h", @@ -96,10 +104,11 @@ component("features") { source_set("unit_tests") { testonly = true sources = [ + "autocomplete_parsing_util_unittest.cc", "autofill_internals/log_message_unittest.cc", "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", diff --git a/chromium/components/autofill/core/common/autocomplete_parsing_util.cc b/chromium/components/autofill/core/common/autocomplete_parsing_util.cc new file mode 100644 index 00000000000..dda22921127 --- /dev/null +++ b/chromium/components/autofill/core/common/autocomplete_parsing_util.cc @@ -0,0 +1,297 @@ +// Copyright 2022 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/autocomplete_parsing_util.h" + +#include <vector> + +#include "base/containers/fixed_flat_map.h" +#include "base/notreached.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_regexes.h" +#include "components/autofill/core/common/autofill_util.h" + +namespace autofill { + +namespace { + +// Returns true iff the `token` is a type hint for a contact field, as +// specified in the implementation section of http://is.gd/whatwg_autocomplete +// Note that "fax" and "pager" are intentionally ignored, as Chrome does not +// support filling either type of information. +bool IsContactTypeHint(const std::string& token) { + return token == "home" || token == "work" || token == "mobile"; +} + +// Returns true iff the `token` is a type hint appropriate for a field of the +// given `field_type`, as specified in the implementation section of +// http://is.gd/whatwg_autocomplete +bool ContactTypeHintMatchesFieldType(const std::string& token, + HtmlFieldType field_type) { + // The "home" and "work" type hints are only appropriate for email and phone + // number field types. + if (token == "home" || token == "work") { + return field_type == HTML_TYPE_EMAIL || + (field_type >= HTML_TYPE_TEL && + field_type <= HTML_TYPE_TEL_LOCAL_SUFFIX); + } + + // The "mobile" type hint is only appropriate for phone number field types. + // Note that "fax" and "pager" are intentionally ignored, as Chrome does not + // support filling either type of information. + if (token == "mobile") { + return field_type >= HTML_TYPE_TEL && + field_type <= HTML_TYPE_TEL_LOCAL_SUFFIX; + } + + return false; +} + +// Rationalizes the HTML `type` of `field`, based on the fields properties. At +// the moment only `max_length` is considered. For example, a max_length of 4 +// might indicate a 4 digit year. +// In case no rationalization rule applies, the original type is returned. +HtmlFieldType RationalizeAutocompleteType(HtmlFieldType type, + const FormFieldData& field) { + // (original-type, max-length) -> new-type + static constexpr auto rules = + base::MakeFixedFlatMap<std::pair<HtmlFieldType, uint64_t>, HtmlFieldType>( + { + {{HTML_TYPE_ADDITIONAL_NAME, 1}, + HTML_TYPE_ADDITIONAL_NAME_INITIAL}, + {{HTML_TYPE_CREDIT_CARD_EXP, 5}, + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, + {{HTML_TYPE_CREDIT_CARD_EXP, 7}, + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, + {{HTML_TYPE_CREDIT_CARD_EXP_YEAR, 2}, + HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR}, + {{HTML_TYPE_CREDIT_CARD_EXP_YEAR, 4}, + HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR}, + }); + + auto* it = rules.find(std::make_pair(type, field.max_length)); + return it == rules.end() ? type : it->second; +} + +// Chrome Autofill supports a subset of the field types listed at +// http://is.gd/whatwg_autocomplete. Returns the corresponding HtmlFieldType, if +// `value` matches any of them. +absl::optional<HtmlFieldType> ParseStandardizedAutocompleteAttribute( + base::StringPiece value) { + static constexpr auto standardized_attributes = + base::MakeFixedFlatMap<base::StringPiece, HtmlFieldType>({ + {"additional-name", HTML_TYPE_ADDITIONAL_NAME}, + {"address-level1", HTML_TYPE_ADDRESS_LEVEL1}, + {"address-level2", HTML_TYPE_ADDRESS_LEVEL2}, + {"address-level3", HTML_TYPE_ADDRESS_LEVEL3}, + {"address-line1", HTML_TYPE_ADDRESS_LINE1}, + {"address-line2", HTML_TYPE_ADDRESS_LINE2}, + {"address-line3", HTML_TYPE_ADDRESS_LINE3}, + {"bday-day", HTML_TYPE_BIRTHDATE_DAY}, + {"bday-month", HTML_TYPE_BIRTHDATE_MONTH}, + {"bday-year", HTML_TYPE_BIRTHDATE_YEAR}, + {"cc-csc", HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE}, + {"cc-exp", HTML_TYPE_CREDIT_CARD_EXP}, + {"cc-exp-month", HTML_TYPE_CREDIT_CARD_EXP_MONTH}, + {"cc-exp-year", HTML_TYPE_CREDIT_CARD_EXP_YEAR}, + {"cc-family-name", HTML_TYPE_CREDIT_CARD_NAME_LAST}, + {"cc-given-name", HTML_TYPE_CREDIT_CARD_NAME_FIRST}, + {"cc-name", HTML_TYPE_CREDIT_CARD_NAME_FULL}, + {"cc-number", HTML_TYPE_CREDIT_CARD_NUMBER}, + {"cc-type", HTML_TYPE_CREDIT_CARD_TYPE}, + {"country", HTML_TYPE_COUNTRY_CODE}, + {"country-name", HTML_TYPE_COUNTRY_NAME}, + {"email", HTML_TYPE_EMAIL}, + {"family-name", HTML_TYPE_FAMILY_NAME}, + {"given-name", HTML_TYPE_GIVEN_NAME}, + {"honorific-prefix", HTML_TYPE_HONORIFIC_PREFIX}, + {"name", HTML_TYPE_NAME}, + {"one-time-code", HTML_TYPE_ONE_TIME_CODE}, + {"organization", HTML_TYPE_ORGANIZATION}, + {"postal-code", HTML_TYPE_POSTAL_CODE}, + {"street-address", HTML_TYPE_STREET_ADDRESS}, + {"tel-area-code", HTML_TYPE_TEL_AREA_CODE}, + {"tel-country-code", HTML_TYPE_TEL_COUNTRY_CODE}, + {"tel-extension", HTML_TYPE_TEL_EXTENSION}, + {"tel", HTML_TYPE_TEL}, + {"tel-local", HTML_TYPE_TEL_LOCAL}, + {"tel-local-prefix", HTML_TYPE_TEL_LOCAL_PREFIX}, + {"tel-local-suffix", HTML_TYPE_TEL_LOCAL_SUFFIX}, + {"tel-national", HTML_TYPE_TEL_NATIONAL}, + {"transaction-amount", HTML_TYPE_TRANSACTION_AMOUNT}, + {"transaction-currency", HTML_TYPE_TRANSACTION_CURRENCY}, + }); + + auto* it = standardized_attributes.find(value); + return it != standardized_attributes.end() + ? absl::optional<HtmlFieldType>(it->second) + : absl::nullopt; +} + +// Maps `value`s that Autofill has proposed for the HTML autocomplete standard, +// but which are not standardized, to their HtmlFieldType. +absl::optional<HtmlFieldType> ParseProposedAutocompleteAttribute( + base::StringPiece value) { + static constexpr auto proposed_attributes = + base::MakeFixedFlatMap<base::StringPiece, HtmlFieldType>({ + {"address", HTML_TYPE_STREET_ADDRESS}, + {"coupon-code", HTML_TYPE_MERCHANT_PROMO_CODE}, + // TODO(crbug.com/1351760): Investigate if this mapping makes sense. + {"username", HTML_TYPE_EMAIL}, + }); + + auto* it = proposed_attributes.find(value); + return it != proposed_attributes.end() + ? absl::optional<HtmlFieldType>(it->second) + : absl::nullopt; +} + +// Maps non-standardized `value`s for the HTML autocomplete attribute to an +// HtmlFieldType. This is primarily a list of "reasonable guesses". +absl::optional<HtmlFieldType> ParseNonStandarizedAutocompleteAttribute( + base::StringPiece value) { + static constexpr auto non_standardized_attributes = + base::MakeFixedFlatMap<base::StringPiece, HtmlFieldType>({ + {"company", HTML_TYPE_ORGANIZATION}, + {"first-name", HTML_TYPE_GIVEN_NAME}, + {"gift-code", HTML_TYPE_MERCHANT_PROMO_CODE}, + {"iban", HTML_TYPE_IBAN}, + {"locality", HTML_TYPE_ADDRESS_LEVEL2}, + {"promo-code", HTML_TYPE_MERCHANT_PROMO_CODE}, + {"promotional-code", HTML_TYPE_MERCHANT_PROMO_CODE}, + {"promotion-code", HTML_TYPE_MERCHANT_PROMO_CODE}, + {"region", HTML_TYPE_ADDRESS_LEVEL1}, + {"tel-ext", HTML_TYPE_TEL_EXTENSION}, + {"upi", HTML_TYPE_UPI_VPA}, + {"upi-vpa", HTML_TYPE_UPI_VPA}, + }); + + auto* it = non_standardized_attributes.find(value); + return it != non_standardized_attributes.end() + ? absl::optional<HtmlFieldType>(it->second) + : absl::nullopt; +} + +// If the autocomplete `value` doesn't match any of Autofill's supported values, +// Autofill should remain enabled for good intended values. This function checks +// if there is reason to believe so, by matching `value` against patterns like +// "address". +// Ignoring autocomplete="off" and alike is treated separately in +// `ParseFieldTypesFromAutocompleteAttributes()`. +bool ShouldIgnoreAutocompleteValue(base::StringPiece value) { + static constexpr char16_t kRegex[] = u"address"; + return MatchesRegex<kRegex>(base::UTF8ToUTF16(value)); +} + +} // namespace + +HtmlFieldType FieldTypeFromAutocompleteAttributeValue( + std::string value, + const FormFieldData& field) { + if (value.empty()) + return HTML_TYPE_UNSPECIFIED; + + // We are lenient and accept '_' instead of '-' as a separator. E.g. + // "given_name" is treated like "given-name". + base::ReplaceChars(value, "_", "-", &value); + // We accept e.g. "phone-country" instead of "tel-country". + if (base::StartsWith(value, "phone")) + base::ReplaceFirstSubstringAfterOffset(&value, 0, "phone", "tel"); + + absl::optional<HtmlFieldType> type = + ParseStandardizedAutocompleteAttribute(value); + if (!type.has_value()) { + type = ParseProposedAutocompleteAttribute(value); + if (!type.has_value()) + type = ParseNonStandarizedAutocompleteAttribute(value); + } + + if (type.has_value()) + return RationalizeAutocompleteType(type.value(), field); + + // `value` cannot be mapped to any HtmlFieldType. By classifying the field + // as HTML_TYPE_UNRECOGNIZED Autofill is effectively disabled. Instead, check + // if we have reason to ignore the value and treat the field as + // HTML_TYPE_UNSPECIFIED. This makes us ignore the autocomplete value. + return ShouldIgnoreAutocompleteValue(value) && + base::FeatureList::IsEnabled( + features::kAutofillIgnoreUnmappableAutocompleteValues) + ? HTML_TYPE_UNSPECIFIED + : HTML_TYPE_UNRECOGNIZED; +} + +absl::optional<AutocompleteParsingResult> ParseAutocompleteAttribute( + const FormFieldData& field) { + std::vector<std::string> tokens = + LowercaseAndTokenizeAttributeString(field.autocomplete_attribute); + + // The autocomplete attribute is overloaded: it can specify either a field + // type hint or whether autocomplete should be enabled at all. Ignore the + // latter type of attribute value. + if (tokens.empty() || + (tokens.size() == 1 && ShouldIgnoreAutocompleteAttribute(tokens[0]))) { + return absl::nullopt; + } + + AutocompleteParsingResult result; + + // The "webauthn" token is unused by Autofill, but skipped to parse the type + // correctly. + if (tokens.back() == "webauthn") { + tokens.pop_back(); + if (tokens.empty()) + return absl::nullopt; + } + + // (1) The final token must be the field type. + std::string field_type_token = tokens.back(); + tokens.pop_back(); + result.field_type = + FieldTypeFromAutocompleteAttributeValue(field_type_token, field); + + // (2) The preceding token, if any, may be a type hint. + if (!tokens.empty() && IsContactTypeHint(tokens.back())) { + // If it is, it must match the field type; otherwise, abort. + // Note that an invalid token invalidates the entire attribute value, even + // if the other tokens are valid. + if (!ContactTypeHintMatchesFieldType(tokens.back(), result.field_type)) + return absl::nullopt; + // Chrome Autofill ignores these type hints. + tokens.pop_back(); + } + + // (3) The preceding token, if any, may be a fixed string that is either + // "shipping" or "billing". + result.mode = HTML_MODE_NONE; + if (!tokens.empty()) { + for (HtmlFieldMode mode : {HTML_MODE_BILLING, HTML_MODE_SHIPPING}) + if (tokens.back() == HtmlFieldModeToStringPiece(mode)) { + result.mode = mode; + tokens.pop_back(); + break; + } + } + + // (4) The preceding token, if any, may be a named section. + constexpr base::StringPiece kSectionPrefix = "section-"; + if (!tokens.empty() && base::StartsWith(tokens.back(), kSectionPrefix, + base::CompareCase::SENSITIVE)) { + // Prepend this section name to the suffix set in the preceding block. + result.section = tokens.back().substr(kSectionPrefix.size()); + tokens.pop_back(); + } + + // (5) No other tokens are allowed. If there are any remaining, abort. + if (!tokens.empty()) + return absl::nullopt; + + return result; +} + +bool ShouldIgnoreAutocompleteAttribute(base::StringPiece autocomplete) { + return autocomplete == "on" || autocomplete == "off" || + autocomplete == "false"; +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/common/autocomplete_parsing_util.h b/chromium/components/autofill/core/common/autocomplete_parsing_util.h new file mode 100644 index 00000000000..7fd6da8ee9e --- /dev/null +++ b/chromium/components/autofill/core/common/autocomplete_parsing_util.h @@ -0,0 +1,55 @@ +// Copyright 2022 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_AUTOCOMPLETE_PARSING_UTIL_H_ +#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOCOMPLETE_PARSING_UTIL_H_ + +#include <string> + +#include "base/strings/string_piece.h" +#include "components/autofill/core/common/form_field_data.h" +#include "components/autofill/core/common/html_field_types.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace autofill { + +// The autocomplete attribute consists of several components, as described at +// http://is.gd/whatwg_autocomplete. Autofill supports part of the specification +// and parses the following tokens: +// [section-*] [shipping|billing] [type_hint] field_type [webauthn] +// The parsing extracts these components from `field.autocomplete_attribute` or +// returns absl::nullopt, if the parsing fails. The latter happens if: +// - The autocomplete value is empty or contains more than 5 tokens. +// - The type_hint doesn't match the field_type. +// - If ShouldIgnoreAutocompleteAttribute(autocomplete) is true. +// An unrecognizable field_type doesn't stop parsing and yields +// HTML_TYPE_UNRECOGNIZED instead. +struct AutocompleteParsingResult { + // `section` corresponds to the string after "section-". + std::string section; + HtmlFieldMode mode; + // Type hints are parsed and validated, but otherwise unused. + HtmlFieldType field_type; + // webauthn is parsed, but otherwise unused. +}; +absl::optional<AutocompleteParsingResult> ParseAutocompleteAttribute( + const FormFieldData& field); + +// Checks if `autocomplete` is one of "on", "off" or "false". These values are +// currently ignored by Autofill. +bool ShouldIgnoreAutocompleteAttribute(base::StringPiece autocomplete); + +// Parses `value` as an HTML field type and converts it to the corresponding +// HtmlFieldType, if it is supposed by Autofill. Rationalization based on the +// `field` is done. +// HTML_TYPE_UNSPECIFIED is returned if `value` is empty, or if `value` is +// supposed to be ignored by `kAutofillIgnoreUnmappableAutocompleteValues`. +// Otherwise HTML_TYPE_UNRECOGNIZED is returned. +HtmlFieldType FieldTypeFromAutocompleteAttributeValue( + std::string value, + const FormFieldData& field); + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOCOMPLETE_PARSING_UTIL_H_ diff --git a/chromium/components/autofill/core/common/autocomplete_parsing_util_unittest.cc b/chromium/components/autofill/core/common/autocomplete_parsing_util_unittest.cc new file mode 100644 index 00000000000..41f167054cd --- /dev/null +++ b/chromium/components/autofill/core/common/autocomplete_parsing_util_unittest.cc @@ -0,0 +1,103 @@ +// Copyright 2022 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/autocomplete_parsing_util.h" + +#include <string> + +#include "base/strings/string_piece.h" +#include "components/autofill/core/common/form_field_data.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace autofill { + +// Tests that parsing a field with autocomplete=`autocomplete` and +// maxlength=`max_length` results in `expected_result`. +struct AutocompleteAttributeTestcase { + base::StringPiece autocomplete; + absl::optional<AutocompleteParsingResult> expected_result; + int max_length = 0; +}; + +class AutocompleteAttributeProcessingUtilTest + : public testing::TestWithParam<AutocompleteAttributeTestcase> {}; + +// In general, `ParseAutocompleteAttribute()` returns absl::nullopt if one of +// the tokens cannot be parsed. The exception is the field type, which defaults +// to HTML_TYPE_UNRECOGNIZED. +const AutocompleteAttributeTestcase kAutocompleteTestcases[]{ + // Only the field type: + {"name", {{"", HTML_MODE_NONE, HTML_TYPE_NAME}}}, + {"autofill", {{"", HTML_MODE_NONE, HTML_TYPE_UNRECOGNIZED}}}, + // autocomplete=off is ignored completely. + {"off", absl::nullopt}, + + // Rationalization based on the field's max_length is done. + {"cc-exp-year", {{"", HTML_MODE_NONE, HTML_TYPE_CREDIT_CARD_EXP_YEAR}}}, + {"cc-exp-year", + {{"", HTML_MODE_NONE, HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR}}, + /*max_length=*/2}, + + // Type hints: + // They are parsed and validated, but otherwise unused. Type hints are only + // valid before tel* and email. + {"home email", {{"", HTML_MODE_NONE, HTML_TYPE_EMAIL}}}, + {"work email", {{"", HTML_MODE_NONE, HTML_TYPE_EMAIL}}}, + {"work cc-number", absl::nullopt}, + {"unrecognized_type_hint email", absl::nullopt}, + + // Billing and shipping modes: + {"billing country", {{"", HTML_MODE_BILLING, HTML_TYPE_COUNTRY_CODE}}}, + {"shipping country", {{"", HTML_MODE_SHIPPING, HTML_TYPE_COUNTRY_CODE}}}, + {"billing unrecognized", {{"", HTML_MODE_BILLING, HTML_TYPE_UNRECOGNIZED}}}, + {"shipping work tel-local", + {{"", HTML_MODE_SHIPPING, HTML_TYPE_TEL_LOCAL}}}, + {"unrecognized_mode country", absl::nullopt}, + {"unrecognized_mode unrecognized", absl::nullopt}, + + // Sections: + {"section-one tel", {{"one", HTML_MODE_NONE, HTML_TYPE_TEL}}}, + {"section-one shipping tel", {{"one", HTML_MODE_SHIPPING, HTML_TYPE_TEL}}}, + {"section-one shipping home tel", + {{"one", HTML_MODE_SHIPPING, HTML_TYPE_TEL}}}, + {"section- tel", {{"", HTML_MODE_NONE, HTML_TYPE_TEL}}}, + {"section tel", absl::nullopt}, + {"no_section tel", absl::nullopt}, + {"no_section work tel", absl::nullopt}, + {"section-random", {{"", HTML_MODE_NONE, HTML_TYPE_UNRECOGNIZED}}}, + + // "webauthn" shouldn't prevent parsing, but is otherwise ignored. + {"name webauthn", {{"", HTML_MODE_NONE, HTML_TYPE_NAME}}}, + {"section-one shipping home tel webauthn", + {{"one", HTML_MODE_SHIPPING, HTML_TYPE_TEL}}}, + {"webauthn", absl::nullopt}, + + // Too many tokens. + {"hello section-one shipping home tel webauthn", absl::nullopt}}; + +INSTANTIATE_TEST_SUITE_P(, + AutocompleteAttributeProcessingUtilTest, + testing::ValuesIn(kAutocompleteTestcases)); + +TEST_P(AutocompleteAttributeProcessingUtilTest, ParseAutocompleteAttribute) { + auto test = GetParam(); + SCOPED_TRACE(testing::Message() + << "autocomplete=\"" << test.autocomplete << "\""); + + FormFieldData field; + field.autocomplete_attribute = std::string(test.autocomplete); + if (test.max_length) + field.max_length = test.max_length; + + auto result = ParseAutocompleteAttribute(field); + ASSERT_EQ(result.has_value(), test.expected_result.has_value()); + if (result.has_value()) { + EXPECT_EQ(result->section, test.expected_result->section); + EXPECT_EQ(result->mode, test.expected_result->mode); + EXPECT_EQ(result->field_type, test.expected_result->field_type); + } +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_features.cc b/chromium/components/autofill/core/common/autofill_features.cc index f7fb7e74750..9fd9d35b68c 100644 --- a/chromium/components/autofill/core/common/autofill_features.cc +++ b/chromium/components/autofill/core/common/autofill_features.cc @@ -95,7 +95,14 @@ const base::Feature kAutofillInferCountryCallingCode{ // determine the address requirements. // TODO(crbug.com/1297032): Cleanup when launched. const base::Feature kAutofillComplementCountryCodeOnImport{ - "AutofillComplementCountryCodeOnImport", base::FEATURE_DISABLED_BY_DEFAULT}; + "AutofillComplementCountryCodeOnImport", base::FEATURE_ENABLED_BY_DEFAULT}; + +// If enabled, label inference considers strings entirely made up of '(', ')' +// and '-' as valid labels. +// TODO(crbug.com/1311937): Cleanup when launched. +const base::Feature kAutofillConsiderPhoneNumberSeparatorsValidLabels{ + "AutofillConsiderPhoneNumberSeparatorsValidLabels", + base::FEATURE_DISABLED_BY_DEFAULT}; // If enabled, local heuristics fall back to the fields placeholder attribute. const base::Feature kAutofillConsiderPlaceholderForParsing{ @@ -128,12 +135,6 @@ const base::Feature kAutofillFillCreditCardAsPerFormatString{ "AutofillFillCreditCardAsPerFormatString", base::FEATURE_DISABLED_BY_DEFAULT}; -// If enabled, AutofillPopupControllerImpl is destructed not immediately in its -// HideViewAndDie() function, but as a delayed task. -// TODO(crbug.com/1277218): Cleanup when launched. -const base::Feature kAutofillDelayPopupControllerDeletion{ - "AutofillDelayPopupControllerDeletion", base::FEATURE_DISABLED_BY_DEFAULT}; - // Kill switch for Autofill filling. const base::Feature kAutofillDisableFilling{"AutofillDisableFilling", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -195,14 +196,11 @@ const base::Feature kAutofillEnableAugmentedPhoneCountryCode{ "AutofillEnableAugmentedPhoneCountryCode", base::FEATURE_DISABLED_BY_DEFAULT}; -// This feature guards the logic for Autofills future compatibility launch of -// birthdates. Currently filling is not supported and this effectively -// disables the birthdate merging logic, reads/writes to the AutofillTable and -// reading/writing from the sync proto. -// TODO(crbug.com/1305940): Remove once launched. -const base::Feature kAutofillEnableCompatibilitySupportForBirthdates{ - "AutofillEnableCompatibilitySupportForBirthdates", - base::FEATURE_ENABLED_BY_DEFAULT}; +// Enables parsing for birthdate fields. Filling is not supported and parsing +// is meant to prevent false positive credit card expiration dates. +// TODO(crbug.com/1306654): Remove once launched. +const base::Feature kAutofillEnableBirthdateParsing{ + "AutofillEnableBirthdateParsing", base::FEATURE_DISABLED_BY_DEFAULT}; // Controls if Autofill parses ADDRESS_HOME_DEPENDENT_LOCALITY. // TODO(crbug.com/1157405): Remove once launched. @@ -218,7 +216,7 @@ const base::Feature kAutofillEnableExtendedAddressFormats{ // Controls whether to save the first number in a form with multiple phone // numbers instead of aborting the import. -// TODO(crbug.com/1167484) Remove once launched +// TODO(crbug.com/1167484) Remove once launched. const base::Feature kAutofillEnableImportWhenMultiplePhoneNumbers{ "AutofillEnableImportWhenMultiplePhoneNumbers", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -267,13 +265,13 @@ const base::Feature kAutofillEnableProfileDeduplication{ // TODO(crbug.com/1098943): Remove once launched. const base::Feature kAutofillEnableSupportForMoreStructureInNames{ "AutofillEnableSupportForMoreStructureInNames", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; // Controls if Autofill supports new structure in addresses. // TODO(crbug.com/1098943): Remove once launched. const base::Feature kAutofillEnableSupportForMoreStructureInAddresses{ "AutofillEnableSupportForMoreStructureInAddresses", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; // Controls if Autofill supports merging subset names. // TODO(crbug.com/1098943): Remove once launched. @@ -323,25 +321,6 @@ const base::Feature kAutofillExtractAllDatalists{ const base::Feature kAutofillTypeSpecificPopupWidth{ "AutofillTypeSpecificPopupWidth", base::FEATURE_DISABLED_BY_DEFAULT}; -// Autofill uses the local heuristic such that address forms are only filled if -// at least 3 fields are fillable according to local heuristics. Unfortunately, -// the criterion for fillability is only that the field type is unknown. So many -// field types that we don't fill (search term, price, ...) count towards that -// counter, effectively reducing the threshold for some forms. -const base::Feature kAutofillFixFillableFieldTypes{ - "AutofillFixFillableFieldTypes", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Lookups for field classifications are gated on either Autofill for addresses -// or payments being enabled. As a consequence, if both are disabled, the -// password manager does not get server-side field classifications anymore -// and its performance is reduced. When this feature is enabled, Autofill parse -// forms and perform server lookups even if only the password manager is -// enabled. -// TODO(crbug.com/1293341): Remove once launched. -const base::Feature kAutofillFixServerQueriesIfPasswordManagerIsEnabled{ - "AutofillFixServerQueriesIfPasswordManagerIsEnabled", - base::FEATURE_DISABLED_BY_DEFAULT}; - // When enabled, the Autofill popup ignores second clicks for a certain period // (kAutofillIgnoreEarlyClicksOnPopupDuration) after the Autofill popup was // shown. This is to prevent double clicks accidentally accepting suggestions. @@ -356,7 +335,20 @@ const base::Feature kAutofillIgnoreEarlyClicksOnPopup{ const base::FeatureParam<base::TimeDelta> kAutofillIgnoreEarlyClicksOnPopupDuration{ &kAutofillIgnoreEarlyClicksOnPopup, "duration", - base::Milliseconds(250)}; + base::Milliseconds(500)}; + +// When enabled, HTML autocomplete values that do not map to any known type, but +// look reasonable (e.g. contain "address") are simply ignored. Without the +// feature, Autofill is disabled on such fields. +const base::Feature kAutofillIgnoreUnmappableAutocompleteValues{ + "AutofillIgnoreUnmappableAutocompleteValues", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// When enabled, <label for=..> inference relies on control.labels() instead of +// iterating through all <label> tags manually. +// TODO(crbug.com/1339277) Remove once launched. +const base::Feature kAutofillImprovedLabelForInference{ + "AutofillImprovedLabelForInference", base::FEATURE_DISABLED_BY_DEFAULT}; // When enabled, only changed values are highlighted in preview mode. // TODO(crbug/1248585): Remove when launched. @@ -434,6 +426,19 @@ const base::FeatureParam<std::string> kAutofillParsingPatternActiveSource{ const base::Feature kAutofillPageLanguageDetection{ "AutofillPageLanguageDetection", base::FEATURE_DISABLED_BY_DEFAULT}; +// If enabled, AutofillManager::ParseForm() isn't called synchronously. +// Instead, all incoming events parse the form asynchronously and proceed +// afterwards. +// TODO(crbug.com/1309848) Remove once launched. +const base::Feature kAutofillParseAsync{"AutofillParseAsync", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// If enabled, local heuristics fall back to interpreting the fields' name as an +// autocomplete type. +// TODO(crbug.com/TODO) Remove once launched. +const base::Feature kAutofillParseNameAsAutocompleteType{ + "AutofillParseNameAsAutocompleteType", base::FEATURE_DISABLED_BY_DEFAULT}; + // If the feature is enabled, FormTracker's probable-form-submission detection // is disabled and replaced with browser-side detection. // TODO(crbug/1117451): Remove once it works. @@ -536,6 +541,7 @@ const base::Feature kAutofillSilentProfileUpdateForInsufficientImport{ // Controls whether inferred label is considered for comparing in // FormFieldData.SimilarFieldAs. +// TODO(crbug.com/1211834): The experiment seems dead; remove? const base::Feature kAutofillSkipComparingInferredLabels{ "AutofillSkipComparingInferredLabels", base::FEATURE_DISABLED_BY_DEFAULT}; diff --git a/chromium/components/autofill/core/common/autofill_features.h b/chromium/components/autofill/core/common/autofill_features.h index d8b3036b433..56cff1ad9b2 100644 --- a/chromium/components/autofill/core/common/autofill_features.h +++ b/chromium/components/autofill/core/common/autofill_features.h @@ -49,6 +49,8 @@ extern const base::Feature kAutofillInferCountryCallingCode; COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillComplementCountryCodeOnImport; COMPONENT_EXPORT(AUTOFILL) +extern const base::Feature kAutofillConsiderPhoneNumberSeparatorsValidLabels; +COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillConsiderPlaceholderForParsing; COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillConsiderVariationCountryCodeForPhoneNumbers; @@ -60,8 +62,6 @@ COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillFillAndImportFromMoreFields; COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillFillCreditCardAsPerFormatString; -COMPONENT_EXPORT(AUTOFILL) -extern const base::Feature kAutofillDelayPopupControllerDeletion; COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillDisableFilling; COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillDisableAddressImport; @@ -72,7 +72,7 @@ extern const base::Feature kAutofillEnableAccountWalletStorage; COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillEnableAugmentedPhoneCountryCode; COMPONENT_EXPORT(AUTOFILL) -extern const base::Feature kAutofillEnableCompatibilitySupportForBirthdates; +extern const base::Feature kAutofillEnableBirthdateParsing; COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillEnableDependentLocalityParsing; COMPONENT_EXPORT(AUTOFILL) @@ -124,15 +124,15 @@ extern const base::Feature kAutofillRefillModifiedCreditCardExpirationDates; COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillTypeSpecificPopupWidth; COMPONENT_EXPORT(AUTOFILL) -extern const base::Feature kAutofillFixFillableFieldTypes; -COMPONENT_EXPORT(AUTOFILL) -extern const base::Feature kAutofillFixServerQueriesIfPasswordManagerIsEnabled; -COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillIgnoreEarlyClicksOnPopup; COMPONENT_EXPORT(AUTOFILL) extern const base::FeatureParam<base::TimeDelta> kAutofillIgnoreEarlyClicksOnPopupDuration; COMPONENT_EXPORT(AUTOFILL) +extern const base::Feature kAutofillIgnoreUnmappableAutocompleteValues; +COMPONENT_EXPORT(AUTOFILL) +extern const base::Feature kAutofillImprovedLabelForInference; +COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillHighlightOnlyChangedValuesInPreviewMode; COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillServerTypeTakesPrecedence; @@ -151,6 +151,10 @@ extern const base::FeatureParam<int> COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillPageLanguageDetection; COMPONENT_EXPORT(AUTOFILL) +extern const base::Feature kAutofillParseAsync; +COMPONENT_EXPORT(AUTOFILL) +extern const base::Feature kAutofillParseNameAsAutocompleteType; +COMPONENT_EXPORT(AUTOFILL) extern const base::Feature kAutofillParsingPatternProvider; COMPONENT_EXPORT(AUTOFILL) extern const base::FeatureParam<std::string> diff --git a/chromium/components/autofill/core/common/autofill_internals/log_message_unittest.cc b/chromium/components/autofill/core/common/autofill_internals/log_message_unittest.cc index 2d4f225cb99..f6fbb63e67b 100644 --- a/chromium/components/autofill/core/common/autofill_internals/log_message_unittest.cc +++ b/chromium/components/autofill/core/common/autofill_internals/log_message_unittest.cc @@ -14,7 +14,7 @@ TEST(LogMessage, Serialization) { LogBuffer buffer; buffer << LogMessage::kParsedForms; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"attributes":{"class":"log-message","message":"ParsedForms"},)" R"("children":[{"type":"text","value":"Parsed forms:"}],)" R"("type":"element","value":"div"})", diff --git a/chromium/components/autofill/core/common/autofill_internals/logging_scope_unittest.cc b/chromium/components/autofill/core/common/autofill_internals/logging_scope_unittest.cc index 16ffe96f22c..25c6dfdc3a3 100644 --- a/chromium/components/autofill/core/common/autofill_internals/logging_scope_unittest.cc +++ b/chromium/components/autofill/core/common/autofill_internals/logging_scope_unittest.cc @@ -14,7 +14,7 @@ TEST(LoggingScope, Serialization) { LogBuffer buffer; buffer << LoggingScope::kContext; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"attributes":{"class":"log-entry","scope":"Context"},)" R"("type":"element","value":"div"})", json); diff --git a/chromium/components/autofill/core/common/autofill_l10n_util.cc b/chromium/components/autofill/core/common/autofill_l10n_util.cc index 07a94b1d1ea..207c09762ac 100644 --- a/chromium/components/autofill/core/common/autofill_l10n_util.cc +++ b/chromium/components/autofill/core/common/autofill_l10n_util.cc @@ -10,7 +10,6 @@ #include "base/i18n/string_compare.h" #include "base/logging.h" #include "base/memory/ptr_util.h" -#include "base/metrics/histogram_macros.h" namespace autofill { namespace l10n { @@ -38,9 +37,6 @@ std::unique_ptr<icu::Collator> GetCollatorForLocale(const icu::Locale& locale) { << "locale."; } } - - UMA_HISTOGRAM_BOOLEAN("Autofill.IcuCollatorCreationSuccess", - (!!collator && U_SUCCESS(error_code))); return collator; } diff --git a/chromium/components/autofill/core/common/autofill_l10n_util_unittest.cc b/chromium/components/autofill/core/common/autofill_l10n_util_unittest.cc deleted file mode 100644 index 6692ebee5ba..00000000000 --- a/chromium/components/autofill/core/common/autofill_l10n_util_unittest.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015 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_l10n_util.h" - -#include "base/test/metrics/histogram_tester.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/icu/source/common/unicode/locid.h" - -namespace autofill { -namespace l10n { - -// Test the success in the creation of the ICU Collator. -TEST(CaseInsensitiveCompareTest, IcuCollatorCreation_Success) { - base::HistogramTester histogram_tester; - CaseInsensitiveCompare compare; - histogram_tester.ExpectUniqueSample("Autofill.IcuCollatorCreationSuccess", - true, 1); -} - -// Test the failure in creating the ICU Collator. -TEST(CaseInsensitiveCompareTest, IcuCollatorCreation_FailureBadLocale) { - // Setting the locale to a bogus value. - icu::Locale bogusLocale = icu::Locale::createFromName("bogus"); - bogusLocale.setToBogus(); - - base::HistogramTester histogram_tester; - CaseInsensitiveCompare compare(bogusLocale); - histogram_tester.ExpectUniqueSample("Autofill.IcuCollatorCreationSuccess", - false, 1); -} - -} // namespace l10n -} // 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 4ebd3a423e5..57064945746 100644 --- a/chromium/components/autofill/core/common/autofill_payments_features.cc +++ b/chromium/components/autofill/core/common/autofill_payments_features.cc @@ -47,12 +47,6 @@ const base::Feature kAutofillCreditCardAuthentication{ #endif }; -// When enabled, if credit card upload succeeded, the avatar icon will show a -// highlight otherwise, the credit card icon image will be updated and if user -// clicks on the icon, a save card failure bubble will pop up. -const base::Feature kAutofillCreditCardUploadFeedback{ - "AutofillCreditCardUploadFeedback", base::FEATURE_DISABLED_BY_DEFAULT}; - // When enabled, the GetDetailsForEnrollResponseDetails in the // UploadCardResponseDetails will be parsed, which will allow the Virtual Card // Enrollment flow to skip making a new GetDetailsForEnroll request. This is an @@ -62,11 +56,22 @@ const base::Feature "AutofillEnableGetDetailsForEnrollParsingInUploadCardResponse", base::FEATURE_ENABLED_BY_DEFAULT}; +// When enabled, a progress dialog will display while authenticating with FIDO. +// TODO(crbug.com/1337380): Clean up kAutofillEnableFIDOProgressDialog when it's +// fully rolled out. +const base::Feature kAutofillEnableFIDOProgressDialog{ + "AutofillEnableFIDOProgressDialog", base::FEATURE_DISABLED_BY_DEFAULT}; + // When enabled, enable manual falling component for virtual cards on Android. const base::Feature kAutofillEnableManualFallbackForVirtualCards{ "AutofillEnableManualFallbackForVirtualCards", base::FEATURE_ENABLED_BY_DEFAULT}; +// When enabled, card product name (instead of issuer network) will be shown in +// Payments UI. +const base::Feature kAutofillEnableCardProductName{ + "AutofillEnableCardProductName", base::FEATURE_DISABLED_BY_DEFAULT}; + // When enabled, a notification will be displayed on page navigation if the // domain has an eligible merchant promo code offer or reward. const base::Feature kAutofillEnableOfferNotificationForPromoCodes{ @@ -79,11 +84,15 @@ const base::Feature kAutofillEnableOffersInClankKeyboardAccessory{ "AutofillEnableOffersInClankKeyboardAccessory", base::FEATURE_DISABLED_BY_DEFAULT}; +// When enabled, some extra metrics logging for Autofill Downstream will start. +const base::Feature kAutofillEnableRemadeDownstreamMetrics{ + "AutofillEnableRemadeDownstreamMetrics", base::FEATURE_ENABLED_BY_DEFAULT}; + // Controls whether we send billing customer number in GetUploadDetails // preflight call. const base::Feature kAutofillEnableSendingBcnInGetUploadDetails{ "AutofillEnableSendingBcnInGetUploadDetails", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; // When enabled, if the user interacts with the manual fallback bottom sheet // on Android, it'll remain sticky until the user dismisses it. @@ -91,11 +100,6 @@ const base::Feature kAutofillEnableStickyManualFallbackForCards{ "AutofillEnableStickyManualFallbackForCards", base::FEATURE_DISABLED_BY_DEFAULT}; -// When enabled, Autofill data related icons will be shown in the status -// chip in toolbar along with the avatar toolbar button. -const base::Feature kAutofillEnableToolbarStatusChip{ - "AutofillEnableToolbarStatusChip", base::FEATURE_DISABLED_BY_DEFAULT}; - // When enabled, UnmaskCardRequest will set instrument id, which is Chrome-side // field for non-legacy ID. const base::Feature kAutofillEnableUnmaskCardRequestSetInstrumentId{ @@ -145,16 +149,32 @@ const base::Feature kAutofillEnableVirtualCardMetadata{ const base::Feature kAutofillEnforceDelaysInStrikeDatabase{ "AutofillEnforceDelaysInStrikeDatabase", base::FEATURE_DISABLED_BY_DEFAULT}; +// When enabled, Autofill will attempt to fill IBAN (International Bank Account +// Number) fields when data is available. +const base::Feature kAutofillFillIbanFields{"AutofillFillIbanFields", + base::FEATURE_DISABLED_BY_DEFAULT}; + // When enabled, Autofill will attempt to fill merchant promo/coupon/gift code // fields when data is available. const base::Feature kAutofillFillMerchantPromoCodeFields{ "AutofillFillMerchantPromoCodeFields", base::FEATURE_DISABLED_BY_DEFAULT}; +// When enabled, Autofill will attempt to find International Bank Account Number +// (IBAN) fields when parsing forms. +const base::Feature kAutofillParseIBANFields{"AutofillParseIBANFields", + base::FEATURE_DISABLED_BY_DEFAULT}; + // When enabled, Autofill will attempt to find merchant promo/coupon/gift code // fields when parsing forms. const base::Feature kAutofillParseMerchantPromoCodeFields{ "AutofillParseMerchantPromoCodeFields", base::FEATURE_DISABLED_BY_DEFAULT}; +// When enabled, Autofill will attempt to find standalone CVC fields for VCN +// card on file when parsing forms. +const base::Feature kAutofillParseVcnCardOnFileStandaloneCvcFields{ + "AutofillParseVcnCardOnFileStandaloneCvcFields", + base::FEATURE_DISABLED_BY_DEFAULT}; + // When enabled, the Save Card infobar will be dismissed by a user initiated // navigation other than one caused by submitted form. const base::Feature kAutofillSaveCardDismissOnNavigation{ @@ -187,12 +207,6 @@ const base::Feature kAutofillShowUnmaskedCachedCardInManualFillingView{ "AutofillShowUnmaskedCachedCardInManualFillingView", base::FEATURE_DISABLED_BY_DEFAULT}; -// When enabled, merchant bound virtual cards will be suggested even if we don't -// detect all of the card number, exp date and CVC fields in the payment form. -const base::Feature kAutofillSuggestVirtualCardsOnIncompleteForm{ - "AutofillSuggestVirtualCardsOnIncompleteForm", - base::FEATURE_DISABLED_BY_DEFAULT}; - // Controls offering credit card upload to Google Payments. Cannot ever be // ENABLED_BY_DEFAULT because the feature state depends on the user's country. // The set of launched countries is listed in autofill_experiments.cc, and this diff --git a/chromium/components/autofill/core/common/autofill_payments_features.h b/chromium/components/autofill/core/common/autofill_payments_features.h index 96a96778243..1d0b5f95d66 100644 --- a/chromium/components/autofill/core/common/autofill_payments_features.h +++ b/chromium/components/autofill/core/common/autofill_payments_features.h @@ -9,10 +9,6 @@ #include "base/metrics/field_trial_params.h" #include "build/build_config.h" -namespace base { -struct Feature; -} - namespace autofill { namespace features { @@ -20,15 +16,16 @@ namespace features { extern const base::Feature kAutofillAlwaysReturnCloudTokenizedCard; extern const base::Feature kAutofillAutoTriggerManualFallbackForCards; extern const base::Feature kAutofillCreditCardAuthentication; -extern const base::Feature kAutofillCreditCardUploadFeedback; extern const base::Feature kAutofillEnableGetDetailsForEnrollParsingInUploadCardResponse; +extern const base::Feature kAutofillEnableFIDOProgressDialog; extern const base::Feature kAutofillEnableManualFallbackForVirtualCards; +extern const base::Feature kAutofillEnableCardProductName; extern const base::Feature kAutofillEnableOfferNotificationForPromoCodes; extern const base::Feature kAutofillEnableOffersInClankKeyboardAccessory; +extern const base::Feature kAutofillEnableRemadeDownstreamMetrics; extern const base::Feature kAutofillEnableSendingBcnInGetUploadDetails; extern const base::Feature kAutofillEnableStickyManualFallbackForCards; -extern const base::Feature kAutofillEnableToolbarStatusChip; extern const base::Feature kAutofillEnableUnmaskCardRequestSetInstrumentId; extern const base::Feature kAutofillEnableUpdateVirtualCardEnrollment; extern const base::Feature kAutofillEnableVirtualCard; @@ -37,8 +34,11 @@ extern const base::Feature kAutofillEnableVirtualCardManagementInDesktopSettingsPage; extern const base::Feature kAutofillEnableVirtualCardMetadata; extern const base::Feature kAutofillEnforceDelaysInStrikeDatabase; +extern const base::Feature kAutofillFillIbanFields; extern const base::Feature kAutofillFillMerchantPromoCodeFields; +extern const base::Feature kAutofillParseIBANFields; extern const base::Feature kAutofillParseMerchantPromoCodeFields; +extern const base::Feature kAutofillParseVcnCardOnFileStandaloneCvcFields; extern const base::Feature kAutofillRemoveCardExpiryFromDownstreamSuggestion; extern const base::Feature kAutofillSaveCardDismissOnNavigation; extern const base::Feature kAutofillSaveCardInfobarEditSupport; @@ -46,7 +46,6 @@ extern const base::Feature kAutofillSaveCardUiExperiment; extern const base::FeatureParam<int> kAutofillSaveCardUiExperimentSelectorInNumber; extern const base::Feature kAutofillShowUnmaskedCachedCardInManualFillingView; -extern const base::Feature kAutofillSuggestVirtualCardsOnIncompleteForm; extern const base::Feature kAutofillUpstream; extern const base::Feature kAutofillUpstreamAllowAdditionalEmailDomains; extern const base::Feature kAutofillUpstreamAllowAllEmailDomains; diff --git a/chromium/components/autofill/core/common/autofill_prefs.cc b/chromium/components/autofill/core/common/autofill_prefs.cc index d2e741e5593..07d39cd9ea6 100644 --- a/chromium/components/autofill/core/common/autofill_prefs.cc +++ b/chromium/components/autofill/core/common/autofill_prefs.cc @@ -19,19 +19,13 @@ namespace { // was found. int GetSyncTransportOptInBitFieldForAccount(const PrefService* prefs, const std::string& account_hash) { - auto* dictionary = prefs->GetDictionary(prefs::kAutofillSyncTransportOptIn); - - // If there is no dictionary it means the account didn't opt-in. Use 0 because - // it's the same as not having opted-in to anything. - if (!dictionary) { - return 0; - } + const auto& dictionary = + prefs->GetValueDict(prefs::kAutofillSyncTransportOptIn); // If there is no entry in the dictionary, it means the account didn't opt-in. // Use 0 because it's the same as not having opted-in to anything. - auto* found = - dictionary->FindKeyOfType(account_hash, base::Value::Type::INTEGER); - return found ? found->GetInt() : 0; + const auto found = dictionary.FindInt(account_hash); + return found.value_or(0); } } // namespace @@ -61,6 +55,9 @@ const char kAutofillEnabledDeprecated[] = "autofill.enabled"; const char kAutofillJapanCityFieldMigratedDeprecated[] = "autofill.japan_city_field_migrated_to_street_address"; +// Boolean that is true if Autofill is enabled and allowed to save IBAN data. +extern const char kAutofillIBANEnabled[] = "autofill.iban_enabled"; + // Integer that is set to the last version where the profile deduping routine // was run. This routine will be run once per version. const char kAutofillLastVersionDeduped[] = "autofill.last_version_deduped"; @@ -135,6 +132,9 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref( prefs::kAutofillCreditCardEnabled, true, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); + registry->RegisterBooleanPref( + prefs::kAutofillIBANEnabled, true, + user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); // Non-synced prefs. Used for per-device choices, e.g., signin promo. registry->RegisterBooleanPref(prefs::kAutofillCreditCardFidoAuthEnabled, @@ -217,6 +217,14 @@ void SetAutofillCreditCardEnabled(PrefService* prefs, bool enabled) { prefs->SetBoolean(kAutofillCreditCardEnabled, enabled); } +bool IsAutofillIBANEnabled(const PrefService* prefs) { + return prefs->GetBoolean(kAutofillIBANEnabled); +} + +void SetAutofillIBANEnabled(PrefService* prefs, bool enabled) { + prefs->SetBoolean(kAutofillIBANEnabled, enabled); +} + bool IsAutofillManaged(const PrefService* prefs) { return prefs->IsManagedPreference(kAutofillEnabledDeprecated); } diff --git a/chromium/components/autofill/core/common/autofill_prefs.h b/chromium/components/autofill/core/common/autofill_prefs.h index f086613bc5b..12d118243ce 100644 --- a/chromium/components/autofill/core/common/autofill_prefs.h +++ b/chromium/components/autofill/core/common/autofill_prefs.h @@ -28,8 +28,10 @@ extern const char kAutofillCreditCardFidoAuthEnabled[]; extern const char kAutofillCreditCardFidoAuthOfferCheckboxState[]; #endif extern const char kAutofillCreditCardSigninPromoImpressionCount[]; -// Please use kAutofillCreditCardEnabled and kAutofillProfileEnabled instead. +// Please use kAutofillCreditCardEnabled, kAutofillIBANEnabled and +// kAutofillProfileEnabled instead. extern const char kAutofillEnabledDeprecated[]; +extern const char kAutofillIBANEnabled[]; extern const char kAutofillJapanCityFieldMigratedDeprecated[]; extern const char kAutofillLastVersionDeduped[]; extern const char kAutofillLastVersionDisusedAddressesDeleted[]; @@ -68,6 +70,10 @@ bool IsAutofillCreditCardEnabled(const PrefService* prefs); void SetAutofillCreditCardEnabled(PrefService* prefs, bool enabled); +bool IsAutofillIBANEnabled(const PrefService* prefs); + +void SetAutofillIBANEnabled(PrefService* prefs, bool enabled); + bool IsAutofillManaged(const PrefService* prefs); bool IsAutofillProfileManaged(const PrefService* prefs); diff --git a/chromium/components/autofill/core/common/autofill_prefs_unittest.cc b/chromium/components/autofill/core/common/autofill_prefs_unittest.cc index 7576fceced2..a294b6617e6 100644 --- a/chromium/components/autofill/core/common/autofill_prefs_unittest.cc +++ b/chromium/components/autofill/core/common/autofill_prefs_unittest.cc @@ -89,45 +89,43 @@ TEST_F(AutofillPrefsTest, WalletSyncTransportPref_GetAndSet) { ASSERT_FALSE(IsUserOptedInWalletSyncTransport(pref_service(), account1)); ASSERT_FALSE(IsUserOptedInWalletSyncTransport(pref_service(), account2)); // There should be no entry for the accounts in the dictionary. - EXPECT_TRUE(pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictEmpty()); + EXPECT_TRUE( + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); // Set the opt-in for the first account. SetUserOptedInWalletSyncTransport(pref_service(), account1, true); EXPECT_TRUE(IsUserOptedInWalletSyncTransport(pref_service(), account1)); EXPECT_FALSE(IsUserOptedInWalletSyncTransport(pref_service(), account2)); // There should only be one entry in the dictionary. - EXPECT_EQ(1U, pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictSize()); + EXPECT_EQ( + 1U, + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).size()); // Unset the opt-in for the first account. SetUserOptedInWalletSyncTransport(pref_service(), account1, false); EXPECT_FALSE(IsUserOptedInWalletSyncTransport(pref_service(), account1)); EXPECT_FALSE(IsUserOptedInWalletSyncTransport(pref_service(), account2)); // There should be no entry for the accounts in the dictionary. - EXPECT_TRUE(pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictEmpty()); + EXPECT_TRUE( + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); // Set the opt-in for the second account. SetUserOptedInWalletSyncTransport(pref_service(), account2, true); EXPECT_FALSE(IsUserOptedInWalletSyncTransport(pref_service(), account1)); EXPECT_TRUE(IsUserOptedInWalletSyncTransport(pref_service(), account2)); // There should only be one entry in the dictionary. - EXPECT_EQ(1U, pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictSize()); + EXPECT_EQ( + 1U, + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).size()); // Set the opt-in for the first account too. SetUserOptedInWalletSyncTransport(pref_service(), account1, true); EXPECT_TRUE(IsUserOptedInWalletSyncTransport(pref_service(), account1)); EXPECT_TRUE(IsUserOptedInWalletSyncTransport(pref_service(), account1)); // There should be tow entries in the dictionary. - EXPECT_EQ(2U, pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictSize()); + EXPECT_EQ( + 2U, + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).size()); } #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) @@ -137,21 +135,18 @@ TEST_F(AutofillPrefsTest, WalletSyncTransportPref_UsesHashAccountId) { const CoreAccountId account1("account1"); // There should be no opt-in recorded at first. - EXPECT_TRUE(pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictEmpty()); + EXPECT_TRUE( + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); // Set the opt-in for the first account. SetUserOptedInWalletSyncTransport(pref_service(), account1, true); - EXPECT_FALSE(pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictEmpty()); + EXPECT_FALSE( + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); // Make sure that the dictionary keys don't contain the account id. - auto* dictionary = - pref_service()->GetDictionary(prefs::kAutofillSyncTransportOptIn); - EXPECT_EQ(nullptr, dictionary->FindKeyOfType(account1.ToString(), - base::Value::Type::INTEGER)); + const auto& dictionary = + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn); + EXPECT_EQ(absl::nullopt, dictionary.FindInt(account1.ToString())); } // Tests that clearing the AutofillSyncTransportOptIn works as expected. @@ -160,27 +155,23 @@ TEST_F(AutofillPrefsTest, WalletSyncTransportPref_Clear) { const CoreAccountId account2("account2"); // There should be no opt-in recorded at first. - EXPECT_TRUE(pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictEmpty()); + EXPECT_TRUE( + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); // Set the opt-in for the first account. SetUserOptedInWalletSyncTransport(pref_service(), account1, true); - EXPECT_FALSE(pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictEmpty()); + EXPECT_FALSE( + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); // Set the opt-in for the second account. SetUserOptedInWalletSyncTransport(pref_service(), account2, true); - EXPECT_FALSE(pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictEmpty()); + EXPECT_FALSE( + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); // Clear all opt-ins. The dictionary should be empty. ClearSyncTransportOptIns(pref_service()); - EXPECT_TRUE(pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictEmpty()); + EXPECT_TRUE( + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); } // Tests that the account id hash that we generate can be written and read from @@ -190,17 +181,15 @@ TEST_F(AutofillPrefsTest, WalletSyncTransportPref_CanBeSetAndReadFromJSON) { // Set the opt-in for the first account. SetUserOptedInWalletSyncTransport(pref_service(), account1, true); - EXPECT_FALSE(pref_service() - ->GetDictionary(prefs::kAutofillSyncTransportOptIn) - ->DictEmpty()); + EXPECT_FALSE( + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn).empty()); - const base::Value* dictionary = - pref_service()->GetDictionary(prefs::kAutofillSyncTransportOptIn); - ASSERT_TRUE(dictionary); + const base::Value::Dict& dictionary = + pref_service()->GetValueDict(prefs::kAutofillSyncTransportOptIn); std::string output_js; - ASSERT_TRUE(base::JSONWriter::Write(*dictionary, &output_js)); - EXPECT_EQ(*dictionary, *base::JSONReader::Read(output_js)); + ASSERT_TRUE(base::JSONWriter::Write(dictionary, &output_js)); + EXPECT_EQ(dictionary, *base::JSONReader::Read(output_js)); } } // namespace prefs diff --git a/chromium/components/autofill/core/browser/autofill_regex_constants.cc b/chromium/components/autofill/core/common/autofill_regex_constants.h index b09c8608887..91466aa2f35 100644 --- a/chromium/components/autofill/core/browser/autofill_regex_constants.cc +++ b/chromium/components/autofill/core/common/autofill_regex_constants.h @@ -2,26 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This file contains UTF16 strings that we want as char16_t arrays. - -#include "components/autofill/core/browser/autofill_regex_constants.h" +#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_REGEX_CONSTANTS_H_ +#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_REGEX_CONSTANTS_H_ namespace autofill { ///////////////////////////////////////////////////////////////////////////// // address_field.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kAttentionIgnoredRe[] = u"attention|attn"; -const char16_t kRegionIgnoredRe[] = +inline constexpr char16_t kAttentionIgnoredRe[] = u"attention|attn"; +inline constexpr char16_t kRegionIgnoredRe[] = u"province|region|other" u"|provincia" // es u"|bairro|suburb"; // pt-BR, pt-PT -const char16_t kAddressNameIgnoredRe[] = +inline constexpr char16_t kAddressNameIgnoredRe[] = u"address.*nickname|address.*label" u"|adres ([İi]sim|başlığı|adı)" // tr u"|identificação do endereço" // pt-BR, pt-PT u"|(label|judul|nama) alamat"; // id -const char16_t kCompanyRe[] = +inline constexpr char16_t kCompanyRe[] = u"company|business|organization|organisation" u"|(?<!con)firma|firmenname" // de-DE u"|empresa" // es @@ -33,26 +32,26 @@ const char16_t kCompanyRe[] = u"|شرکت" // fa u"|회사|직장" // ko-KR u"|(nama.?)?perusahaan"; // id -const char16_t kStreetNameRe[] = +inline constexpr char16_t kStreetNameRe[] = u"stra(ss|ß)e" // de u"|street" // en u"|улица|название.?улицы" // ru u"|rua|avenida" // pt-PT, pt-BR u"|((?<!do |de )endereço)" // pt-BR u"|calle"; // es-MX -const char16_t kHouseNumberRe[] = +inline constexpr char16_t kHouseNumberRe[] = u"(house.?|street.?|^)(number|no\\.?$)" // en u"|(haus|^)(nummer|nr)" // de u"|^\\*?.?número(.?\\*?$| da residência)" // pt-BR, pt-PT u"|дом|номер.?дома" // ru u"|exterior"; // es-MX -const char16_t kApartmentNumberRe[] = +inline constexpr char16_t kApartmentNumberRe[] = u"apartment" // en u"|interior" // es-MX u"|n(u|ú)mero.*app?art(a|e)ment" // es,fr,it u"|Wohnung" // de u"|квартир"; // ru -const char16_t kAddressLine1Re[] = +inline constexpr char16_t kAddressLine1Re[] = u"^address$|address[_-]?line(one)?|address1|addr1|street" u"|(?:shipping|billing)address$" u"|strasse|straße|hausnummer|housenumber" // de-DE @@ -67,7 +66,7 @@ const char16_t kAddressLine1Re[] = u"|(\\b|_)adres(?! tarifi)(\\b|_)" // tr u"|^주소.?$|주소.?1" // ko-KR u"|^alamat"; // id -const char16_t kAddressLine1LabelRe[] = +inline constexpr char16_t kAddressLine1LabelRe[] = u"(^\\W*address)" u"|(address\\W*$)" u"|(?:shipping|billing|mailing|pick.?up|drop.?off|delivery|sender|postal|" @@ -86,7 +85,7 @@ const char16_t kAddressLine1LabelRe[] = u"|(sokak|cadde).*(apartman|bina|daire|mahalle)" // tr u"|(apartman|bina|daire|mahalle).*(sokak|cadde)" u"|улиц.*(дом|корпус|квартир|этаж)|(дом|корпус|квартир|этаж).*улиц"; // ru -const char16_t kAddressLine2Re[] = +inline constexpr char16_t kAddressLine2Re[] = u"address[_-]?line(2|two)|address2|addr2|street|suite|unit" u"|adresszusatz|ergänzende.?angaben" // de-DE u"|direccion2|colonia|adicional" // es @@ -97,19 +96,19 @@ const char16_t kAddressLine2Re[] = u"|Улица" // ru u"|地址2" // zh-CN u"|주소.?2"; // ko-KR -const char16_t kAddressLine2LabelRe[] = +inline constexpr char16_t kAddressLine2LabelRe[] = u"address|line" u"|adresse" // fr-FR u"|indirizzo" // it-IT u"|地址" // zh-CN u"|주소"; // ko-KR -const char16_t kAddressLinesExtraRe[] = +inline constexpr char16_t kAddressLinesExtraRe[] = u"address.*line[3-9]|address[3-9]|addr[3-9]|street|line[3-9]" u"|municipio" // es u"|batiment|residence" // fr-FR u"|indirizzo[3-9]"; // it-IT -const char16_t kAddressLookupRe[] = u"lookup"; -const char16_t kCountryRe[] = +inline constexpr char16_t kAddressLookupRe[] = u"lookup"; +inline constexpr char16_t kCountryRe[] = u"country|countries" u"|país|pais" // es u"|(\\b|_)land(\\b|_)(?!.*(mark.*))" // de-DE landmark is a type in india. @@ -119,8 +118,8 @@ const char16_t kCountryRe[] = u"|(\\b|_)(ülke|ulce|ulke)(\\b|_)" // tr u"|کشور" // fa u"|negara"; // id -const char16_t kCountryLocationRe[] = u"location"; -const char16_t kZipCodeRe[] = +inline constexpr char16_t kCountryLocationRe[] = u"location"; +inline constexpr char16_t kZipCodeRe[] = u"((?<!\\.))zip" // .zip indicates a file extension u"|postal|post.*code|pcode" u"|pin.?code" // en-IN @@ -138,16 +137,16 @@ const char16_t kZipCodeRe[] = u"|(\\b|_)posta kodu(\\b|_)" // tr u"|우편.?번호" // ko-KR u"|kode.?pos"; // id -const char16_t kZip4Re[] = +inline constexpr char16_t kZip4Re[] = u"((?<!\\.))zip" // .zip indicates a file extension u"|^-$|post2" u"|codpos2"; // pt-BR, pt-PT -const char16_t kDependentLocalityRe[] = +inline constexpr char16_t kDependentLocalityRe[] = u"neighbo(u)?rhood" // en u"|bairro" // pt-BR, pt-PT u"|mahalle|köy" // tr u"|kecamatan"; // id -const char16_t kCityRe[] = +inline constexpr char16_t kCityRe[] = u"city|town" u"|\\bort\\b|stadt" // de-DE u"|suburb" // en-AU @@ -166,7 +165,7 @@ const char16_t kCityRe[] = u"|((\\b|_|\\*)([İii̇]l[cç]e(miz|niz)?)(\\b|_|\\*))" // tr u"|^시[^도·・]|시[·・]?군[·・]?구" // ko-KR u"|kota|kabupaten"; // id -const char16_t kStateRe[] = +inline constexpr char16_t kStateRe[] = u"(?<!(united|hist|history).?)state|county|region|province" u"|county|principality" // en-UK u"|都道府県" // ja-JP @@ -184,7 +183,7 @@ const char16_t kStateRe[] = ///////////////////////////////////////////////////////////////////////////// // search_field.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kSearchTermRe[] = +inline constexpr char16_t kSearchTermRe[] = u"^q$" u"|search" u"|query" @@ -200,7 +199,7 @@ const char16_t kSearchTermRe[] = ///////////////////////////////////////////////////////////////////////////// // field_price.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kPriceRe[] = +inline constexpr char16_t kPriceRe[] = u"\\bprice\\b|\\brate\\b|\\bcost\\b" u"|قیمة|سعر" // ar u"|قیمت" // fa @@ -209,7 +208,7 @@ const char16_t kPriceRe[] = ///////////////////////////////////////////////////////////////////////////// // credit_card_field.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kNameOnCardRe[] = +inline constexpr char16_t kNameOnCardRe[] = u"card.?(?:holder|owner)|name.*(\\b)?on(\\b)?.*card" u"|(?:card|cc).?name|cc.?full.?name" u"|karteninhaber" // de-DE @@ -221,8 +220,8 @@ const char16_t kNameOnCardRe[] = u"|nama.*kartu" // id u"|信用卡开户名|开户名|持卡人姓名" // zh-CN u"|持卡人姓名"; // zh-TW -const char16_t kNameOnCardContextualRe[] = u"name"; -const char16_t kCardNumberRe[] = +inline constexpr char16_t kNameOnCardContextualRe[] = u"name"; +inline constexpr char16_t kCardNumberRe[] = u"(add)?(?:card|cc|acct).?(?:number|#|no|num|field|pan)" u"|(?<!telefon|haus|person|fødsels|kunden)nummer" // de-DE, sv-SE, no u"|カード番号" // ja-JP @@ -234,7 +233,7 @@ const char16_t kCardNumberRe[] = // es/pt/fr u"|(numero|número|numéro)(?!.*(document|fono|phone|réservation))"; -const char16_t kCardCvcRe[] = +inline constexpr char16_t kCardCvcRe[] = u"verification|card.?identification|security.?code|card.?code" u"|security.?value" u"|security.?number|card.?pin|c-v-v" @@ -253,7 +252,7 @@ const char16_t kCardCvcRe[] = // Toolbar Bug 51451: indeed, simply matching "month" is too general for // https://rps.fidelity.com/ftgw/rps/RtlCust/CreatePIN/Init. // Instead, we match only words beginning with "month". -const char16_t kExpirationMonthRe[] = +inline constexpr char16_t kExpirationMonthRe[] = u"expir|exp.*mo|exp.*date|ccmonth|cardmonth|addmonth" u"|gueltig|gültig|monat" // de-DE u"|fecha" // es @@ -264,7 +263,7 @@ const char16_t kExpirationMonthRe[] = u"|Срок действия карты" // ru u"|masa berlaku|berlaku hingga" // id u"|月"; // zh-CN -const char16_t kExpirationYearRe[] = +inline constexpr char16_t kExpirationYearRe[] = u"exp|^/|(add)?year" u"|ablaufdatum|gueltig|gültig|jahr" // de-DE u"|fecha" // es @@ -282,16 +281,16 @@ const char16_t kExpirationYearRe[] = // - (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/browser/autofill_regexes_unittest.cc -const char16_t kExpirationDate2DigitYearRe[] = +inline constexpr char16_t kExpirationDate2DigitYearRe[] = u"(?: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/browser/autofill_regexes_unittest.cc -const char16_t kExpirationDate4DigitYearRe[] = +inline constexpr char16_t kExpirationDate4DigitYearRe[] = u"(?:exp.*date[^y\\n\\r]*|mm\\s*[-/]?\\s*)yyyy(?:[^y]|$)"; // Used to match expiration date fields that do not specify a year length. -const char16_t kExpirationDateRe[] = +inline constexpr char16_t kExpirationDateRe[] = u"expir|exp.*date|^expfield$" u"|gueltig|gültig" // de-DE u"|fecha" // es @@ -300,16 +299,16 @@ const char16_t kExpirationDateRe[] = u"|有効期限" // ja-JP u"|validade" // pt-BR, pt-PT u"|Срок действия карты"; // ru -const char16_t kGiftCardRe[] = u"gift.?(card|cert)"; -const char16_t kDebitGiftCardRe[] = +inline constexpr char16_t kGiftCardRe[] = u"gift.?(card|cert)"; +inline constexpr char16_t kDebitGiftCardRe[] = u"(?:visa|mastercard|discover|amex|american express).*gift.?card"; -const char16_t kDebitCardRe[] = u"debit.*card"; -const char16_t kDayRe[] = u"day"; +inline constexpr char16_t kDebitCardRe[] = u"debit.*card"; +inline constexpr char16_t kDayRe[] = u"day"; ///////////////////////////////////////////////////////////////////////////// // email_field.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kEmailRe[] = +inline constexpr char16_t kEmailRe[] = u"e.?mail" u"|courriel" // fr u"|correo.*electr(o|ó)nico" // es-ES @@ -327,12 +326,12 @@ const char16_t kEmailRe[] = ///////////////////////////////////////////////////////////////////////////// // name_field.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kNameIgnoredRe[] = +inline constexpr char16_t kNameIgnoredRe[] = u"user.?name|user.?id|nickname|maiden name|title|prefix|suffix|mail" u"|vollständiger.?name" // de-DE u"|用户名" // zh-CN u"|(?:사용자.?)?아이디|사용자.?ID"; // ko-KR -const char16_t kFullNameRe[] = +inline constexpr char16_t kFullNameRe[] = u"^name|full.?name|your.?name|customer.?name|bill.?name|ship.?name" u"|name.*first.*last|firstandlastname|contact.?(name|person)" u"|nombre.*y.*apellidos" // es @@ -345,11 +344,11 @@ const char16_t kFullNameRe[] = u"|(\\b|_|\\*)ad[ı]? soyad[ı]?(\\b|_|\\*)" // tr u"|성명" // ko-KR u"|nama.?(lengkap|penerima|kamu)"; // id -const char16_t kNameGenericRe[] = +inline constexpr char16_t kNameGenericRe[] = u"^name" u"|^nom" // fr-FR u"|^nome"; // pt-BR, pt-PT -const char16_t kFirstNameRe[] = +inline constexpr char16_t kFirstNameRe[] = u"first.*name|initials|fname|first$|given.*name" u"|vorname" // de-DE u"|nombre" // es @@ -363,9 +362,10 @@ const char16_t kFirstNameRe[] = u"|(\\b|_|\\*)(isim|ad|ad(i|ı|iniz|ınız)?)(\\b|_|\\*)" // tr u"|नाम" // hi u"|nama depan"; // id -const char16_t kMiddleInitialRe[] = u"middle.*initial|m\\.i\\.|mi$|\\bmi\\b"; -const char16_t kMiddleNameRe[] = u"middle.*name|mname|middle$"; -const char16_t kLastNameRe[] = +inline constexpr char16_t kMiddleInitialRe[] = + u"middle.*initial|m\\.i\\.|mi$|\\bmi\\b"; +inline constexpr char16_t kMiddleNameRe[] = u"middle.*name|mname|middle$"; +inline constexpr char16_t kLastNameRe[] = u"last.*name|lname|surname(?!\\d)|last$|secondname|family.*name" u"|nachname" // de-DE u"|apellidos?" // es @@ -380,17 +380,17 @@ const char16_t kLastNameRe[] = u"|(\\b|_|\\*)(soyisim|soyad(i|ı|iniz|ınız)?)(\\b|_|\\*)" // tr u"|\\b성(?:[^명]|\\b)" // ko-KR u"|nama belakang"; // id -const char16_t kNameLastFirstRe[] = +inline constexpr char16_t kNameLastFirstRe[] = u"(primer.*apellido)" // es u"|(apellido1)" // es u"|(apellido.*paterno)" // es u"|surname_?1|first(\\s|_)?surname"; // es -const char16_t kNameLastSecondRe[] = +inline constexpr char16_t kNameLastSecondRe[] = u"(segund.*apellido)" // es u"|(apellido2)" // es u"|(apellido.*materno)" // es u"|surname_?2|second(\\s|_)?surname"; // es -const char16_t kHonorificPrefixRe[] = +inline constexpr char16_t kHonorificPrefixRe[] = u"anrede|titel" // de-DE u"|tratamiento|encabezamiento" // es u"|^title:?$" // Matched only if there is no prefix or suffix. @@ -403,7 +403,7 @@ const char16_t kHonorificPrefixRe[] = ///////////////////////////////////////////////////////////////////////////// // phone_field.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kPhoneRe[] = +inline constexpr char16_t kPhoneRe[] = u"phone|mobile|contact.?number" u"|telefonnummer" // de-DE u"|telefono|teléfono" // es @@ -417,22 +417,22 @@ const char16_t kPhoneRe[] = u"|മൊബൈല്" // ml for mobile u"|(?:전화|핸드폰|휴대폰|휴대전화)(?:.?번호)?" // ko-KR u"|telepon|ponsel|(nomor|no\\.?).?(hp|handphone)"; // id -const char16_t kAugmentedPhoneCountryCodeRe[] = +inline constexpr char16_t kAugmentedPhoneCountryCodeRe[] = u"^[^0-9+]*(?:\\+|00)\\s*([1-9]\\d{0,3})\\D*$"; -const char16_t kCountryCodeRe[] = +inline constexpr char16_t kCountryCodeRe[] = u"country.*code|ccode|_cc|phone.*code|user.*phone.*code"; -const char16_t kAreaCodeNotextRe[] = u"^\\($"; -const char16_t kAreaCodeRe[] = +inline constexpr char16_t kAreaCodeNotextRe[] = u"^\\($"; +inline constexpr char16_t kAreaCodeRe[] = u"area.*code|acode|area" u"|지역.?번호"; // ko-KR -const char16_t kPhonePrefixSeparatorRe[] = u"^-$|^\\)$"; -const char16_t kPhoneSuffixSeparatorRe[] = u"^-$"; -const char16_t kPhonePrefixRe[] = +inline constexpr char16_t kPhonePrefixSeparatorRe[] = u"^-$|^\\)$"; +inline constexpr char16_t kPhoneSuffixSeparatorRe[] = u"^-$"; +inline constexpr char16_t kPhonePrefixRe[] = u"prefix|exchange" u"|preselection" // fr-FR u"|ddd"; // pt-BR, pt-PT -const char16_t kPhoneSuffixRe[] = u"suffix"; -const char16_t kPhoneExtensionRe[] = +inline constexpr char16_t kPhoneSuffixRe[] = u"suffix"; +inline constexpr char16_t kPhoneExtensionRe[] = u"\\bext|ext\\b|extension" u"|ramal"; // pt-BR, pt-PT @@ -440,20 +440,20 @@ const char16_t kPhoneExtensionRe[] = // travel_field.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kPassportRe[] = +inline constexpr char16_t kPassportRe[] = u"document.*number|passport" // en-US u"|passeport" // fr-FR u"|numero.*documento|pasaporte" // es-ES u"|書類"; // ja-JP -const char16_t kTravelOriginRe[] = +inline constexpr char16_t kTravelOriginRe[] = u"point.*of.*entry|arrival" // en-US u"|punto.*internaci(o|ó)n|fecha.*llegada" // es-ES u"|入国"; // ja-JP -const char16_t kTravelDestinationRe[] = +inline constexpr char16_t kTravelDestinationRe[] = u"departure" // en-US u"|fecha.*salida|destino" // es-ES u"|出国"; // ja-JP -const char16_t kFlightRe[] = +inline constexpr char16_t kFlightRe[] = u"airline|flight" // en-US u"|aerol(i|í)nea|n(u|ú)mero.*vuelo" // es-ES u"|便名|航空会社"; // ja-JP @@ -461,7 +461,12 @@ const char16_t kFlightRe[] = ///////////////////////////////////////////////////////////////////////////// // validation.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kUPIVirtualPaymentAddressRe[] = + +// Used to match field data that might be a UPI Virtual Payment Address. +// See: +// - http://crbug.com/702220 +// - https://upipayments.co.in/virtual-payment-address-vpa/ +inline constexpr char16_t kUPIVirtualPaymentAddressRe[] = u"^[\\w.+-_]+@(" // eg user@ u"\\w+\\.ifsc\\.npci|" // IFSC code u"aadhaar\\.npci|" // Aadhaar number @@ -576,44 +581,66 @@ const char16_t kUPIVirtualPaymentAddressRe[] = u"yesbankltd" u")$"; -const char16_t kInternationalBankAccountNumberRe[] = +// Used to match the HTML name and label for International Bank Account Number +// (IBAN). +inline constexpr char16_t kIBANRe[] = + u"(\\biban(\\b|_)|international bank account number)"; + +// Used to match field value that might be an International Bank Account Number. +// TODO(crbug.com/977377): The regex doesn't match IBANs for Saint Lucia (LC), +// Kazakhstan (KZ) and Romania (RO). Consider replace the regex with something +// like "(?:IT|SM)\d{2}[A-Z]\d{22}|CY\d{2}[A-Z]\d{23}...". For reference: +// - https://www.swift.com/resource/iban-registry-pdf +inline constexpr char16_t kInternationalBankAccountNumberValueRe[] = u"^[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}$"; // Matches all 3 and 4 digit numbers. -const char16_t kCreditCardCVCPattern[] = u"^\\d{3,4}$"; +inline constexpr char16_t kCreditCardCVCPattern[] = u"^\\d{3,4}$"; // Matches numbers in the range [2010-2099]. -const char16_t kCreditCard4DigitExpYearPattern[] = u"^[2][0][1-9][0-9]$"; +inline constexpr char16_t kCreditCard4DigitExpYearPattern[] = + u"^[2][0][1-9][0-9]$"; ///////////////////////////////////////////////////////////////////////////// // form_structure.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kUrlSearchActionRe[] = u"/search(/|((\\w*\\.\\w+)?$))"; + +// Match the path values for form actions that look like generic search: +// e.g. /search +// /search/ +// /search/products... +// /products/search/ +// /blah/search_all.jsp +inline constexpr char16_t kUrlSearchActionRe[] = + u"/search(/|((\\w*\\.\\w+)?$))"; ///////////////////////////////////////////////////////////////////////////// // form_parser.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kSocialSecurityRe[] = u"ssn|social.?security.?(num(ber)?|#)*"; -const char16_t kOneTimePwdRe[] = +inline constexpr char16_t kSocialSecurityRe[] = + u"ssn|social.?security.?(num(ber)?|#)*"; +inline constexpr char16_t kOneTimePwdRe[] = u"one.?time|sms.?(code|token|password|pwd|pass)"; // Matches strings that consist of one repeated non alphanumeric symbol, // that is likely a result of website modifying the value to hide it. -const char16_t kHiddenValueRe[] = u"^(\\W)\\1+$"; +inline constexpr char16_t kHiddenValueRe[] = u"^(\\W)\\1+$"; ///////////////////////////////////////////////////////////////////////////// // merchant_promo_code_field.cc ///////////////////////////////////////////////////////////////////////////// // "promo code", "promotion code", "promotional code" are all acceptable // keywords. -const char16_t kMerchantPromoCodeRe[] = +inline constexpr char16_t kMerchantPromoCodeRe[] = u"(promo(tion|tional)?|gift|discount|coupon)[-_. ]*code"; ///////////////////////////////////////////////////////////////////////////// // votes_uploader.cc ///////////////////////////////////////////////////////////////////////////// -const char16_t kEmailValueRe[] = +inline constexpr char16_t kEmailValueRe[] = u"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"; -const char16_t kPhoneValueRe[] = u"^[0-9()+-]{6,25}$"; -const char16_t kUsernameLikeValueRe[] = u"[A-Za-z0-9_\\-.]{7,30}"; +inline constexpr char16_t kPhoneValueRe[] = u"^[0-9()+-]{6,25}$"; +inline constexpr char16_t kUsernameLikeValueRe[] = u"[A-Za-z0-9_\\-.]{7,30}"; } // namespace autofill + +#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/common/autofill_regexes.cc new file mode 100644 index 00000000000..b62c57a7a64 --- /dev/null +++ b/chromium/components/autofill/core/common/autofill_regexes.cc @@ -0,0 +1,96 @@ +// 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 <map> +#include <memory> +#include <string> +#include <utility> + +#include "base/check.h" +#include "base/i18n/unicodestring.h" +#include "base/memory/ptr_util.h" + +namespace { + +// Maximum length of the string to match to avoid causing an icu::RegexMatcher +// stack overflow. (crbug.com/1198219) +constexpr int kMaxStringLength = 5000; + +} // namespace + +namespace autofill { + +std::unique_ptr<const icu::RegexPattern> CompileRegex( + base::StringPiece16 regex) { + const icu::UnicodeString icu_regex(false, regex.data(), regex.length()); + UErrorCode status = U_ZERO_ERROR; + std::unique_ptr<icu::RegexPattern> regex_pattern = base::WrapUnique( + icu::RegexPattern::compile(icu_regex, UREGEX_CASE_INSENSITIVE, status)); + DCHECK(U_SUCCESS(status)); + return regex_pattern; +} + +bool MatchesRegex(base::StringPiece16 input, + const icu::RegexPattern& regex_pattern, + std::vector<std::u16string>* groups) { + if (input.size() > kMaxStringLength) + return false; + + UErrorCode status = U_ZERO_ERROR; + // `icu_input` must outlive `regex_matcher` because it holds a reference to + // it. + icu::UnicodeString icu_input(false, input.data(), input.length()); + std::unique_ptr<icu::RegexMatcher> regex_matcher = + base::WrapUnique(regex_pattern.matcher(icu_input, status)); + UBool matched = regex_matcher->find(0, status); + DCHECK(U_SUCCESS(status)); + + if (matched && groups) { + int32_t matched_groups = regex_matcher->groupCount(); + groups->resize(matched_groups + 1); + for (int32_t i = 0; i < matched_groups + 1; ++i) { + icu::UnicodeString match_unicode = regex_matcher->group(i, status); + DCHECK(U_SUCCESS(status)); + (*groups)[i] = base::i18n::UnicodeStringToString16(match_unicode); + } + } + return matched; +} + +AutofillRegexCache::AutofillRegexCache(ThreadSafe thread_safe) + : thread_safe_(thread_safe) { + if (!thread_safe_) + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +AutofillRegexCache::~AutofillRegexCache() { + if (!thread_safe_) + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +const icu::RegexPattern* AutofillRegexCache::GetRegexPattern( + base::StringPiece16 regex) { + auto GetOrCreate = [&]() { + auto it = cache_.find(regex); + if (it == cache_.end()) { + bool success; + std::tie(it, success) = + cache_.emplace(std::u16string(regex), CompileRegex(regex)); + DCHECK(success); + } + DCHECK(it != cache_.end()); + DCHECK(it->second.get()); + return it->second.get(); + }; + if (!thread_safe_) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return GetOrCreate(); + } + base::AutoLock lock(lock_); + return GetOrCreate(); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_regexes.h b/chromium/components/autofill/core/common/autofill_regexes.h new file mode 100644 index 00000000000..d378abbf621 --- /dev/null +++ b/chromium/components/autofill/core/common/autofill_regexes.h @@ -0,0 +1,87 @@ +// 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_AUTOFILL_REGEXES_H_ +#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_REGEXES_H_ + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "base/no_destructor.h" +#include "base/sequence_checker.h" +#include "base/strings/string_piece.h" +#include "base/synchronization/lock.h" +#include "base/types/strong_alias.h" +#include "third_party/icu/source/i18n/unicode/regex.h" + +namespace autofill { + +using ThreadSafe = base::StrongAlias<struct ThreadSafeTag, bool>; + +// Compiles a case-insensitive regular expression. +// +// The return icu::RegexPattern is thread-safe (because it's const and icu +// guarantees thread-safety of the const functions). In particularly this +// includes icu::RegexPattern::matcher(). +// +// May also be used to initialize `static base::NoDestructor<icu::RegexPattern>` +// function-scope variables. +std::unique_ptr<const icu::RegexPattern> CompileRegex( + base::StringPiece16 regex); + +// Returns true if `regex` is found in `input`. +// If `groups` is non-null, it gets resized and the found capture groups +// are written into it. +// Thread-safe. +bool MatchesRegex(base::StringPiece16 input, + const icu::RegexPattern& regex_pattern, + std::vector<std::u16string>* groups = nullptr); + +// Calls MatchesRegex() after compiling the `regex` on the first call and +// retrieving it from a static variable in subsequent calls. +// +// This function is thread-safe. +template <const char16_t regex[]> +bool MatchesRegex(base::StringPiece16 input, + std::vector<std::u16string>* groups = nullptr) { + static base::NoDestructor<std::unique_ptr<const icu::RegexPattern>> + regex_pattern(CompileRegex(regex)); + return MatchesRegex(input, **regex_pattern, groups); +} + +// A cache of compiled regex patterns. It can be configured to be thread-safe +// (using a mutex) or not (in which case it uses a sequence checker). +class AutofillRegexCache { + public: + explicit AutofillRegexCache(ThreadSafe thread_safe); + ~AutofillRegexCache(); + + AutofillRegexCache(const AutofillRegexCache&) = delete; + AutofillRegexCache& operator=(const AutofillRegexCache&) = delete; + + // Returns the compiled regex corresponding to `regex`. + // This function is thread-safe if `thread_safe_`. + // The returned object is thread-safe in any case (because it's const). + // Although the returned pointer is guaranteed to be non-nullptr, we do not + // return a reference to avoid accidental copies. + const icu::RegexPattern* GetRegexPattern(base::StringPiece16 regex); + + private: + // `MatchesPattern()` uses the lock if `thread_safe_`. Otherwise, it validates + // the sequence. + ThreadSafe thread_safe_{false}; + base::Lock lock_; + SEQUENCE_CHECKER(sequence_checker_); + + // Maps regex strings to their corresponding compiled regex patterns. + std:: + map<std::u16string, std::unique_ptr<const icu::RegexPattern>, std::less<>> + cache_; +}; + +} // namespace autofill + +#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/common/autofill_regexes_unittest.cc index 236b9fc5288..dffad89354c 100644 --- a/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc +++ b/chromium/components/autofill/core/common/autofill_regexes_unittest.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/browser/autofill_regexes.h" +#include "components/autofill/core/common/autofill_regexes.h" // Keep these tests in sync with // components/autofill/core/browser/pattern_provider/default_regex_patterns_unittest.cc @@ -12,12 +12,24 @@ #include <string> -#include "components/autofill/core/browser/autofill_regex_constants.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/common/autofill_regex_constants.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace autofill { +namespace { + +bool MatchesRegex(base::StringPiece16 input, + base::StringPiece16 regex, + std::vector<std::u16string>* groups = nullptr) { + static base::NoDestructor<AutofillRegexCache> cache(ThreadSafe(true)); + return autofill::MatchesRegex(input, *cache->GetRegexPattern(regex), groups); +} + +} // namespace + struct InputPatternTestCase { const char16_t* const input; const char16_t* const pattern; @@ -28,12 +40,12 @@ 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(test_case.input, test_case.pattern)); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.pattern)); + EXPECT_TRUE(MatchesRegex(test_case.input, test_case.pattern)); } -INSTANTIATE_TEST_SUITE_P(AutofillRegexes, +INSTANTIATE_TEST_SUITE_P(AutofillRegexesTest, PositiveSampleTest, testing::Values( // Empty pattern @@ -56,12 +68,12 @@ 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(test_case.input, test_case.pattern)); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.pattern)); + EXPECT_FALSE(MatchesRegex(test_case.input, test_case.pattern)); } -INSTANTIATE_TEST_SUITE_P(AutofillRegexes, +INSTANTIATE_TEST_SUITE_P(AutofillRegexesTest, NegativeSampleTest, testing::Values( // Empty string @@ -90,7 +102,7 @@ TEST_P(CaptureTest, SampleRegexes) { auto test_case = GetParam(); std::vector<std::u16string> groups; EXPECT_EQ(test_case.matches, - MatchesPattern(test_case.input, test_case.pattern, &groups)); + MatchesRegex(test_case.input, test_case.pattern, &groups)); EXPECT_THAT(groups, testing::Eq(test_case.groups)); } @@ -123,9 +135,9 @@ class ExpirationDate2DigitYearPositive TEST_P(ExpirationDate2DigitYearPositive, ExpirationDate2DigitYearRegexes) { auto test_case = GetParam(); - SCOPED_TRACE(test_case.input); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); const std::u16string pattern = kExpirationDate2DigitYearRe; - EXPECT_TRUE(MatchesPattern(test_case.input, pattern)); + EXPECT_TRUE(MatchesRegex(test_case.input, pattern)); } INSTANTIATE_TEST_SUITE_P( @@ -157,9 +169,9 @@ class ExpirationDate2DigitYearNegative TEST_P(ExpirationDate2DigitYearNegative, ExpirationDate2DigitYearRegexes) { auto test_case = GetParam(); - SCOPED_TRACE(test_case.input); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); const std::u16string pattern = kExpirationDate2DigitYearRe; - EXPECT_FALSE(MatchesPattern(test_case.input, pattern)); + EXPECT_FALSE(MatchesRegex(test_case.input, pattern)); } INSTANTIATE_TEST_SUITE_P( @@ -198,8 +210,8 @@ class ExpirationDate4DigitYearPositive TEST_P(ExpirationDate4DigitYearPositive, ExpirationDate4DigitYearRegexes) { auto test_case = GetParam(); const std::u16string pattern = kExpirationDate4DigitYearRe; - SCOPED_TRACE(test_case.input); - EXPECT_TRUE(MatchesPattern(test_case.input, pattern)); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); + EXPECT_TRUE(MatchesRegex(test_case.input, pattern)); } INSTANTIATE_TEST_SUITE_P(AutofillRegexes, @@ -233,8 +245,8 @@ class ExpirationDate4DigitYearNegative TEST_P(ExpirationDate4DigitYearNegative, ExpirationDate4DigitYearRegexes) { auto test_case = GetParam(); const std::u16string pattern = kExpirationDate4DigitYearRe; - SCOPED_TRACE(test_case.input); - EXPECT_FALSE(MatchesPattern(test_case.input, pattern)); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); + EXPECT_FALSE(MatchesRegex(test_case.input, pattern)); } INSTANTIATE_TEST_SUITE_P( @@ -271,9 +283,9 @@ class ZipCodePositive : public testing::TestWithParam<InputTestCase> {}; TEST_P(ZipCodePositive, ZipCodeRegexes) { auto test_case = GetParam(); - SCOPED_TRACE(test_case.input); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); const std::u16string pattern = kZipCodeRe; - EXPECT_TRUE(MatchesPattern(test_case.input, pattern)); + EXPECT_TRUE(MatchesRegex(test_case.input, pattern)); } INSTANTIATE_TEST_SUITE_P(AutofillRegexes, @@ -286,9 +298,9 @@ class ZipCodeNegative : public testing::TestWithParam<InputTestCase> {}; TEST_P(ZipCodeNegative, ZipCodeRegexes) { auto test_case = GetParam(); - SCOPED_TRACE(test_case.input); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); const std::u16string pattern = kZipCodeRe; - EXPECT_FALSE(MatchesPattern(test_case.input, pattern)); + EXPECT_FALSE(MatchesRegex(test_case.input, pattern)); } INSTANTIATE_TEST_SUITE_P( @@ -302,9 +314,9 @@ class Zip4Positive : public testing::TestWithParam<InputTestCase> {}; TEST_P(Zip4Positive, Zip4Regexes) { auto test_case = GetParam(); - SCOPED_TRACE(test_case.input); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); const std::u16string pattern = kZip4Re; - EXPECT_TRUE(MatchesPattern(test_case.input, pattern)); + EXPECT_TRUE(MatchesRegex(test_case.input, pattern)); } INSTANTIATE_TEST_SUITE_P(AutofillRegexes, @@ -315,9 +327,9 @@ class Zip4Negative : public testing::TestWithParam<InputTestCase> {}; TEST_P(Zip4Negative, Zip4Regexes) { auto test_case = GetParam(); - SCOPED_TRACE(test_case.input); + SCOPED_TRACE(base::UTF16ToUTF8(test_case.input)); const std::u16string pattern = kZip4Re; - EXPECT_FALSE(MatchesPattern(test_case.input, pattern)); + EXPECT_FALSE(MatchesRegex(test_case.input, pattern)); } INSTANTIATE_TEST_SUITE_P( diff --git a/chromium/components/autofill/core/common/autofill_util.h b/chromium/components/autofill/core/common/autofill_util.h index 8ac75dcbf93..c48df5696b7 100644 --- a/chromium/components/autofill/core/common/autofill_util.h +++ b/chromium/components/autofill/core/common/autofill_util.h @@ -10,14 +10,11 @@ #include <string> #include <vector> +#include "base/feature_list.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" #include "url/gurl.h" -namespace base { -struct Feature; -} - namespace autofill { // The length of the GUIDs used for local autofill data. It is different than diff --git a/chromium/components/autofill/core/common/form_data.h b/chromium/components/autofill/core/common/form_data.h index 4574e8996fa..3b4285c820a 100644 --- a/chromium/components/autofill/core/common/form_data.h +++ b/chromium/components/autofill/core/common/form_data.h @@ -134,7 +134,22 @@ struct FrameTokenWithPredecessor { // [4] https://html.spec.whatwg.org/multipage/input.html#attr-input-type // clang-format on struct FormData { - // Returns true if all members of forms |a| and |b| are identical. + // Returns true if many members of forms |a| and |b| are identical. + // + // "Many" is intended to be "all", but currently the following members are not + // being compared: + // + // - FormData::button_titles, + // - FormData::full_url, + // - FormData::is_action_empty, + // - FormData::main_frame_origin, + // - FormData::host_frame, + // - FormData::version, + // - FormData::submission_event, + // - FormData::username_predictions, + // - FormData::is_gaia_with_skip_save_password_form, + // - FormData::frame_id, + // - some fields of FormFieldData (see FormFieldData::Equal()). static bool DeepEqual(const FormData& a, const FormData& b); FormData(); diff --git a/chromium/components/autofill/core/common/form_field_data.cc b/chromium/components/autofill/core/common/form_field_data.cc index e0dd60f8d1f..d99449dc208 100644 --- a/chromium/components/autofill/core/common/form_field_data.cc +++ b/chromium/components/autofill/core/common/form_field_data.cc @@ -7,10 +7,13 @@ #include <algorithm> #include <tuple> +#include "base/notreached.h" #include "base/pickle.h" #include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_util.h" @@ -26,6 +29,9 @@ namespace { // deserialization routine from previous kFormFieldDataPickleVersion format. const int kFormFieldDataPickleVersion = 9; +// Default section name for the fields. +static constexpr char kDefaultSection[] = "-default"; + void WriteSelectOption(const SelectOption& option, base::Pickle* pickle) { pickle->WriteString16(option.value); pickle->WriteString16(option.content); @@ -195,6 +201,7 @@ struct LabelInfo { // Feature |kAutofillSkipComparingInferredLabels| weakens equivalence of // labels: two labels are equivalent if they were inferred from the same // type of tag other than a LABEL tag. + // TODO(crbug.com/1211834): The experiment seems dead; remove? return base::FeatureList::IsEnabled( features::kAutofillSkipComparingInferredLabels) && source != FormFieldData::LabelSource::kLabelTag && @@ -232,18 +239,134 @@ auto IdentityTuple(const FormFieldData& f) { // uniquely identify the field as well. return std::tuple_cat( SimilarityTuple(f), - std::tie( -// TODO(crbug.com/896689): On iOS the unique_id member uniquely addresses -// this field in the DOM. -#if BUILDFLAG(IS_IOS) - f.unique_id, -#endif - f.autocomplete_attribute, f.placeholder, f.max_length, f.css_classes, - f.is_focusable, f.should_autocomplete, f.role, f.text_direction)); + std::tie(f.autocomplete_attribute, f.placeholder, f.max_length, + f.css_classes, f.is_focusable, f.should_autocomplete, f.role, + f.text_direction)); } } // namespace +Section::Section() = default; +Section::Section(const Section& section) = default; +Section::~Section() = default; + +bool operator==(const Section::Autocomplete& a, + const Section::Autocomplete& b) { + return std::tie(a.section, a.mode) == std::tie(b.section, b.mode); +} + +bool operator!=(const Section::Autocomplete& a, + const Section::Autocomplete& b) { + return !(a == b); +} + +bool operator<(const Section::Autocomplete& a, const Section::Autocomplete& b) { + return std::tie(a.section, a.mode) < std::tie(b.section, b.mode); +} + +bool operator==(const Section::FieldIdentifier& a, + const Section::FieldIdentifier& b) { + return std::tie(a.field_name, a.local_frame_id, a.field_renderer_id) == + std::tie(b.field_name, b.local_frame_id, b.field_renderer_id); +} + +bool operator!=(const Section::FieldIdentifier& a, + const Section::FieldIdentifier& b) { + return !(a == b); +} + +bool operator<(const Section::FieldIdentifier& a, + const Section::FieldIdentifier& b) { + return std::tie(a.field_name, a.local_frame_id, a.field_renderer_id) < + std::tie(b.field_name, b.local_frame_id, b.field_renderer_id); +} + +bool operator==(const Section& a, const Section& b) { + return std::tie(a.field_type_group_, a.prefix_) == + std::tie(b.field_type_group_, b.prefix_); +} + +bool operator!=(const Section& a, const Section& b) { + return !(a == b); +} + +bool operator<(const Section& a, const Section& b) { + return std::tie(a.field_type_group_, a.prefix_) < + std::tie(b.field_type_group_, b.prefix_); +} + +bool Section::is_from_autocomplete() const { + return absl::holds_alternative<Autocomplete>(prefix_); +} + +void Section::set_field_type_group(FieldTypeGroupSuffix field_type_group) { + field_type_group_ = field_type_group; +} + +void Section::SetPrefixToCreditCard() { + prefix_ = CreditCard(); +} + +bool Section::SetPrefixFromAutocomplete(Autocomplete autocomplete) { + if (autocomplete.section.empty() && autocomplete.mode == HTML_MODE_NONE) + return false; + prefix_ = std::move(autocomplete); + return true; +} + +void Section::SetPrefixFromFieldIdentifier( + const FormFieldData& field, + base::flat_map<LocalFrameToken, size_t>& frame_token_ids) { + size_t generated_frame_id = + frame_token_ids.emplace(field.host_frame, frame_token_ids.size()) + .first->second; + prefix_ = FieldIdentifier(base::UTF16ToUTF8(field.name), generated_frame_id, + field.unique_renderer_id); +} + +std::string Section::ToString() const { + std::string section_name; + if (const Autocomplete* autocomplete = absl::get_if<Autocomplete>(&prefix_)) { + // To prevent potential section name collisions, append `kDefaultSection` + // suffix to fields without a `HtmlFieldMode`. Without this, 'autocomplete' + // attribute values "section--shipping street-address" and "shipping + // street-address" would have the same prefix. + section_name = + autocomplete->section + + (autocomplete->mode != HTML_MODE_NONE + ? "-" + std::string(HtmlFieldModeToStringPiece(autocomplete->mode)) + : kDefaultSection); + } else if (const FieldIdentifier* f = + absl::get_if<FieldIdentifier>(&prefix_)) { + FieldIdentifier field_identifier = *f; + section_name = base::StrCat( + {field_identifier.field_name, "_", + base::NumberToString(field_identifier.local_frame_id), "_", + base::NumberToString(field_identifier.field_renderer_id.value())}); + } else if (absl::holds_alternative<CreditCard>(prefix_)) { + section_name = "credit-card"; + } + + section_name = section_name.empty() ? kDefaultSection : section_name; + switch (field_type_group_) { + case FieldTypeGroupSuffix::kNoGroup: + return section_name; + case FieldTypeGroupSuffix::kDefault: + return section_name + kDefaultSection; + case FieldTypeGroupSuffix::kCreditCard: + return section_name + "-cc"; + } + NOTREACHED(); +} + +LogBuffer& operator<<(LogBuffer& buffer, const Section& section) { + return buffer << section.ToString(); +} + +std::ostream& operator<<(std::ostream& os, const Section& section) { + return os << section.ToString(); +} + FormFieldData::FormFieldData() = default; FormFieldData::FormFieldData(const FormFieldData&) = default; diff --git a/chromium/components/autofill/core/common/form_field_data.h b/chromium/components/autofill/core/common/form_field_data.h index de784bca427..8214c437d73 100644 --- a/chromium/components/autofill/core/common/form_field_data.h +++ b/chromium/components/autofill/core/common/form_field_data.h @@ -14,6 +14,7 @@ #include "base/i18n/rtl.h" #include "build/build_config.h" +#include "components/autofill/core/common/html_field_types.h" #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" #include "components/autofill/core/common/signatures.h" #include "components/autofill/core/common/unique_ids.h" @@ -63,6 +64,103 @@ struct SelectOption { std::u16string content; }; +// Stores information about the section of the field. +class Section { + public: + // In the second pass of `FormStructure::IdentifySections()`, a suffix is + // added to the legacy section string based on the field type group. + enum class FieldTypeGroupSuffix : uint8_t { kNoGroup, kDefault, kCreditCard }; + + struct Autocomplete { + std::string section; + HtmlFieldMode mode; + }; + + using Default = base::StrongAlias<struct DefaultTag, absl::monostate>; + + // TODO(crbug.com/1153539): Remove when sectioning is redesigned. + using CreditCard = base::StrongAlias<struct CreditCardTag, absl::monostate>; + + struct FieldIdentifier { + FieldIdentifier() = default; + FieldIdentifier(std::string field_name, + size_t local_frame_id, + FieldRendererId field_renderer_id) + : field_name(std::move(field_name)), + local_frame_id(local_frame_id), + field_renderer_id(field_renderer_id) {} + + friend bool operator==(const FieldIdentifier& a, const FieldIdentifier& b); + friend bool operator!=(const FieldIdentifier& a, const FieldIdentifier& b); + friend bool operator<(const FieldIdentifier& a, const FieldIdentifier& b); + + std::string field_name; + size_t local_frame_id; + FieldRendererId field_renderer_id; + }; + + // Represents the prefix of the section's name. The type of the variant + // represents the origin of the prefix: + // - `Default` is the empty, initial value before running any sectioning + // algorithm, + // - `Autocomplete` represents a prefix derived from the autocomplete + // attribute, + // - `FieldIdentifier` represents a prefix generated from the first field in + // the section, + // - `CreditCard` represents the credit card prefix generated by + // FormStructure::IdentifySections(). + // + // TODO(crbug.com/1153539): Remove CreditCard when sectioning is redesigned. + using SectionPrefix = + absl::variant<Default, Autocomplete, FieldIdentifier, CreditCard>; + + friend bool operator==(const Section& a, const Section& b); + friend bool operator!=(const Section& a, const Section& b); + friend bool operator<(const Section& a, const Section& b); + + Section(); + Section(const Section& section); + ~Section(); + + bool is_from_autocomplete() const; + + void set_field_type_group(FieldTypeGroupSuffix field_type_group); + void SetPrefixToCreditCard(); + bool SetPrefixFromAutocomplete(Autocomplete autocomplete); + + // Sets the section prefix based on the field identifiers: the field's name, + // mapped frame id, renderer id. We do not use LocalFrameTokens but instead + // map them to consecutive integers using `frame_token_ids`, which uniquely + // identify a frame within a given FormStructure. Since we do not intend to + // compare sections from different FormStructures, this is sufficient. + // + // We intentionally do not include the LocalFrameToken in the section + // because frame tokens should not be sent to a renderer. + // + // TODO(crbug.com/1257141): Remove special handling of FrameTokens. + void SetPrefixFromFieldIdentifier( + const FormFieldData& field, + base::flat_map<LocalFrameToken, size_t>& frame_token_ids); + + // Reconstructs `this` to a string. The string representation of the section + // is used in the renderer. + // TODO(crbug/1257141): Remove when fixed. + std::string ToString() const; + + private: + friend struct mojo::StructTraits<autofill::mojom::SectionDataView, + autofill::Section>; + + // The `kNoGroup` is assigned in the beginning, as + // `FormStructure::IdentifySections()` was not run yet. + FieldTypeGroupSuffix field_type_group_ = FieldTypeGroupSuffix::kNoGroup; + + SectionPrefix prefix_; +}; + +LogBuffer& operator<<(LogBuffer& buffer, const Section& section); +std::ostream& operator<<(std::ostream& os, const Section& section); + // Stores information about a field in a form. Read more about forms and fields // at FormData. struct FormFieldData { @@ -70,7 +168,31 @@ struct FormFieldData { using RoleAttribute = mojom::FormFieldData_RoleAttribute; using LabelSource = mojom::FormFieldData_LabelSource; - // Returns true if all members of fields |a| and |b| are identical. + // Returns true if many members of fields |a| and |b| are identical. + // + // "Many" is intended to be "all", but currently the following members are not + // being compared: + // + // - FormFieldData::value, + // - FormFieldData::aria_label, + // - FormFieldData::aria_description, + // - FormFieldData::host_frame, + // - FormFieldData::host_form_id, + // - FormFieldData::host_form_signature, + // - FormFieldData::origin, + // - FormFieldData::force_override, + // - FormFieldData::form_control_ax_id, + // - FormFieldData::section, + // - FormFieldData::is_autofilled, + // - FormFieldData::properties_mask, + // - FormFieldData::is_enabled, + // - FormFieldData::is_readonly, + // - FormFieldData::user_input, + // - FormFieldData::options, + // - FormFieldData::label_source, + // - FormFieldData::bounds, + // - FormFieldData::datalist_values, + // - FormFieldData::datalist_labels. static bool DeepEqual(const FormFieldData& a, const FormFieldData& b); FormFieldData(); @@ -85,9 +207,10 @@ struct FormFieldData { FieldGlobalId global_id() const { return {host_frame, unique_renderer_id}; } // An identifier of the renderer form that contained this field. - // This may be from the browser form that contains this field in the case of a - // frame-transcending form. See ContentAutofillRouter and internal::FormForest - // for details on the distinction between renderer and browser forms. + // This may be different from the browser form that contains this field in the + // case of a frame-transcending form. See ContentAutofillRouter and + // internal::FormForest for details on the distinction between renderer and + // browser forms. FormGlobalId renderer_form_id() const { return {host_frame, host_form_id}; } // TODO(crbug/1211834): This function is deprecated. Use @@ -123,26 +246,10 @@ struct FormFieldData { return is_focusable && role != RoleAttribute::kPresentation; } - // These functions do not work for Autofill code. - // TODO(https://crbug.com/1006745): Fix this. bool DidUserType() const; bool HadFocus() const; bool WasAutofilled() const; -#if BUILDFLAG(IS_IOS) - // The identifier which uniquely addresses this field in the DOM. This is an - // ephemeral value which is not guaranteed to be stable across page loads. It - // serves to allow a given field to be found during the current navigation. - // - // TODO(crbug.com/896689): Expand the logic/application of this to other - // platforms and/or merge this concept with |unique_renderer_id|. - std::u16string unique_id; -#define EXPECT_EQ_UNIQUE_ID(expected, actual) \ - EXPECT_EQ((expected).unique_id, (actual).unique_id) -#else -#define EXPECT_EQ_UNIQUE_ID(expected, actual) -#endif - // NOTE: update SameFieldAs() if needed when adding new a member. // NOTE: update SimilarFieldAs() if needed when adding new a member. // NOTE: update DynamicallySameFieldAs() if needed when adding new a member. @@ -197,7 +304,7 @@ struct FormFieldData { // The unique identifier of the section (e.g. billing vs. shipping address) // of this field. - std::string section; + Section section; // Note: we use uint64_t instead of size_t because this struct is sent over // IPC which could span 32 & 64 bit processes. We chose uint64_t instead of @@ -262,7 +369,6 @@ std::ostream& operator<<(std::ostream& os, const FormFieldData& field); // TODO(crbug.com/1208354): Replace this with FormData::DeepEqual(). #define EXPECT_FORM_FIELD_DATA_EQUALS(expected, actual) \ do { \ - EXPECT_EQ_UNIQUE_ID(expected, actual); \ EXPECT_EQ(expected.label, actual.label); \ EXPECT_EQ(expected.name, actual.name); \ EXPECT_EQ(expected.value, actual.value); \ diff --git a/chromium/components/autofill/core/common/html_field_types.cc b/chromium/components/autofill/core/common/html_field_types.cc new file mode 100644 index 00000000000..d54928a2b29 --- /dev/null +++ b/chromium/components/autofill/core/common/html_field_types.cc @@ -0,0 +1,23 @@ +// Copyright 2022 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/html_field_types.h" + +#include "base/notreached.h" +#include "base/strings/string_piece.h" + +namespace autofill { + +base::StringPiece HtmlFieldModeToStringPiece(HtmlFieldMode mode) { + switch (mode) { + case HTML_MODE_NONE: + return ""; + case HTML_MODE_BILLING: + return "billing"; + case HTML_MODE_SHIPPING: + return "shipping"; + } + NOTREACHED(); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/common/html_field_types.h b/chromium/components/autofill/core/common/html_field_types.h new file mode 100644 index 00000000000..0cfcdfe2d45 --- /dev/null +++ b/chromium/components/autofill/core/common/html_field_types.h @@ -0,0 +1,115 @@ +// Copyright 2022 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_HTML_FIELD_TYPES_H_ +#define COMPONENTS_AUTOFILL_CORE_COMMON_HTML_FIELD_TYPES_H_ + +#include <stdint.h> +#include "base/strings/string_piece_forward.h" + +namespace autofill { + +// The list of all HTML autocomplete field type hints supported by Chrome. +// See [ http://is.gd/whatwg_autocomplete ] for the full list of specced hints. +enum HtmlFieldType { + // Default type. + HTML_TYPE_UNSPECIFIED, + + // Name types. + HTML_TYPE_NAME, + HTML_TYPE_HONORIFIC_PREFIX, + HTML_TYPE_GIVEN_NAME, + HTML_TYPE_ADDITIONAL_NAME, + HTML_TYPE_FAMILY_NAME, + + // Business types. + HTML_TYPE_ORGANIZATION, + + // Address types. + HTML_TYPE_STREET_ADDRESS, + HTML_TYPE_ADDRESS_LINE1, + HTML_TYPE_ADDRESS_LINE2, + HTML_TYPE_ADDRESS_LINE3, + HTML_TYPE_ADDRESS_LEVEL1, // For U.S. addresses, corresponds to the state. + HTML_TYPE_ADDRESS_LEVEL2, // For U.S. addresses, corresponds to the city. + HTML_TYPE_ADDRESS_LEVEL3, // An area that is more specific than LEVEL2. + HTML_TYPE_COUNTRY_CODE, // The ISO 3166-1-alpha-2 country code. + HTML_TYPE_COUNTRY_NAME, // The localized country name. + HTML_TYPE_POSTAL_CODE, + HTML_TYPE_FULL_ADDRESS, // The complete address, formatted for display. + + // Credit card types. + HTML_TYPE_CREDIT_CARD_NAME_FULL, + HTML_TYPE_CREDIT_CARD_NAME_FIRST, + HTML_TYPE_CREDIT_CARD_NAME_LAST, + HTML_TYPE_CREDIT_CARD_NUMBER, + HTML_TYPE_CREDIT_CARD_EXP, + HTML_TYPE_CREDIT_CARD_EXP_MONTH, + HTML_TYPE_CREDIT_CARD_EXP_YEAR, + HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE, + HTML_TYPE_CREDIT_CARD_TYPE, + + // Phone number types. + HTML_TYPE_TEL, + HTML_TYPE_TEL_COUNTRY_CODE, + HTML_TYPE_TEL_NATIONAL, + HTML_TYPE_TEL_AREA_CODE, + HTML_TYPE_TEL_LOCAL, + HTML_TYPE_TEL_LOCAL_PREFIX, + HTML_TYPE_TEL_LOCAL_SUFFIX, + HTML_TYPE_TEL_EXTENSION, + + // Email. + HTML_TYPE_EMAIL, + + // Birthdate. + HTML_TYPE_BIRTHDATE_DAY, + HTML_TYPE_BIRTHDATE_MONTH, + HTML_TYPE_BIRTHDATE_YEAR, + + // Transaction details. + HTML_TYPE_TRANSACTION_AMOUNT, + HTML_TYPE_TRANSACTION_CURRENCY, + + // Variants of type hints specified in the HTML specification that are + // inferred based on a field's 'maxlength' attribute. + // TODO(isherman): Remove these types, in favor of understanding maxlength + // when filling fields. See also: AutofillField::phone_part_. + HTML_TYPE_ADDITIONAL_NAME_INITIAL, + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, + HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, + HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, + + // Universal Payment Interface - Virtual Payment Address. + HTML_TYPE_UPI_VPA, + + // Phone number verification one-time-codes. + HTML_TYPE_ONE_TIME_CODE, + + // Promo code for merchant sites. + HTML_TYPE_MERCHANT_PROMO_CODE, + + // International Bank Account Number (IBAN) for banking and merchant sites. + HTML_TYPE_IBAN, + + // Non-standard autocomplete types. + HTML_TYPE_UNRECOGNIZED, +}; + +// The list of all HTML autocomplete field mode hints supported by Chrome. +// See [ http://is.gd/whatwg_autocomplete ] for the full list of specced hints. +enum HtmlFieldMode : uint8_t { + HTML_MODE_NONE, + HTML_MODE_BILLING, + HTML_MODE_SHIPPING, +}; + +// Maps HTML_MODE_BILLING and HTML_MODE_SHIPPING to their string constants, as +// specified in the autocomplete standard. +base::StringPiece HtmlFieldModeToStringPiece(HtmlFieldMode mode); + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_COMMON_HTML_FIELD_TYPES_H_ diff --git a/chromium/components/autofill/core/common/logging/log_buffer.cc b/chromium/components/autofill/core/common/logging/log_buffer.cc index 7f2dffbebf6..c8d955c3f7e 100644 --- a/chromium/components/autofill/core/common/logging/log_buffer.cc +++ b/chromium/components/autofill/core/common/logging/log_buffer.cc @@ -13,40 +13,40 @@ namespace autofill { namespace { -bool IsElement(const base::Value& value) { - const std::string* type = value.FindStringKey("type"); +bool IsElement(const base::Value::Dict& value) { + const std::string* type = value.FindString("type"); return type && *type == "element"; } -bool IsTextNode(const base::Value& value) { - const std::string* type = value.FindStringKey("type"); +bool IsTextNode(const base::Value::Dict& value) { + const std::string* type = value.FindString("type"); return type && *type == "text"; } -bool IsFragment(const base::Value& value) { - const std::string* type = value.FindStringKey("type"); +bool IsFragment(const base::Value::Dict& value) { + const std::string* type = value.FindString("type"); return type && *type == "fragment"; } -void AppendChildToLastNode(std::vector<base::Value>* buffer, - base::Value&& new_child) { +void AppendChildToLastNode(std::vector<base::Value::Dict>* buffer, + base::Value::Dict&& new_child) { if (buffer->empty()) { buffer->push_back(std::move(new_child)); return; } - base::Value& parent = buffer->back(); + base::Value::Dict& parent = buffer->back(); // Elements and Fragments can have children, but TextNodes cannot. DCHECK(!IsTextNode(parent)); - if (auto* children = parent.FindListKey("children")) { + if (auto* children = parent.FindList("children")) { children->Append(std::move(new_child)); return; } base::Value::List list; list.Append(std::move(new_child)); - parent.SetKey("children", base::Value(std::move(list))); + parent.Set("children", std::move(list)); } // This is an optimization to reduce the number of text nodes in the DOM. @@ -57,40 +57,41 @@ void AppendChildToLastNode(std::vector<base::Value>* buffer, // // If the last child of the element in buffer is a text node, append |text| to // it and return true (successful coalescing). Otherwise return false. -bool TryCoalesceString(std::vector<base::Value>* buffer, +bool TryCoalesceString(std::vector<base::Value::Dict>* buffer, base::StringPiece text) { if (buffer->empty()) return false; - base::Value& parent = buffer->back(); - auto* children = parent.FindListKey("children"); + base::Value::Dict& parent = buffer->back(); + auto* children = parent.FindList("children"); if (!children) return false; - DCHECK(!children->GetListDeprecated().empty()); - auto& last_child = children->GetListDeprecated().back(); + DCHECK(!children->empty()); + auto& last_child = children->back().GetDict(); if (!IsTextNode(last_child)) return false; - std::string* old_text = last_child.FindStringKey("value"); + std::string* old_text = last_child.FindString("value"); old_text->append(text.data(), text.size()); return true; } -base::Value CreateEmptyFragment() { +base::Value::Dict CreateEmptyFragment() { base::Value::Dict dict; dict.Set("type", "fragment"); - return base::Value(std::move(dict)); + return dict; } } // namespace -LogBuffer::LogBuffer() { - buffer_.push_back(CreateEmptyFragment()); +LogBuffer::LogBuffer(IsActive active) : active_(*active) { + if (active_) + buffer_.push_back(CreateEmptyFragment()); } LogBuffer::LogBuffer(LogBuffer&& other) noexcept = default; LogBuffer& LogBuffer::operator=(LogBuffer&& other) = default; LogBuffer::~LogBuffer() = default; -base::Value LogBuffer::RetrieveResult() { +absl::optional<base::Value::Dict> LogBuffer::RetrieveResult() { // The buffer should always start with a fragment. DCHECK(buffer_.size() >= 1); @@ -98,14 +99,15 @@ base::Value LogBuffer::RetrieveResult() { while (buffer_.size() > 1) *this << CTag{}; - auto* children = buffer_[0].FindListKey("children"); - if (!children || children->GetListDeprecated().empty()) - return base::Value(); + auto* children = buffer_[0].FindList("children"); + if (!children || children->empty()) + return absl::nullopt; // If the fragment has a single child, remove it from |children| and return // that directly. - if (children->GetListDeprecated().size() == 1) { - return std::move(std::move(*children).TakeListDeprecated().back()); + if (children->size() == 1) { + return absl::optional<base::Value::Dict>( + std::move((*children).back().GetDict())); } return std::exchange(buffer_.back(), CreateEmptyFragment()); @@ -129,7 +131,7 @@ LogBuffer& operator<<(LogBuffer& buf, CTag&& tag) { if (buf.buffer_.size() <= 1) return buf; - base::Value node_to_add = std::move(buf.buffer_.back()); + base::Value::Dict node_to_add = std::move(buf.buffer_.back()); buf.buffer_.pop_back(); AppendChildToLastNode(&buf.buffer_, std::move(node_to_add)); @@ -140,16 +142,15 @@ LogBuffer& operator<<(LogBuffer& buf, Attrib&& attrib) { if (!buf.active()) return buf; - base::Value& node = buf.buffer_.back(); + base::Value::Dict& node = buf.buffer_.back(); DCHECK(IsElement(node)); - if (auto* attributes = node.FindDictKey("attributes")) { - attributes->SetKey(std::move(attrib.name), - base::Value(std::move(attrib.value))); + if (auto* attributes = node.FindDict("attributes")) { + attributes->Set(attrib.name, std::move(attrib.value)); } else { base::Value::Dict dict; dict.Set(attrib.name, std::move(attrib.value)); - node.SetKey("attributes", base::Value(std::move(dict))); + node.Set("attributes", std::move(dict)); } return buf; @@ -171,12 +172,11 @@ LogBuffer& operator<<(LogBuffer& buf, base::StringPiece text) { if (TryCoalesceString(&buf.buffer_, text)) return buf; - base::Value::Dict dict; - dict.Set("type", "text"); + base::Value::Dict node_to_add; + node_to_add.Set("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. - dict.Set("value", text); - base::Value node_to_add(std::move(dict)); + node_to_add.Set("value", text); AppendChildToLastNode(&buf.buffer_, std::move(node_to_add)); return buf; } @@ -189,19 +189,21 @@ LogBuffer& operator<<(LogBuffer& buf, LogBuffer&& buffer) { if (!buf.active()) return buf; - base::Value node_to_add(buffer.RetrieveResult()); - if (node_to_add.is_none()) + absl::optional<base::Value::Dict> node_to_add = buffer.RetrieveResult(); + if (!node_to_add) return buf; - if (IsFragment(node_to_add)) { - auto* children = node_to_add.FindListKey("children"); + if (IsFragment(*node_to_add)) { + auto* children = node_to_add->FindList("children"); if (!children) return buf; - for (auto& child : children->GetListDeprecated()) - AppendChildToLastNode(&buf.buffer_, std::exchange(child, base::Value())); + for (auto& child : *children) { + AppendChildToLastNode( + &buf.buffer_, std::exchange(child.GetDict(), base::Value::Dict())); + } return buf; } - AppendChildToLastNode(&buf.buffer_, std::move(node_to_add)); + AppendChildToLastNode(&buf.buffer_, std::move(*node_to_add)); return buf; } @@ -242,7 +244,7 @@ namespace { template <typename T, typename CharT = typename T::value_type> LogBuffer HighlightValueInternal(T haystack, T needle) { using StringPieceT = base::BasicStringPiece<CharT>; - LogBuffer buffer; + LogBuffer buffer(LogBuffer::IsActive(true)); size_t pos = haystack.find(needle); if (pos == StringPieceT::npos || needle.empty()) { buffer << haystack; diff --git a/chromium/components/autofill/core/common/logging/log_buffer.h b/chromium/components/autofill/core/common/logging/log_buffer.h index a71b15613ba..c1f8e280438 100644 --- a/chromium/components/autofill/core/common/logging/log_buffer.h +++ b/chromium/components/autofill/core/common/logging/log_buffer.h @@ -12,7 +12,9 @@ #include "base/memory/raw_ptr.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" +#include "base/types/strong_alias.h" #include "base/values.h" +#include "components/autofill/core/common/logging/log_macros.h" #include "url/gurl.h" // The desired pattern to generate log messages is to pass a scope, a log @@ -46,6 +48,21 @@ // LogBuffer buffer; // for (...) { buffer << something; } // LogBuffer() << std::move(buffer); +// +// In practice the LogBuffer requires a boolean parameter indicating whether +// logging should happen. You should rely on +// components/autofill/core/common/logging/log_macros.h and follow one of the +// following patterns: +// +// (1) void MyFunction(LogManager* log_manager) { +// LOG_AF(log_mannager) << "foobar"; +// } +// (2) void MyFunction(LogManager* log_manager) { +// LogBuffer buffer( +// /*active=*/ log_manager && log_manager->IsLoggingActive()); +// LOG_AF(buffer) << "foobar"; +// LOG_AF(log_manager) << std::move(buffer); +// } namespace autofill { @@ -76,11 +93,15 @@ struct Br {}; // See LogTableRowBuffer below. struct Tr {}; +class LogManager; + // A buffer into which you can stream values. See the top of this header file // for samples. class LogBuffer { public: - LogBuffer(); + using IsActive = base::StrongAlias<struct ActiveTag, bool>; + + explicit LogBuffer(IsActive active = IsActive(true)); ~LogBuffer(); LogBuffer(LogBuffer&& other) noexcept; @@ -89,13 +110,12 @@ class LogBuffer { LogBuffer(const LogBuffer& other) = delete; LogBuffer& operator=(const LogBuffer& other) = delete; - // Returns the contents of the buffer and empties it. - base::Value RetrieveResult(); + // Returns the contents of the buffer if any and empties it. + absl::optional<base::Value::Dict> RetrieveResult(); // Returns whether an active WebUI is listening. If false, the buffer may // not do any logging. bool active() const { return active_; } - void set_active(bool active) { active_ = active; } private: friend LogBuffer& operator<<(LogBuffer& buf, Tag&& tag); @@ -114,7 +134,7 @@ class LogBuffer { // constructed. Once it is read (i.e. closed via a CTag), it is popped from // the stack and attached as a child of the previously second last element. // Only the first element of buffer_ is a 'fragment' and it is never closed. - std::vector<base::Value> buffer_; + std::vector<base::Value::Dict> buffer_; bool active_ = true; }; @@ -205,6 +225,36 @@ LogBuffer HighlightValue(base::StringPiece haystack, base::StringPiece needle); LogBuffer HighlightValue(base::StringPiece16 haystack, base::StringPiece16 needle); +namespace internal { + +// Traits for LOG_AF() macro for `LogBuffer*`. +template <typename T> +struct LoggerTraits< + T, + typename std::enable_if_t< + std::is_convertible_v<decltype(std::declval<T>()), const LogBuffer*>>> { + static bool active(const LogBuffer* log_buffer) { + return log_buffer && log_buffer->active(); + } + + static LogBuffer& get_stream(LogBuffer* log_buffer) { return *log_buffer; } +}; + +// Traits for LOG_AF() macro for `LogBuffer&`. +template <typename T> +struct LoggerTraits< + T, + typename std::enable_if_t< + std::is_convertible_v<decltype(std::declval<T>()), const LogBuffer&>>> { + static bool active(const LogBuffer& log_buffer) { + return log_buffer.active(); + } + + static LogBuffer& get_stream(LogBuffer& log_buffer) { return log_buffer; } +}; + +} // namespace internal + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_COMMON_LOGGING_LOG_BUFFER_H_ diff --git a/chromium/components/autofill/core/common/logging/log_buffer_unittest.cc b/chromium/components/autofill/core/common/logging/log_buffer_unittest.cc index b1ce2386f2a..3083fdd6098 100644 --- a/chromium/components/autofill/core/common/logging/log_buffer_unittest.cc +++ b/chromium/components/autofill/core/common/logging/log_buffer_unittest.cc @@ -17,7 +17,7 @@ TEST(LogBuffer, JSONSerializeString) { LogBuffer buffer; buffer << "<foo><!--\""; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); // JSON takes care of serializing the <, we don't want < as that would then // be escaped twice. EXPECT_EQ(R"({"type":"text","value":"\u003Cfoo>\u003C!--\""})", json); @@ -27,7 +27,7 @@ TEST(LogBuffer, JSONSerializeString16) { LogBuffer buffer; buffer << u"<foo><!--\""; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); // JSON takes care of serializing the <, we don't want < as that would then // be escaped twice. EXPECT_EQ(R"({"type":"text","value":"\u003Cfoo>\u003C!--\""})", json); @@ -37,7 +37,7 @@ TEST(LogBuffer, SupportNumbers) { LogBuffer buffer; buffer << 42; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"type":"text","value":"42"})", json); } @@ -45,21 +45,21 @@ TEST(LogBuffer, SanitizeURLs) { LogBuffer buffer; buffer << GURL("https://user:pw@www.example.com:80/foo?bar=1#foo"); std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); // Verify that the url gets scrubbed. EXPECT_EQ(R"({"type":"text","value":"https://www.example.com:80/"})", json); } TEST(LogBuffer, Empty) { LogBuffer buffer; - EXPECT_EQ(base::Value(), buffer.RetrieveResult()); + EXPECT_FALSE(buffer.RetrieveResult()); } TEST(LogBuffer, UnclosedTag) { LogBuffer buffer; buffer << Tag{"foo"}; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"type":"element","value":"foo"})", json); } @@ -67,7 +67,7 @@ TEST(LogBuffer, ClosedTag) { LogBuffer buffer; buffer << Tag{"foo"} << CTag{}; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"type":"element","value":"foo"})", json); } @@ -75,7 +75,7 @@ TEST(LogBuffer, NestedTag) { LogBuffer buffer; buffer << Tag{"foo"} << Tag{"bar"} << CTag{} << CTag{}; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"children":[{"type":"element","value":"bar"}],)" R"("type":"element","value":"foo"})", json); @@ -85,7 +85,7 @@ TEST(LogBuffer, NestedTagClosingTooOften) { LogBuffer buffer; buffer << Tag{"foo"} << Tag{"bar"} << CTag{} << CTag{} << CTag{}; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"children":[{"type":"element","value":"bar"}],)" R"("type":"element","value":"foo"})", json); @@ -95,7 +95,7 @@ TEST(LogBuffer, NestedTagClosingNotAtAll) { LogBuffer buffer; buffer << Tag{"foo"} << Tag{"bar"}; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"children":[{"type":"element","value":"bar"}],)" R"("type":"element","value":"foo"})", json); @@ -106,7 +106,7 @@ TEST(LogBuffer, NestedTagWithAttributes) { buffer << Tag{"foo"} << Tag{"bar"} << Attrib{"b1", "1"} << Attrib{"b2", "2"} << CTag{} << Attrib{"f1", "1"}; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ( R"({"attributes":{"f1":"1"},"children":[)" R"({"attributes":{"b1":"1","b2":"2"},"type":"element","value":"bar"})" @@ -118,7 +118,7 @@ TEST(LogBuffer, DivWithBr) { LogBuffer buffer; buffer << Tag{"div"} << "foo" << Br{} << "bar" << CTag{}; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"children":[{"type":"text","value":"foo"},)" R"({"type":"element","value":"br"},{"type":"text","value":"bar"}],)" R"("type":"element","value":"div"})", @@ -130,7 +130,7 @@ TEST(LogBuffer, CoalesceStrings) { buffer << Tag{"div"} << "foo" << "bar"; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"children":[{"type":"text","value":"foobar"}],)" R"("type":"element","value":"div"})", json); @@ -156,7 +156,7 @@ TEST(LogBuffer, CanStreamCustomObjects) { SampleObject o{42, "foobar<!--"}; buffer << o; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"children":[)" // table /**/ R"({"children":[)" // tr /****/ R"({"children":[{"type":"text","value":"x"}],)" // td @@ -185,14 +185,14 @@ TEST(LogBuffer, LogTableRowBuffer) { actual << Tr{} << Attrib{"class", "awesome"} << "Foo" << "Bar"; actual << CTag{"table"}; - EXPECT_EQ(expected.RetrieveResult(), actual.RetrieveResult()); + EXPECT_EQ(*expected.RetrieveResult(), *actual.RetrieveResult()); } TEST(LogBuffer, CreateFragment) { LogBuffer buffer; buffer << "foo" << Br{} << "bar"; std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"children":[{"type":"text","value":"foo"},)" R"({"type":"element","value":"br"},{"type":"text","value":"bar"}],)" R"("type":"fragment"})", @@ -205,7 +205,7 @@ TEST(LogBuffer, AppendFragmentByInlining) { LogBuffer buffer; buffer << std::move(tmp_buffer); std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"children":[{"type":"text","value":"foo"},)" R"({"type":"element","value":"br"},{"type":"text","value":"bar"}],)" R"("type":"fragment"})", @@ -218,7 +218,7 @@ TEST(LogBuffer, AppendSingleElementBuffer) { LogBuffer buffer; buffer << std::move(tmp_buffer); std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_TRUE(base::JSONWriter::Write(*buffer.RetrieveResult(), &json)); EXPECT_EQ(R"({"type":"text","value":"foo"})", json); } @@ -227,7 +227,7 @@ TEST(LogBuffer, Highlight) { expected << "foo" << Tag{"b"} << "bar" << CTag{"b"} << "baz"; LogBuffer actual; actual << HighlightValue("foobarbaz", "bar"); - EXPECT_EQ(expected.RetrieveResult(), actual.RetrieveResult()); + EXPECT_EQ(*expected.RetrieveResult(), *actual.RetrieveResult()); } TEST(LogBuffer, HighlightAtStart) { @@ -235,7 +235,7 @@ TEST(LogBuffer, HighlightAtStart) { expected << Tag{"b"} << "foo" << CTag{"b"} << "barbaz"; LogBuffer actual; actual << HighlightValue("foobarbaz", "foo"); - EXPECT_EQ(expected.RetrieveResult(), actual.RetrieveResult()); + EXPECT_EQ(*expected.RetrieveResult(), *actual.RetrieveResult()); } TEST(LogBuffer, HighlightAtEnd) { @@ -243,7 +243,7 @@ TEST(LogBuffer, HighlightAtEnd) { expected << "foobar" << Tag{"b"} << "baz" << CTag{"b"}; LogBuffer actual; actual << HighlightValue("foobarbaz", "baz"); - EXPECT_EQ(expected.RetrieveResult(), actual.RetrieveResult()); + EXPECT_EQ(*expected.RetrieveResult(), *actual.RetrieveResult()); } TEST(LogBuffer, HighlightEmpty) { @@ -251,7 +251,7 @@ TEST(LogBuffer, HighlightEmpty) { expected << "foobarbaz"; LogBuffer actual; actual << HighlightValue("foobarbaz", ""); - EXPECT_EQ(expected.RetrieveResult(), actual.RetrieveResult()); + EXPECT_EQ(*expected.RetrieveResult(), *actual.RetrieveResult()); } TEST(LogBuffer, HighlightNotFound) { @@ -259,7 +259,7 @@ TEST(LogBuffer, HighlightNotFound) { expected << "foobarbaz"; LogBuffer actual; actual << HighlightValue("foobarbaz", "notfound"); - EXPECT_EQ(expected.RetrieveResult(), actual.RetrieveResult()); + EXPECT_EQ(*expected.RetrieveResult(), *actual.RetrieveResult()); } TEST(LogBuffer, HighlightEmptyString) { diff --git a/chromium/components/autofill/core/common/logging/log_macros.h b/chromium/components/autofill/core/common/logging/log_macros.h new file mode 100644 index 00000000000..e7253ad0a21 --- /dev/null +++ b/chromium/components/autofill/core/common/logging/log_macros.h @@ -0,0 +1,52 @@ +// Copyright 2022 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_LOGGING_LOG_MACROS_H_ +#define COMPONENTS_AUTOFILL_CORE_COMMON_LOGGING_LOG_MACROS_H_ + +// Logging macro in the style of LOG(INFO) intended for +// chrome://autofill-internals. +// +// In `LOG_AF(logger) << expression`, the `expression` is evaluated only +// if the `logger` is active. The expression `logger` must be of type +// `LogManager` or `LogBuffer` or `LogManager*` or `LogBuffer`*. +// +// Support for other types of `logger` can be added by adding template +// specializations of `LoggerTraits`. +#define LOG_AF(logger) \ + !::autofill::internal::LoggerTraits<decltype(logger)>::active(logger) \ + ? (void)0 \ + : ::autofill::internal::Voidify() & \ + ::autofill::internal::LoggerTraits<decltype(logger)>::get_stream( \ + logger) + +namespace autofill::internal { + +// Traits for targets of LOG_AF(). There are currently specializations for +// `LogManager*` and `LogBuffer*`. +template <typename T, typename Enable = void> +struct LoggerTraits { + // Returns true iff logging to should be enabled. + static bool active(const T& logger) { return false; } + + // Returns an object that implements the stream insertion operator + // operator<<(). + static int get_stream(const T& logger) { return {}; } +}; + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". +class Voidify { + public: + Voidify() = default; + // This has to be an operator with a precedence lower than << but + // higher than ?: + template <typename U> + void operator&(const U&) {} +}; + +} // namespace autofill::internal + +#endif // COMPONENTS_AUTOFILL_CORE_COMMON_LOGGING_LOG_MACROS_H_ diff --git a/chromium/components/autofill/core/common/mojom/BUILD.gn b/chromium/components/autofill/core/common/mojom/BUILD.gn index f076b75e8cb..e4a2f5efc99 100644 --- a/chromium/components/autofill/core/common/mojom/BUILD.gn +++ b/chromium/components/autofill/core/common/mojom/BUILD.gn @@ -19,6 +19,10 @@ mojom("mojo_types") { { types = [ { + mojom = "autofill.mojom.Section" + cpp = "::autofill::Section" + }, + { mojom = "autofill.mojom.FormData" cpp = "::autofill::FormData" }, diff --git a/chromium/components/autofill/core/common/mojom/autofill_types.mojom b/chromium/components/autofill/core/common/mojom/autofill_types.mojom index a709ecc3fba..0afc2f8cce1 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types.mojom +++ b/chromium/components/autofill/core/common/mojom/autofill_types.mojom @@ -35,8 +35,9 @@ enum SubmissionSource { NONE, // No submission signal was detected. SAME_DOCUMENT_NAVIGATION, // The form was removed in same document // navigation. - XHR_SUCCEEDED, // The form was removed whem XHR succeeded. - FRAME_DETACHED, // The subframe which has form was detached. + XHR_SUCCEEDED, // The form was removed when XHR succeeded. + FRAME_DETACHED, // The subframe or non primary main frame + // containing the form was detached. DOM_MUTATION_AFTER_XHR, // The form was removed after XHR. PROBABLY_FORM_SUBMITTED, // The form was probably submitted since new page // is loaded. @@ -136,6 +137,38 @@ struct SelectOption { mojo_base.mojom.String16 content; }; +// autofill::Section::Autocomplete +// (components/autofill/core/common/form_field_data.h) +struct SectionAutocomplete { + string section; + // autofill::HtmlFieldMode + uint8 html_field_mode; +}; + +// autofill::Section::FieldIdentifier +// (components/autofill/core/common/form_field_data.h) +struct SectionFieldIdentifier { + string field_name; + uint64 local_frame_id; + FieldRendererId field_renderer_id; +}; + +// autofill::Section::SectionPrefix +// (components/autofill/core/common/form_field_data.h) +union SectionPrefix { + bool default_prefix; + SectionAutocomplete autocomplete_section_prefix; + SectionFieldIdentifier from_field_prefix; + bool credit_card_prefix; +}; + +// autofill::Section (components/autofill/core/common/form_field_data.h) +struct Section { + // autofill::Section::FieldTypeGroupSuffix + uint8 field_type_group; + SectionPrefix prefix; +}; + // autofill::FormFieldData (components/autofill/core/common/form_field_data.h) struct FormFieldData { enum CheckStatus { @@ -185,7 +218,7 @@ struct FormFieldData { uint64 max_length; bool is_autofilled; - string section; + Section section; CheckStatus check_status; bool is_focusable; bool is_visible; diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc index 88fe33bc5f2..1e41d1570d2 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc +++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc @@ -5,6 +5,8 @@ #include "components/autofill/core/common/mojom/autofill_types_mojom_traits.h" #include "base/i18n/rtl.h" +#include "components/autofill/core/common/form_field_data.h" +#include "components/autofill/core/common/html_field_types.h" #include "mojo/public/cpp/base/string16_mojom_traits.h" #include "mojo/public/cpp/base/time_mojom_traits.h" #include "third_party/abseil-cpp/absl/types/variant.h" @@ -69,6 +71,97 @@ bool StructTraits< } // static +autofill::mojom::SectionPrefixDataView::Tag +UnionTraits<autofill::mojom::SectionPrefixDataView, + autofill::Section::SectionPrefix>:: + GetTag(const autofill::Section::SectionPrefix& r) { + if (absl::holds_alternative<autofill::Section::Default>(r)) + return autofill::mojom::SectionPrefixDataView::Tag::kDefaultPrefix; + if (absl::holds_alternative<autofill::Section::Autocomplete>(r)) { + return autofill::mojom::SectionPrefixDataView::Tag:: + kAutocompleteSectionPrefix; + } + if (absl::holds_alternative<autofill::Section::FieldIdentifier>(r)) + return autofill::mojom::SectionPrefixDataView::Tag::kFromFieldPrefix; + if (absl::holds_alternative<autofill::Section::CreditCard>(r)) + return autofill::mojom::SectionPrefixDataView::Tag::kCreditCardPrefix; + + NOTREACHED(); + return autofill::mojom::SectionPrefixDataView::Tag::kDefaultPrefix; +} + +// static +bool UnionTraits<autofill::mojom::SectionPrefixDataView, + autofill::Section::SectionPrefix>:: + Read(autofill::mojom::SectionPrefixDataView data, + autofill::Section::SectionPrefix* out) { + switch (data.tag()) { + case autofill::mojom::SectionPrefixDataView::Tag::kDefaultPrefix: + *out = autofill::Section::Default(); + break; + case autofill::mojom::SectionPrefixDataView::Tag:: + kAutocompleteSectionPrefix: { + autofill::Section::Autocomplete autocomplete_section_prefix; + if (!data.ReadAutocompleteSectionPrefix(&autocomplete_section_prefix)) + return false; + *out = std::move(autocomplete_section_prefix); + break; + } + case autofill::mojom::SectionPrefixDataView::Tag::kFromFieldPrefix: { + autofill::Section::FieldIdentifier field_identifier; + if (!data.ReadFromFieldPrefix(&field_identifier)) + return false; + *out = std::move(field_identifier); + break; + } + case autofill::mojom::SectionPrefixDataView::Tag::kCreditCardPrefix: + *out = autofill::Section::CreditCard(); + break; + } + return true; +} + +// static +bool StructTraits<autofill::mojom::SectionAutocompleteDataView, + autofill::Section::Autocomplete>:: + Read(autofill::mojom::SectionAutocompleteDataView data, + autofill::Section::Autocomplete* out) { + if (!data.ReadSection(&out->section)) + return false; + static_assert(sizeof(data.html_field_mode()) <= + sizeof(autofill::HtmlFieldMode)); + out->mode = static_cast<autofill::HtmlFieldMode>(data.html_field_mode()); + return true; +} + +// static +bool StructTraits<autofill::mojom::SectionFieldIdentifierDataView, + autofill::Section::FieldIdentifier>:: + Read(autofill::mojom::SectionFieldIdentifierDataView data, + autofill::Section::FieldIdentifier* out) { + if (!data.ReadFieldName(&out->field_name)) + return false; + out->local_frame_id = data.local_frame_id(); + if (!data.ReadFieldRendererId(&out->field_renderer_id)) + return false; + return true; +} + +// static +bool StructTraits<autofill::mojom::SectionDataView, autofill::Section>::Read( + autofill::mojom::SectionDataView data, + autofill::Section* out) { + static_assert(sizeof(data.field_type_group()) <= + sizeof(autofill::Section::FieldTypeGroupSuffix)); + out->field_type_group_ = static_cast<autofill::Section::FieldTypeGroupSuffix>( + data.field_type_group()); + + if (!data.ReadPrefix(&out->prefix_)) + return false; + return true; +} + +// static bool StructTraits< autofill::mojom::FormFieldDataDataView, autofill::FormFieldData>::Read(autofill::mojom::FormFieldDataDataView data, diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h index 9060cf6a44a..0710814c876 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h +++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h @@ -14,7 +14,6 @@ #include "components/autofill/core/common/aliases.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_data_predictions.h" -#include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/form_field_data_predictions.h" #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" #include "components/autofill/core/common/password_form_fill_data.h" @@ -92,6 +91,89 @@ struct StructTraits<autofill::mojom::SelectOptionDataView, }; template <> +struct UnionTraits<autofill::mojom::SectionPrefixDataView, + autofill::Section::SectionPrefix> { + static autofill::mojom::SectionPrefixDataView::Tag GetTag( + const autofill::Section::SectionPrefix& r); + + static bool default_prefix(const autofill::Section::SectionPrefix& r) { + DCHECK(absl::holds_alternative<autofill::Section::Default>(r)); + return true; + } + + static autofill::Section::Autocomplete autocomplete_section_prefix( + const autofill::Section::SectionPrefix& r) { + return absl::get<autofill::Section::Autocomplete>(r); + } + + static autofill::Section::FieldIdentifier from_field_prefix( + const autofill::Section::SectionPrefix& r) { + return absl::get<autofill::Section::FieldIdentifier>(r); + } + + static bool credit_card_prefix(const autofill::Section::SectionPrefix& r) { + DCHECK(absl::holds_alternative<autofill::Section::CreditCard>(r)); + return true; + } + + static bool Read(autofill::mojom::SectionPrefixDataView data, + autofill::Section::SectionPrefix* out); +}; + +template <> +struct StructTraits<autofill::mojom::SectionAutocompleteDataView, + autofill::Section::Autocomplete> { + static const std::string& section(const autofill::Section::Autocomplete& r) { + return r.section; + } + + static uint8_t html_field_mode(const autofill::Section::Autocomplete& r) { + static_assert(sizeof(r.mode) <= sizeof(uint8_t)); + return r.mode; + } + + static bool Read(autofill::mojom::SectionAutocompleteDataView data, + autofill::Section::Autocomplete* out); +}; + +template <> +struct StructTraits<autofill::mojom::SectionFieldIdentifierDataView, + autofill::Section::FieldIdentifier> { + static const std::string& field_name( + const autofill::Section::FieldIdentifier& r) { + return r.field_name; + } + + static size_t local_frame_id(const autofill::Section::FieldIdentifier& r) { + return r.local_frame_id; + } + + static autofill::FieldRendererId field_renderer_id( + const autofill::Section::FieldIdentifier& r) { + return r.field_renderer_id; + } + + static bool Read(autofill::mojom::SectionFieldIdentifierDataView data, + autofill::Section::FieldIdentifier* out); +}; + +template <> +struct StructTraits<autofill::mojom::SectionDataView, autofill::Section> { + static uint8_t field_type_group(const autofill::Section& r) { + static_assert(sizeof(r.field_type_group_) <= sizeof(uint8_t)); + return static_cast<uint8_t>(r.field_type_group_); + } + + static const autofill::Section::SectionPrefix& prefix( + const autofill::Section& r) { + return r.prefix_; + } + + static bool Read(autofill::mojom::SectionDataView data, + autofill::Section* out); +}; + +template <> struct StructTraits<autofill::mojom::FormFieldDataDataView, autofill::FormFieldData> { static const std::u16string& label(const autofill::FormFieldData& r) { @@ -168,7 +250,7 @@ struct StructTraits<autofill::mojom::FormFieldDataDataView, return r.is_autofilled; } - static const std::string& section(const autofill::FormFieldData& r) { + static const autofill::Section& section(const autofill::FormFieldData& r) { return r.section; } 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 b3f8d8f0a82..d1b2aa7799c 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 @@ -12,6 +12,7 @@ #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" +#include "components/autofill/core/common/html_field_types.h" #include "components/autofill/core/common/mojom/test_autofill_types.mojom.h" #include "components/autofill/core/common/password_generation_util.h" #include "components/autofill/core/common/signatures.h" @@ -145,6 +146,10 @@ class AutofillTypeTraitsTestImpl : public testing::Test, std::move(callback).Run(s); } + void PassSection(const Section& s, PassSectionCallback callback) override { + std::move(callback).Run(s); + } + void PassFormDataPredictions( const FormDataPredictions& s, PassFormDataPredictionsCallback callback) override { @@ -241,6 +246,64 @@ void ExpectPasswordGenerationUIData( std::move(closure).Run(); } +// Test all Section::SectionPrefix states. +class AutofillTypeTraitsTestImplSectionTest + : public AutofillTypeTraitsTestImpl, + public testing::WithParamInterface<Section> { + public: + const Section& section() const { return GetParam(); } +}; + +TEST_P(AutofillTypeTraitsTestImplSectionTest, PassSection) { + base::RunLoop loop; + mojo::Remote<mojom::TypeTraitsTest> remote(GetTypeTraitsTestRemote()); + remote->PassSection( + section(), + base::BindOnce( + [](const Section& a, base::OnceClosure closure, const Section& b) { + EXPECT_EQ(a, b); + std::move(closure).Run(); + }, + section(), loop.QuitClosure())); + loop.Run(); +} + +std::vector<Section> SectionTestCases() { + std::vector<Section> test_cases; + Section s; + // Default. + test_cases.push_back(s); + + // Autocomplete. + s = Section(); + s.SetPrefixFromAutocomplete({.section = "autocomplete_section", + .mode = HtmlFieldMode::HTML_MODE_BILLING}); + s.set_field_type_group(Section::FieldTypeGroupSuffix::kDefault); + test_cases.push_back(s); + + // FieldIdentifier. + s = Section(); + base::flat_map<LocalFrameToken, size_t> frame_token_ids; + FormFieldData field; + field.name = u"from_field_name"; + field.host_frame = test::MakeLocalFrameToken(); + field.unique_renderer_id = test::MakeFieldRendererId(); + s.SetPrefixFromFieldIdentifier(field, frame_token_ids); + test_cases.push_back(s); + + // CreditCard. + s = Section(); + s.SetPrefixToCreditCard(); + s.set_field_type_group(Section::FieldTypeGroupSuffix::kCreditCard); + test_cases.push_back(s); + + return test_cases; +} + +INSTANTIATE_TEST_SUITE_P(All, + AutofillTypeTraitsTestImplSectionTest, + testing::ValuesIn(SectionTestCases())); + TEST_F(AutofillTypeTraitsTestImpl, PassFormFieldData) { FormFieldData input; test::CreateTestSelectField("TestLabel", "TestName", "TestValue", kOptions, @@ -264,6 +327,10 @@ TEST_F(AutofillTypeTraitsTestImpl, PassFormFieldData) { input.properties_mask = FieldPropertiesFlags::kHadFocus; input.user_input = u"TestTypedValue"; input.bounds = gfx::RectF(1, 2, 10, 100); + base::flat_map<LocalFrameToken, size_t> frame_token_ids; + input.section.SetPrefixFromAutocomplete( + {.section = "autocomplete_section", + .mode = HtmlFieldMode::HTML_MODE_SHIPPING}); EXPECT_FALSE(input.host_frame.is_empty()); base::RunLoop loop; diff --git a/chromium/components/autofill/core/common/mojom/test_autofill_types.mojom b/chromium/components/autofill/core/common/mojom/test_autofill_types.mojom index e73f044924f..3b0b7c51c20 100644 --- a/chromium/components/autofill/core/common/mojom/test_autofill_types.mojom +++ b/chromium/components/autofill/core/common/mojom/test_autofill_types.mojom @@ -7,6 +7,7 @@ module autofill.mojom; import "components/autofill/core/common/mojom/autofill_types.mojom"; interface TypeTraitsTest { + PassSection(Section s) => (Section passed); PassFormData(FormData s) => (FormData passed); PassFormFieldData(FormFieldData s) => (FormFieldData passed); PassFormDataPredictions(FormDataPredictions s) => diff --git a/chromium/components/autofill/ios/browser/autofill_agent.mm b/chromium/components/autofill/ios/browser/autofill_agent.mm index c195f976b35..aeb0f6c8a3c 100644 --- a/chromium/components/autofill/ios/browser/autofill_agent.mm +++ b/chromium/components/autofill/ios/browser/autofill_agent.mm @@ -90,9 +90,9 @@ typedef void (^FetchFormsCompletionHandler)(BOOL, const FormDataVector&); // modifies the field's value for the select elements. void GetFormField(autofill::FormFieldData* field, const autofill::FormData& form, - const std::u16string& fieldIdentifier) { + FieldRendererId fieldIdentifier) { for (const auto& currentField : form.fields) { - if (currentField.unique_id == fieldIdentifier && + if (currentField.unique_renderer_id == fieldIdentifier && currentField.is_focusable) { *field = currentField; break; @@ -151,9 +151,9 @@ void GetFormField(autofill::FormFieldData* field, base::WeakPtr<autofill::AutofillPopupDelegate> _popupDelegate; // The autofill data that needs to be send when the |webState_| is shown. - // The pair contains the frame ID and the base::Value to send. - // If the value is nullptr, no data needs to be sent. - std::pair<std::string, std::unique_ptr<base::Value>> _pendingFormData; + // The pair contains the frame ID and the base::Value::Dict to send. + // If the value is nullopt, no data needs to be sent. + absl::optional<std::pair<std::string, base::Value::Dict>> _pendingFormData; // Bridge to listen to pref changes. std::unique_ptr<PrefObserverBridge> _prefObserverBridge; @@ -308,7 +308,7 @@ void GetFormField(autofill::FormFieldData* field, // Sends a request to BrowserAutofillManager to retrieve suggestions for the // specified form and field. - (void)queryAutofillForForm:(const autofill::FormData&)form - fieldIdentifier:(NSString*)fieldIdentifier + fieldIdentifier:(FieldRendererId)fieldIdentifier type:(NSString*)type typedValue:(NSString*)typedValue frameID:(NSString*)frameID @@ -323,7 +323,7 @@ void GetFormField(autofill::FormFieldData* field, // Find the right field. autofill::FormFieldData field; - GetFormField(&field, form, SysNSStringToUTF16(fieldIdentifier)); + GetFormField(&field, form, fieldIdentifier); // Save the completion and go look for suggestions. _suggestionsAvailableCompletion = [completion copy]; @@ -331,8 +331,8 @@ void GetFormField(autofill::FormFieldData* field, // Query the BrowserAutofillManager for suggestions. Results will arrive in // -showAutofillPopup:popupDelegate:. - autofillManager->OnAskForValuesToFill(++_lastQueryID, form, field, - gfx::RectF(), + autofillManager->OnAskForValuesToFill(form, field, gfx::RectF(), + ++_lastQueryID, /*autoselect_first_suggestion=*/false, autofill::TouchToFillEligible(false)); } @@ -370,7 +370,7 @@ void GetFormField(autofill::FormFieldData* field, id completionHandler = ^(BOOL success, const FormDataVector& forms) { if (success && forms.size() == 1) { [weakSelf queryAutofillForForm:forms[0] - fieldIdentifier:formQuery.fieldIdentifier + fieldIdentifier:formQuery.uniqueFieldID type:formQuery.type typedValue:formQuery.typedValue frameID:formQuery.frameID @@ -486,26 +486,25 @@ void GetFormField(autofill::FormFieldData* field, - (void)fillFormData:(const autofill::FormData&)form inFrame:(web::WebFrame*)frame { - auto autofillData = - std::make_unique<base::Value>(base::Value::Type::DICTIONARY); - autofillData->SetKey("formName", base::Value(base::UTF16ToUTF8(form.name))); - autofillData->SetKey( + base::Value::Dict autofillData; + autofillData.Set("formName", base::Value(base::UTF16ToUTF8(form.name))); + autofillData.Set( "formRendererID", base::Value(static_cast<int>(form.unique_renderer_id.value()))); - base::Value fieldsData(base::Value::Type::DICTIONARY); + base::Value::Dict fieldsData; for (const auto& field : form.fields) { // Skip empty fields and those that are not autofilled. if (field.value.empty() || !field.is_autofilled) continue; - base::Value fieldData(base::Value::Type::DICTIONARY); - fieldData.SetKey("value", base::Value(field.value)); - fieldData.SetKey("section", base::Value(field.section)); - fieldsData.SetKey(NumberToString(field.unique_renderer_id.value()), - std::move(fieldData)); + base::Value::Dict fieldData; + fieldData.Set("value", field.value); + fieldData.Set("section", field.section.ToString()); + fieldsData.Set(NumberToString(field.unique_renderer_id.value()), + std::move(fieldData)); } - autofillData->SetKey("fields", std::move(fieldsData)); + autofillData.Set("fields", std::move(fieldsData)); // Store the form data when WebState is not visible, to send it as soon as it // becomes visible again, e.g., when the CVC unmask prompt is showing. @@ -536,17 +535,16 @@ void GetFormField(autofill::FormFieldData* field, return; } - auto predictionData = - std::make_unique<base::Value>(base::Value::Type::DICTIONARY); + base::Value::Dict predictionData; for (const auto& form : forms) { - base::Value fieldData(base::Value::Type::DICTIONARY); + base::Value::Dict fieldData; DCHECK(form.fields.size() == form.data.fields.size()); for (size_t i = 0; i < form.fields.size(); i++) { - fieldData.SetKey(base::UTF16ToUTF8(form.data.fields[i].unique_id), - base::Value(form.fields[i].overall_type)); + fieldData.Set( + NumberToString(form.data.fields[i].unique_renderer_id.value()), + base::Value(form.fields[i].overall_type)); } - predictionData->SetKey(base::UTF16ToUTF8(form.data.name), - std::move(fieldData)); + predictionData.Set(base::UTF16ToUTF8(form.data.name), std::move(fieldData)); } autofill::AutofillJavaScriptFeature::GetInstance()->FillPredictionData( frame, std::move(predictionData)); @@ -631,16 +629,19 @@ void GetFormField(autofill::FormFieldData* field, - (void)webStateWasShown:(web::WebState*)webState { DCHECK_EQ(_webState, webState); - if (_pendingFormData.second) { - // The frameID cannot be empty. - DCHECK(!_pendingFormData.first.empty()); - web::WebFrame* frame = nullptr; - if (!_pendingFormData.first.empty()) { - frame = web::GetWebFrameWithId(_webState, _pendingFormData.first); - } - [self sendData:std::move(_pendingFormData.second) toFrame:frame]; + if (!_pendingFormData.has_value()) { + return; } - _pendingFormData = std::make_pair("", nullptr); + + std::pair<std::string, base::Value::Dict> pendingFormData = + std::move(_pendingFormData).value(); + _pendingFormData = absl::nullopt; + + // The frameID cannot be empty. + DCHECK(!pendingFormData.first.empty()); + web::WebFrame* frame = + web::GetWebFrameWithId(_webState, pendingFormData.first); + [self sendData:std::move(pendingFormData.second) toFrame:frame]; } - (void)webState:(web::WebState*)webState @@ -814,7 +815,7 @@ void GetFormField(autofill::FormFieldData* field, // -onFormsFetched:formsData:webFrameId:fieldIdentifier. __weak AutofillAgent* weakSelf = self; __block const std::string webFrameId = frame->GetFrameId(); - __block const std::string fieldIdentifier = params.field_identifier; + __block FieldRendererId fieldIdentifier = params.unique_field_id; auto completionHandler = ^(BOOL success, const FormDataVector& forms) { [weakSelf onFormsFetched:success formsData:forms @@ -905,11 +906,11 @@ void GetFormField(autofill::FormFieldData* field, formName:(const std::string&)formName value:(const std::u16string)value inFrame:(web::WebFrame*)frame { - auto data = std::make_unique<base::DictionaryValue>(); - data->SetInteger("unique_renderer_id", uniqueFieldID.value()); - data->SetString("identifier", fieldIdentifier); - data->SetString("form", formName); - data->SetString("value", value); + base::Value::Dict data; + data.Set("unique_renderer_id", static_cast<int>(uniqueFieldID.value())); + data.Set("identifier", fieldIdentifier); + data.Set("form", formName); + data.Set("value", value); DCHECK(_suggestionHandledCompletion); __weak AutofillAgent* weakSelf = self; @@ -948,8 +949,7 @@ void GetFormField(autofill::FormFieldData* field, } // Sends the the |data| to |frame| to actually fill the data. -- (void)sendData:(std::unique_ptr<base::Value>)data - toFrame:(web::WebFrame*)frame { +- (void)sendData:(base::Value::Dict)data toFrame:(web::WebFrame*)frame { DCHECK(_webState->IsVisible()); __weak AutofillAgent* weakSelf = self; SuggestionHandledCompletion suggestionHandledCompletionCopy = @@ -976,7 +976,7 @@ void GetFormField(autofill::FormFieldData* field, - (void)onFormsFetched:(BOOL)success formsData:(const FormDataVector&)forms webFrameId:(const std::string&)webFrameId - fieldIdentifier:(const std::string&)fieldIdentifier { + fieldIdentifier:(FieldRendererId)fieldIdentifier { if (!success || forms.size() != 1) return; @@ -995,7 +995,7 @@ void GetFormField(autofill::FormFieldData* field, return; autofill::FormFieldData field; - GetFormField(&field, forms[0], base::UTF8ToUTF16(fieldIdentifier)); + GetFormField(&field, forms[0], fieldIdentifier); autofillManager->OnTextFieldDidChange( forms[0], field, gfx::RectF(), autofill::AutofillTickClock::NowTicks()); } diff --git a/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm b/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm index 049f7f025b3..b42ea2c8071 100644 --- a/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm +++ b/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm @@ -143,7 +143,6 @@ TEST_F(AutofillAgentTests, field.name = u"number"; field.name_attribute = field.name; field.id_attribute = u"number"; - field.unique_id = field.id_attribute; field.value = u"number_value"; field.is_autofilled = true; field.unique_renderer_id = FieldRendererId(2); @@ -152,7 +151,6 @@ TEST_F(AutofillAgentTests, field.name = u"name"; field.name_attribute = field.name; field.id_attribute = u"name"; - field.unique_id = field.id_attribute; field.value = u"name_value"; field.is_autofilled = true; field.unique_renderer_id = FieldRendererId(3); @@ -161,7 +159,6 @@ TEST_F(AutofillAgentTests, field.name = u"expiry_month"; field.name_attribute = field.name; field.id_attribute = u"expiry_month"; - field.unique_id = field.id_attribute; field.value = u"01"; field.is_autofilled = false; field.unique_renderer_id = FieldRendererId(4); @@ -170,7 +167,6 @@ TEST_F(AutofillAgentTests, field.name = u"unknown"; field.name_attribute = field.name; field.id_attribute = u"unknown"; - field.unique_id = field.id_attribute; field.value = u""; field.is_autofilled = true; field.unique_renderer_id = FieldRendererId(5); @@ -179,10 +175,10 @@ TEST_F(AutofillAgentTests, fillFormData:form inFrame:fake_web_state_.GetWebFramesManager()->GetMainWebFrame()]; fake_web_state_.WasShown(); - EXPECT_EQ(u"__gCrWeb.autofill.fillForm({\"fields\":{\"2\":{\"section\":\"\"," - "\"value\":\"number_value\"}," - "\"3\":{\"section\":\"\",\"value\":\"name_value\"}}," - "\"formName\":\"CC form\",\"formRendererID\":1}, 0);", + EXPECT_EQ(u"__gCrWeb.autofill.fillForm({\"fields\":{\"2\":{\"section\":\"-" + u"default\",\"value\":\"number_value\"},\"3\":{\"section\":\"-" + u"default\",\"value\":\"name_value\"}},\"formName\":\"CC " + u"form\",\"formRendererID\":1}, 0);", fake_main_frame_->GetLastJavaScriptCall()); } diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.h b/chromium/components/autofill/ios/browser/autofill_driver_ios.h index 1b183a73150..3e1f25142a1 100644 --- a/chromium/components/autofill/ios/browser/autofill_driver_ios.h +++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.h @@ -41,6 +41,7 @@ class AutofillDriverIOS : public AutofillDriver { // AutofillDriver: bool IsIncognito() const override; + bool IsInActiveFrame() const override; bool IsInAnyMainFrame() const override; bool IsPrerendering() const override; bool CanShowAutofillUi() const override; @@ -54,7 +55,7 @@ class AutofillDriverIOS : public AutofillDriver { const url::Origin& triggered_origin, const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map) override; - void HandleParsedForms(const std::vector<const FormData*>& forms) override; + void HandleParsedForms(const std::vector<FormData>& forms) override; void SendAutofillTypePredictionsToRenderer( const std::vector<FormStructure*>& forms) override; void RendererShouldClearFilledSection() override; diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm index 853cc79f283..0a338d48ab5 100644 --- a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm +++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm @@ -67,6 +67,11 @@ bool AutofillDriverIOS::IsIncognito() const { return web_state_->GetBrowserState()->IsOffTheRecord(); } +// Return true as iOS has no MPArch. +bool AutofillDriverIOS::IsInActiveFrame() const { + return true; +} + bool AutofillDriverIOS::IsInAnyMainFrame() const { web::WebFrame* web_frame = web::GetWebFrameWithId(web_state_, web_frame_id_); return web_frame ? web_frame->IsMainFrame() : true; @@ -111,14 +116,13 @@ std::vector<FieldGlobalId> AutofillDriverIOS::FillOrPreviewForm( return safe_fields; } -void AutofillDriverIOS::HandleParsedForms( - const std::vector<const FormData*>& forms) { +void AutofillDriverIOS::HandleParsedForms(const std::vector<FormData>& forms) { const std::map<FormGlobalId, std::unique_ptr<FormStructure>>& map = browser_autofill_manager_.form_structures(); std::vector<FormStructure*> form_structures; form_structures.reserve(forms.size()); - for (const FormData* form : forms) { - auto it = map.find(form->global_id()); + for (const FormData& form : forms) { + auto it = map.find(form.global_id()); if (it != map.end()) form_structures.push_back(it->second.get()); } diff --git a/chromium/components/autofill/ios/browser/autofill_java_script_feature.h b/chromium/components/autofill/ios/browser/autofill_java_script_feature.h index c981f3f6ac2..af7192a79c7 100644 --- a/chromium/components/autofill/ios/browser/autofill_java_script_feature.h +++ b/chromium/components/autofill/ios/browser/autofill_java_script_feature.h @@ -7,8 +7,6 @@ #import <Foundation/Foundation.h> -#include <memory> - #include "base/callback.h" #include "base/no_destructor.h" #include "base/values.h" @@ -32,49 +30,48 @@ class AutofillJavaScriptFeature : public web::JavaScriptFeature { // Adds a delay between filling the form fields in frame. void AddJSDelayInFrame(web::WebFrame* frame); - // Extracts forms from a web |frame|. Only forms with at least - // |requiredFieldsCount| fields are extracted. |callback| is called with the - // JSON string of forms of a web page. |callback| cannot be null. + // Extracts forms from a web `frame`. Only forms with at least + // `required_fields_count` fields are extracted. `callback` is called + // with the JSON string of forms of a web page. `callback` cannot be nil. void FetchForms(web::WebFrame* frame, - NSUInteger requiredFieldsCount, + NSUInteger required_fields_count, base::OnceCallback<void(NSString*)> callback); - // Fills the data in JSON string |dataString| into the active form field in - // |frame|, then executes the |completionHandler|. + // Fills `data` into the active form field in `frame`, then executes the + // `callback`. `callback` cannot be nil. void FillActiveFormField(web::WebFrame* frame, - std::unique_ptr<base::DictionaryValue> data, + base::Value::Dict data, base::OnceCallback<void(BOOL)> callback); // Fills a number of fields in the same named form for full-form Autofill. // Applies Autofill CSS (i.e. yellow background) to filled elements. // Only empty fields will be filled, except that field named - // Field identified by |forceFillFieldID| will always be filled even if - // non-empty. |forceFillFieldID| may be null. Fields must be contained in - // |frame|. |completionHandler| is called after the forms are filled with the - // JSON string containing pairs of unique renderer ids of filled fields and - // corresponding filled values. |completionHandler| cannot be nil. + // Field identified by `force_fill_field_id` will always be filled even if + // non-empty. `force_fill_field_id` may be null. Fields must be contained in + // `frame`. `callback` is called after the forms are filled with `data` + // which must contain pairs of unique renderer ids of filled fields and + // corresponding filled values. `callback` cannot be nil. void FillForm(web::WebFrame* frame, - std::unique_ptr<base::Value> data, - autofill::FieldRendererId forceFillFieldID, + base::Value::Dict data, + autofill::FieldRendererId force_fill_field_id, base::OnceCallback<void(NSString*)> callback); // Clear autofilled fields of the specified form and frame. Fields that are // not currently autofilled are not modified. Field contents are cleared, and // Autofill flag and styling are removed. 'change' events are sent for fields // whose contents changed. - // |fieldUniqueID| identifies the field that initiated the - // clear action. |completionHandler| is called after the forms are filled with - // the JSON string containing a list of unique renderer ids of cleared fields. - // |completionHandler| cannot be nil. + // `form_renderer_id` and `field_renderer_id` identify the field that + // initiated the clear action. `callback is called after the forms are filled + // with the JSON string containing a list of unique renderer ids of cleared + // fields. `callback` cannot be nil. void ClearAutofilledFieldsForForm( web::WebFrame* frame, - autofill::FormRendererId formRendererID, - autofill::FieldRendererId fieldRendererID, + autofill::FormRendererId form_renderer_id, + autofill::FieldRendererId field_renderer_id, base::OnceCallback<void(NSString*)> callback); // Marks up the form with autofill field prediction data (diagnostic tool). - void FillPredictionData(web::WebFrame* frame, - std::unique_ptr<base::Value> data); + void FillPredictionData(web::WebFrame* frame, base::Value::Dict data); private: friend class base::NoDestructor<AutofillJavaScriptFeature>; diff --git a/chromium/components/autofill/ios/browser/autofill_java_script_feature.mm b/chromium/components/autofill/ios/browser/autofill_java_script_feature.mm index 2663fe1be69..63d9cd4b4e9 100644 --- a/chromium/components/autofill/ios/browser/autofill_java_script_feature.mm +++ b/chromium/components/autofill/ios/browser/autofill_java_script_feature.mm @@ -89,11 +89,10 @@ void AutofillJavaScriptFeature::FetchForms( void AutofillJavaScriptFeature::FillActiveFormField( web::WebFrame* frame, - std::unique_ptr<base::DictionaryValue> data, + base::Value::Dict data, base::OnceCallback<void(BOOL)> callback) { - DCHECK(data); std::vector<base::Value> parameters; - parameters.push_back(std::move(*data)); + parameters.push_back(base::Value(std::move(data))); CallJavaScriptFunction(frame, "autofill.fillActiveFormField", parameters, autofill::CreateBoolCallback(std::move(callback)), base::Seconds(kJavaScriptExecutionTimeoutInSeconds)); @@ -101,16 +100,15 @@ void AutofillJavaScriptFeature::FillActiveFormField( void AutofillJavaScriptFeature::FillForm( web::WebFrame* frame, - std::unique_ptr<base::Value> data, - autofill::FieldRendererId force_fill_field_unique_id, + base::Value::Dict data, + autofill::FieldRendererId force_fill_field_id, base::OnceCallback<void(NSString*)> callback) { - DCHECK(data); DCHECK(!callback.is_null()); std::vector<base::Value> parameters; - parameters.push_back(std::move(*data)); + parameters.push_back(base::Value(std::move(data))); parameters.push_back( - base::Value(static_cast<int>(force_fill_field_unique_id.value()))); + base::Value(static_cast<int>(force_fill_field_id.value()))); CallJavaScriptFunction(frame, "autofill.fillForm", parameters, autofill::CreateStringCallback(std::move(callback)), base::Seconds(kJavaScriptExecutionTimeoutInSeconds)); @@ -132,12 +130,10 @@ void AutofillJavaScriptFeature::ClearAutofilledFieldsForForm( base::Seconds(kJavaScriptExecutionTimeoutInSeconds)); } -void AutofillJavaScriptFeature::FillPredictionData( - web::WebFrame* frame, - std::unique_ptr<base::Value> data) { - DCHECK(data); +void AutofillJavaScriptFeature::FillPredictionData(web::WebFrame* frame, + base::Value::Dict data) { std::vector<base::Value> parameters; - parameters.push_back(std::move(*data)); + parameters.push_back(base::Value(std::move(data))); CallJavaScriptFunction(frame, "autofill.fillPredictionData", parameters); } diff --git a/chromium/components/autofill/ios/browser/autofill_util.h b/chromium/components/autofill/ios/browser/autofill_util.h index d4aa8f77fa8..c65ac0479e5 100644 --- a/chromium/components/autofill/ios/browser/autofill_util.h +++ b/chromium/components/autofill/ios/browser/autofill_util.h @@ -7,13 +7,12 @@ #include <vector> +#include "base/values.h" + #import "ios/web/public/js_messaging/web_frame.h" class GURL; -namespace base { -class DictionaryValue; -} namespace web { class WebState; } @@ -59,7 +58,7 @@ bool ExtractFormData(const base::Value& form, // Extracts a single form field from the JSON dictionary into a FormFieldData // object. // Returns false if the field could not be extracted. -bool ExtractFormFieldData(const base::DictionaryValue& field, +bool ExtractFormFieldData(const base::Value::Dict& field, FormFieldData* field_data); typedef base::OnceCallback<void(const base::Value*)> JavaScriptResultCallback; diff --git a/chromium/components/autofill/ios/browser/autofill_util.mm b/chromium/components/autofill/ios/browser/autofill_util.mm index 9cde0ac21f7..3705d11269b 100644 --- a/chromium/components/autofill/ios/browser/autofill_util.mm +++ b/chromium/components/autofill/ios/browser/autofill_util.mm @@ -118,20 +118,24 @@ bool ExtractFormData(const base::Value& form_value, autofill::FormData* form_data) { DCHECK(form_data); // Each form should be a JSON dictionary. - const base::DictionaryValue* form_dictionary = nullptr; - if (!form_value.GetAsDictionary(&form_dictionary)) + if (!form_value.is_dict()) return false; + const base::Value::Dict& form_dictionary = form_value.GetDict(); + // Form data is copied into a FormData object field-by-field. - if (!form_dictionary->GetString("name", &form_data->name)) + const std::string* name = form_dictionary.FindString("name"); + if (!name) return false; + form_data->name = base::UTF8ToUTF16(*name); if (filtered && form_name != form_data->name) return false; // Origin is mandatory. - std::u16string origin; - if (!form_dictionary->GetString("origin", &origin)) + const std::string* origin_ptr = form_dictionary.FindString("origin"); + if (!origin_ptr) return false; + std::u16string origin = base::UTF8ToUTF16(*origin_ptr); // Use GURL object to verify origin of host frame URL. form_data->url = GURL(origin); @@ -141,35 +145,44 @@ bool ExtractFormData(const base::Value& form_value, // main_frame_origin is used for logging UKM. form_data->main_frame_origin = url::Origin::Create(main_frame_url); - std::string unique_renderer_id; - form_dictionary->GetString("unique_renderer_id", &unique_renderer_id); - if (!unique_renderer_id.empty()) { - StringToUint(unique_renderer_id, &form_data->unique_renderer_id.value()); + const std::string* unique_renderer_id = + form_dictionary.FindString("unique_renderer_id"); + if (unique_renderer_id && !unique_renderer_id->empty()) { + StringToUint(*unique_renderer_id, &form_data->unique_renderer_id.value()); } else { form_data->unique_renderer_id = FormRendererId(); } // Action is optional. std::u16string action; - form_dictionary->GetString("action", &action); + if (const std::string* action_ptr = form_dictionary.FindString("action")) { + action = base::UTF8ToUTF16(*action_ptr); + } form_data->action = GURL(action); // Optional fields. - form_dictionary->GetString("name_attribute", &form_data->name_attribute); - form_dictionary->GetString("id_attribute", &form_data->id_attribute); - form_data->is_form_tag = form_dictionary->FindBoolKey("is_form_tag") - .value_or(form_data->is_form_tag); - form_dictionary->GetString("frame_id", &form_data->frame_id); + if (const std::string* name_attribute = + form_dictionary.FindString("name_attribute")) { + form_data->name_attribute = base::UTF8ToUTF16(*name_attribute); + } + if (const std::string* id_attribute = + form_dictionary.FindString("id_attribute")) { + form_data->id_attribute = base::UTF8ToUTF16(*id_attribute); + } + form_data->is_form_tag = + form_dictionary.FindBool("is_form_tag").value_or(form_data->is_form_tag); + if (const std::string* frame_id = form_dictionary.FindString("frame_id")) { + form_data->frame_id = *frame_id; + } // Field list (mandatory) is extracted. - const base::ListValue* fields_list = nullptr; - if (!form_dictionary->GetList("fields", &fields_list)) + const base::Value::List* fields_list = form_dictionary.FindList("fields"); + if (!fields_list) return false; - for (const auto& field_dict : fields_list->GetListDeprecated()) { - const base::DictionaryValue* field; + for (const auto& field_dict : *fields_list) { autofill::FormFieldData field_data; - if (field_dict.GetAsDictionary(&field) && - ExtractFormFieldData(*field, &field_data)) { + if (field_dict.is_dict() && + ExtractFormFieldData(field_dict.GetDict(), &field_data)) { form_data->fields.push_back(std::move(field_data)); } else { return false; @@ -178,68 +191,80 @@ bool ExtractFormData(const base::Value& form_value, return true; } -bool ExtractFormFieldData(const base::DictionaryValue& field, +bool ExtractFormFieldData(const base::Value::Dict& field, autofill::FormFieldData* field_data) { - if (!field.GetString("name", &field_data->name) || - !field.GetString("identifier", &field_data->unique_id) || - !field.GetString("form_control_type", &field_data->form_control_type)) { + const std::string* name; + const std::string* form_control_type; + if (!(name = field.FindString("name")) || + !(form_control_type = field.FindString("form_control_type"))) { return false; } - std::string unique_renderer_id; - field.GetString("unique_renderer_id", &unique_renderer_id); - if (!unique_renderer_id.empty()) { - StringToUint(unique_renderer_id, &field_data->unique_renderer_id.value()); + field_data->name = base::UTF8ToUTF16(*name); + field_data->form_control_type = *form_control_type; + + const std::string* unique_renderer_id = + field.FindString("unique_renderer_id"); + if (unique_renderer_id && !unique_renderer_id->empty()) { + StringToUint(*unique_renderer_id, &field_data->unique_renderer_id.value()); } else { field_data->unique_renderer_id = FieldRendererId(); } // Optional fields. - field.GetString("name_attribute", &field_data->name_attribute); - field.GetString("id_attribute", &field_data->id_attribute); - field.GetString("label", &field_data->label); - field.GetString("value", &field_data->value); - field.GetString("autocomplete_attribute", - &field_data->autocomplete_attribute); + if (const std::string* name_attribute = field.FindString("name_attribute")) { + field_data->name_attribute = base::UTF8ToUTF16(*name_attribute); + } + if (const std::string* id_attribute = field.FindString("id_attribute")) { + field_data->id_attribute = base::UTF8ToUTF16(*id_attribute); + } + if (const std::string* label = field.FindString("label")) { + field_data->label = base::UTF8ToUTF16(*label); + } + if (const std::string* value = field.FindString("value")) { + field_data->value = base::UTF8ToUTF16(*value); + } + if (const std::string* autocomplete_attribute = + field.FindString("autocomplete_attribute")) { + field_data->autocomplete_attribute = *autocomplete_attribute; + } field_data->is_autofilled = - field.FindBoolKey("is_autofilled").value_or(field_data->is_autofilled); + field.FindBool("is_autofilled").value_or(field_data->is_autofilled); - int max_length = 0; - if (field.GetInteger("max_length", &max_length)) - field_data->max_length = max_length; + if (absl::optional<int> max_length = field.FindInt("max_length")) { + field_data->max_length = *max_length; + } // TODO(crbug.com/427614): Extract |is_checked|. - bool is_checkable = field.FindBoolKey("is_checkable").value_or(false); + bool is_checkable = field.FindBool("is_checkable").value_or(false); autofill::SetCheckStatus(field_data, is_checkable, false); field_data->is_focusable = - field.FindBoolKey("is_focusable").value_or(field_data->is_focusable); + field.FindBool("is_focusable").value_or(field_data->is_focusable); field_data->should_autocomplete = - field.FindBoolKey("should_autocomplete") + field.FindBool("should_autocomplete") .value_or(field_data->should_autocomplete); // RoleAttribute::kOther is the default value. The only other value as of this // writing is RoleAttribute::kPresentation. - int role = 0; - if (field.GetInteger("role", &role) && - role == static_cast<int>(FormFieldData::RoleAttribute::kPresentation)) { + absl::optional<int> role = field.FindInt("role"); + if (role && + *role == static_cast<int>(FormFieldData::RoleAttribute::kPresentation)) { field_data->role = FormFieldData::RoleAttribute::kPresentation; } // TODO(crbug.com/427614): Extract |text_direction|. // Load option values where present. - const base::ListValue* option_values; - const base::ListValue* option_contents; - if (field.GetList("option_values", &option_values) && - field.GetList("option_contents", &option_contents)) { - auto value_list = option_values->GetListDeprecated(); - auto content_list = option_contents->GetListDeprecated(); - if (value_list.size() != content_list.size()) + const base::Value::List* option_values = field.FindList("option_values"); + const base::Value::List* option_contents = field.FindList("option_contents"); + if (option_values && option_contents) { + if (option_values->size() != option_contents->size()) return false; - auto value_it = value_list.begin(); - auto content_it = content_list.begin(); - while (value_it != value_list.end() && content_it != content_list.end()) { + auto value_it = option_values->begin(); + auto content_it = option_contents->begin(); + while (value_it != option_values->end() && + content_it != option_contents->end()) { if (value_it->is_string() && content_it->is_string()) { field_data->options.push_back( {.value = base::UTF8ToUTF16(value_it->GetString()), diff --git a/chromium/components/autofill/ios/browser/resources/autofill_controller.js b/chromium/components/autofill/ios/browser/resources/autofill_controller.js index efe852b1b15..24e47206d48 100644 --- a/chromium/components/autofill/ios/browser/resources/autofill_controller.js +++ b/chromium/components/autofill/ios/browser/resources/autofill_controller.js @@ -556,8 +556,8 @@ __gCrWeb.autofill['fillPredictionData'] = function(data) { if (!__gCrWeb.fill.isAutofillableElement(element)) { continue; } - const elementName = __gCrWeb.form.getFieldIdentifier(element); - const value = formData[elementName]; + const elementID = __gCrWeb.fill.getUniqueID(element); + const value = formData[elementID]; if (value) { element.placeholder = value; } diff --git a/chromium/components/autofill/ios/browser/resources/suggestion_controller.js b/chromium/components/autofill/ios/browser/resources/suggestion_controller.js index de02dcd6b44..0a0cd66a478 100644 --- a/chromium/components/autofill/ios/browser/resources/suggestion_controller.js +++ b/chromium/components/autofill/ios/browser/resources/suggestion_controller.js @@ -378,7 +378,7 @@ __gCrWeb.suggestion['hasPreviousElement'] = function(formName, fieldName) { __gCrWeb.suggestion['hasPreviousNextElements'] = function(formName, fieldName) { return [ __gCrWeb.suggestion.hasPreviousElement(formName, fieldName), - __gCrWeb.suggestion.hasNextElement(formName, fieldName) + __gCrWeb.suggestion.hasNextElement(formName, fieldName), ].toString(); }; diff --git a/chromium/components/autofill/ios/form_util/resources/fill.js b/chromium/components/autofill/ios/form_util/resources/fill.js index 5c0b8f96188..6fa6aea5404 100644 --- a/chromium/components/autofill/ios/form_util/resources/fill.js +++ b/chromium/components/autofill/ios/form_util/resources/fill.js @@ -219,7 +219,7 @@ function setInputElementAngularValue_(value, input) { function(parse) { const setter = parse(angularModel); setter.assign(angularScope, value); - } + }, ]); } @@ -327,7 +327,7 @@ function setInputElementValue_(value, input) { // property. return value + ''; }, - configurable: true + configurable: true, }; if (oldPropertyDescriptor.set) { newProperty.set = function(e) { @@ -1025,9 +1025,9 @@ __gCrWeb.fill.findChildTextInner = function(node, depth, divsToSkip) { } // Ignore elements known not to contain inferable labels. + let skipNode = false; if (node.nodeType === Node.ELEMENT_NODE) { - if (node.tagName === 'OPTION' || node.tagName === 'SCRIPT' || - node.tagName === 'NOSCRIPT') { + if (node.tagName === 'OPTION') { return ''; } if (__gCrWeb.form.isFormControlElement(/** @type {Element} */ (node))) { @@ -1036,6 +1036,7 @@ __gCrWeb.fill.findChildTextInner = function(node, depth, divsToSkip) { return ''; } } + skipNode = node.tagName === 'SCRIPT' || node.tagName === 'NOSCRIPT'; } if (node.tagName === 'DIV') { @@ -1047,30 +1048,33 @@ __gCrWeb.fill.findChildTextInner = function(node, depth, divsToSkip) { } // Extract the text exactly at this node. - let nodeText = __gCrWeb.fill.nodeValue(node); - if (node.nodeType === Node.TEXT_NODE && !nodeText) { - // In the C++ version, this text node would have been stripped completely. - // Just pass the buck. - return __gCrWeb.fill.findChildTextInner( - node.nextSibling, depth, divsToSkip); - } + let nodeText = ''; + if (!skipNode) { + nodeText = __gCrWeb.fill.nodeValue(node); + if (node.nodeType === Node.TEXT_NODE && !nodeText) { + // In the C++ version, this text node would have been stripped completely. + // Just pass the buck. + return __gCrWeb.fill.findChildTextInner( + node.nextSibling, depth, divsToSkip); + } - // Recursively compute the children's text. - // Preserve inter-element whitespace separation. - const childText = - __gCrWeb.fill.findChildTextInner(node.firstChild, depth - 1, divsToSkip); - let addSpace = node.nodeType === Node.TEXT_NODE && !nodeText; - // Emulate apparently incorrect Chromium behavior tracked in - // https://crbug.com/239819. - addSpace = false; - nodeText = - __gCrWeb.fill.combineAndCollapseWhitespace(nodeText, childText, addSpace); + // Recursively compute the children's text. + // Preserve inter-element whitespace separation. + const childText = __gCrWeb.fill.findChildTextInner( + node.firstChild, depth - 1, divsToSkip); + let addSpace = node.nodeType === Node.TEXT_NODE && !nodeText; + // Emulate apparently incorrect Chromium behavior tracked in + // https://crbug.com/239819. + addSpace = false; + nodeText = __gCrWeb.fill.combineAndCollapseWhitespace( + nodeText, childText, addSpace); + } // Recursively compute the siblings' text. // Again, preserve inter-element whitespace separation. const siblingText = __gCrWeb.fill.findChildTextInner(node.nextSibling, depth - 1, divsToSkip); - addSpace = node.nodeType === Node.TEXT_NODE && !nodeText; + let addSpace = node.nodeType === Node.TEXT_NODE && !nodeText; // Emulate apparently incorrect Chromium behavior tracked in // https://crbug.com/239819. addSpace = false; @@ -1174,8 +1178,13 @@ __gCrWeb.fill.inferLabelFromSibling = function(element, forward) { const value = __gCrWeb.fill.findChildText(sibling); // A text node's value will be empty if it is for a line break. const addSpace = nodeType === Node.TEXT_NODE && value.length === 0; - inferredLabel = __gCrWeb.fill.combineAndCollapseWhitespace( - value, inferredLabel, addSpace); + if (forward) { + inferredLabel = __gCrWeb.fill.combineAndCollapseWhitespace( + inferredLabel, value, addSpace); + } else { + inferredLabel = __gCrWeb.fill.combineAndCollapseWhitespace( + value, inferredLabel, addSpace); + } continue; } @@ -1575,8 +1584,14 @@ __gCrWeb.fill.inferLabelFromEnclosingLabel = function(element) { * e.g. <div>Some Text<span><input ...></span></div> * e.g. <div>Some Text</div><div><input ...></div> * - * Because this is already traversing the <div> structure, if it finds a <label> - * sibling along the way, infer from that <label>. + * Contrary to the other InferLabelFrom* functions, this functions walks up + * the DOM tree from the original input, instead of down from the surrounding + * tag. While doing so, if a <label> or text node sibling are found along the + * way, a label is inferred from them directly. For example, <div>First + * name<div><input></div>Last name<div><input></div></div> infers "First name" + * and "Last name" for the two inputs, respectively, by picking up the text + * nodes on the way to the surrounding div. Without doing so, the label of both + * inputs becomes "First nameLast name". * * It is based on the logic in * string16 InferLabelFromDivTable(const WebFormControlElement& element) @@ -1623,12 +1638,14 @@ __gCrWeb.fill.inferLabelFromDivTable = function(element) { } lookingForParent = false; - } else if (!lookingForParent && __gCrWeb.fill.hasTagName(node, 'label')) { - if (!node.control) { + } else if (!lookingForParent) { + // Infer a label from text nodes and unassigned <label> siblings. + if (__gCrWeb.fill.hasTagName(node, 'label') && !node.control) { inferredLabel = __gCrWeb.fill.findChildText(node); + } else if (node.nodeType === Node.TEXT_NODE) { + inferredLabel = __gCrWeb.fill.nodeValue(node).trim(); } - } else if ( - lookingForParent && __gCrWeb.fill.isTraversableContainerElement(node)) { + } else if (__gCrWeb.fill.isTraversableContainerElement(node)) { // If the element is in a non-div container, its label most likely is too. break; } diff --git a/chromium/components/autofill/ios/form_util/resources/form_handlers.js b/chromium/components/autofill/ios/form_util/resources/form_handlers.js index b901a5c408e..ce37ce4f0df 100644 --- a/chromium/components/autofill/ios/form_util/resources/form_handlers.js +++ b/chromium/components/autofill/ios/form_util/resources/form_handlers.js @@ -130,7 +130,7 @@ function trackPasswordField_(field) { 'fieldType': '', 'type': 'password_form_cleared', 'value': __gCrWeb.stringify(formData), - 'hasUserGesture': false + 'hasUserGesture': false, }; sendMessageOnNextLoop_(msg); } @@ -226,7 +226,7 @@ function formActivity_(evt) { 'fieldType': fieldType, 'type': type, 'value': value, - 'hasUserGesture': evt.isTrusted + 'hasUserGesture': evt.isTrusted, }; sendMessageOnNextLoop_(msg); } @@ -253,7 +253,7 @@ function formSubmitted_(form) { 'frameID': __gCrWeb.message.getFrameId(), 'formName': __gCrWeb.form.getFormIdentifier(form), 'href': getFullyQualifiedUrl_(action), - 'formData': __gCrWeb.fill.autofillSubmissionData(form) + 'formData': __gCrWeb.fill.autofillSubmissionData(form), }); } @@ -423,7 +423,7 @@ __gCrWeb.formHandlers['trackFormMutations'] = function(delay) { 'fieldType': '', 'type': 'form_changed', 'value': '', - 'hasUserGesture': false + 'hasUserGesture': false, }; return sendFormMutationMessageAfterDelay_(msg, delay); } |