From 6301ca4cdc5149d27df8982f55cd483cb02dae84 Mon Sep 17 00:00:00 2001 From: siashah Date: Fri, 7 May 2021 23:15:29 +0000 Subject: [PATCH] [VirtualCards] Store fields eligible for manual filling in form cache In order to make the decision of showing the manual fallback bottomsheet on form interaction, we need to know whether the currently focused field is eligible for manual filling. Design doc: go/vcn-chrome-client-dd Bug: 1196021 Change-Id: I3d5aabb089183b0906d1ad09ccf3a5171d9cd8c4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2868850 Commit-Queue: Siddharth Shah Reviewed-by: Jared Saul Reviewed-by: Christoph Schwering Reviewed-by: Daniel Cheng Cr-Commit-Position: refs/heads/master@{#880619} --- .../browser/content_autofill_driver.cc | 11 ++++- .../content/browser/content_autofill_driver.h | 2 + .../content_autofill_driver_unittest.cc | 3 ++ .../content/common/mojom/autofill_agent.mojom | 3 ++ .../content/renderer/autofill_agent.cc | 5 +++ .../content/renderer/autofill_agent.h | 2 + .../autofill/content/renderer/form_cache.cc | 14 ++++++ .../autofill/content/renderer/form_cache.h | 14 ++++++ .../renderer/form_cache_browsertest.cc | 37 ++++++++++++++++ .../autofill/core/browser/autofill_driver.h | 5 +++ .../autofill/core/browser/autofill_manager.cc | 15 ++++++- .../browser_autofill_manager_unittest.cc | 31 +++++++++++++ .../autofill/core/browser/form_structure.cc | 23 ++++++++++ .../autofill/core/browser/form_structure.h | 12 +++++ .../core/browser/form_structure_unittest.cc | 44 +++++++++++++++++++ .../core/browser/test_autofill_driver.cc | 3 ++ .../core/browser/test_autofill_driver.h | 2 + .../ios/browser/autofill_driver_ios.h | 2 + .../ios/browser/autofill_driver_ios.mm | 3 ++ 19 files changed, 229 insertions(+), 2 deletions(-) diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc index e5a63aa169e03..6e2021c7fc9b4 100644 --- a/components/autofill/content/browser/content_autofill_driver.cc +++ b/components/autofill/content/browser/content_autofill_driver.cc @@ -193,12 +193,21 @@ void ContentAutofillDriver::SendAutofillTypePredictionsToRenderer( const std::vector& forms) { if (!RendererIsAvailable()) return; - + // TODO(crbug.com/1185232) Send the FormDataPredictions object only if the + // debugging flag is enabled. std::vector type_predictions = FormStructure::GetFieldTypePredictions(forms); GetAutofillAgent()->FieldTypePredictionsAvailable(type_predictions); } +void ContentAutofillDriver::SendFieldsEligibleForManualFillingToRenderer( + const std::vector& fields) { + if (!RendererIsAvailable()) + return; + + GetAutofillAgent()->SetFieldsEligibleForManualFilling(fields); +} + void ContentAutofillDriver::RendererShouldAcceptDataListSuggestion( const FieldGlobalId& field, const std::u16string& value) { diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h index ca0f934d40c17..413adf8b9eae2 100644 --- a/components/autofill/content/browser/content_autofill_driver.h +++ b/components/autofill/content/browser/content_autofill_driver.h @@ -106,6 +106,8 @@ class ContentAutofillDriver : public AutofillDriver, gfx::RectF TransformBoundingBoxToViewportCoordinates( const gfx::RectF& bounding_box) override; net::IsolationInfo IsolationInfo() override; + void SendFieldsEligibleForManualFillingToRenderer( + const std::vector& fields) override; // mojom::AutofillDriver: void SetFormToBeProbablySubmitted( diff --git a/components/autofill/content/browser/content_autofill_driver_unittest.cc b/components/autofill/content/browser/content_autofill_driver_unittest.cc index ddeafa7552e0f..080e9d9d3a15e 100644 --- a/components/autofill/content/browser/content_autofill_driver_unittest.cc +++ b/components/autofill/content/browser/content_autofill_driver_unittest.cc @@ -235,6 +235,9 @@ class FakeAutofillAgent : public mojom::AutofillAgent { void SetAssistantActionState(bool running) override {} + void SetFieldsEligibleForManualFilling( + const std::vector& fields) override {} + mojo::AssociatedReceiverSet receivers_; base::OnceClosure quit_closure_; diff --git a/components/autofill/content/common/mojom/autofill_agent.mojom b/components/autofill/content/common/mojom/autofill_agent.mojom index 3bad5270f5f7f..1e8bab83e2723 100644 --- a/components/autofill/content/common/mojom/autofill_agent.mojom +++ b/components/autofill/content/common/mojom/autofill_agent.mojom @@ -86,6 +86,9 @@ interface AutofillAgent { // Allows heavy scraping of form data (e.g., button titles for // unowned forms). EnableHeavyFormDataScraping(); + + // Update fields that are eligible to show manual filling on form interaction. + SetFieldsEligibleForManualFilling(array fields); }; // There is one instance of this interface per render frame in the render diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc index 7d66cce3a53da..d8c31ad9dfb72 100644 --- a/components/autofill/content/renderer/autofill_agent.cc +++ b/components/autofill/content/renderer/autofill_agent.cc @@ -754,6 +754,11 @@ void AutofillAgent::EnableHeavyFormDataScraping() { is_heavy_form_data_scraping_enabled_ = true; } +void AutofillAgent::SetFieldsEligibleForManualFilling( + const std::vector& fields) { + form_cache_.SetFieldsEligibleForManualFilling(fields); +} + void AutofillAgent::QueryAutofillSuggestions( const WebFormControlElement& element, bool autoselect_first_suggestion) { diff --git a/components/autofill/content/renderer/autofill_agent.h b/components/autofill/content/renderer/autofill_agent.h index eec8d77ed93a3..e2a0d87aa2afb 100644 --- a/components/autofill/content/renderer/autofill_agent.h +++ b/components/autofill/content/renderer/autofill_agent.h @@ -106,6 +106,8 @@ class AutofillAgent : public content::RenderFrameObserver, GetElementFormAndFieldDataAtIndexCallback callback) override; void SetAssistantActionState(bool running) override; void EnableHeavyFormDataScraping() override; + void SetFieldsEligibleForManualFilling( + const std::vector& fields) override; void FormControlElementClicked(const blink::WebFormControlElement& element, bool was_focused); diff --git a/components/autofill/content/renderer/form_cache.cc b/components/autofill/content/renderer/form_cache.cc index 32d2eb4bf50a7..4e50f9d735eb6 100644 --- a/components/autofill/content/renderer/form_cache.cc +++ b/components/autofill/content/renderer/form_cache.cc @@ -227,6 +227,7 @@ void FormCache::Reset() { parsed_forms_.clear(); initial_select_values_.clear(); initial_checked_state_.clear(); + fields_eligible_for_manual_filling_.clear(); } void FormCache::ClearElement(WebFormControlElement& control_element, @@ -422,6 +423,19 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form, return true; } +bool FormCache::IsFormElementEligibleForManualFilling( + const blink::WebFormControlElement& control_element) { + return fields_eligible_for_manual_filling_.find( + FieldRendererId(control_element.UniqueRendererFormControlId())) != + fields_eligible_for_manual_filling_.end(); +} + +void FormCache::SetFieldsEligibleForManualFilling( + const std::vector& fields_eligible_for_manual_filling) { + fields_eligible_for_manual_filling_ = base::flat_set( + std::move(fields_eligible_for_manual_filling)); +} + size_t FormCache::ScanFormControlElements( const std::vector& control_elements, bool log_deprecation_messages) { diff --git a/components/autofill/content/renderer/form_cache.h b/components/autofill/content/renderer/form_cache.h index c2458830d07d1..d3fd4d21f8068 100644 --- a/components/autofill/content/renderer/form_cache.h +++ b/components/autofill/content/renderer/form_cache.h @@ -16,6 +16,7 @@ #include "base/macros.h" #include "components/autofill/core/common/field_data_manager.h" #include "components/autofill/core/common/form_data.h" +#include "components/autofill/core/common/form_data_predictions.h" #include "components/autofill/core/common/unique_ids.h" namespace blink { @@ -55,6 +56,16 @@ class FormCache { bool ShowPredictions(const FormDataPredictions& form, bool attach_predictions_to_dom); + // For a given |control_element| check whether it is eligible for manual + // filling on form interaction. + bool IsFormElementEligibleForManualFilling( + const blink::WebFormControlElement& control_element); + + // Stores the FieldRendererId of the fields that are eligible for manual + // filling in a set. + void SetFieldsEligibleForManualFilling( + const std::vector& fields_eligible_for_manual_filling); + private: FRIEND_TEST_ALL_PREFIXES(FormCacheTest, ShouldShowAutocompleteConsoleWarnings_Enabled); @@ -108,6 +119,9 @@ class FormCache { // keyed by the unique_renderer_form_control_id of the WebInputElements. std::map initial_checked_state_; + // Fields that are eligible to show manual filling on form interaction. + base::flat_set fields_eligible_for_manual_filling_; + DISALLOW_COPY_AND_ASSIGN(FormCache); }; diff --git a/components/autofill/content/renderer/form_cache_browsertest.cc b/components/autofill/content/renderer/form_cache_browsertest.cc index 7ed7e510c8eb4..1e21a2254d04d 100644 --- a/components/autofill/content/renderer/form_cache_browsertest.cc +++ b/components/autofill/content/renderer/form_cache_browsertest.cc @@ -327,4 +327,41 @@ TEST_F(FormCacheBrowserTest, ClearFormSelectElementEditedStateReset) { EXPECT_TRUE(select_month.UserHasEditedTheField()); } +TEST_F(FormCacheBrowserTest, IsFormElementEligibleForManualFilling) { + // Load a form. + LoadHTML( + "
" + "
" + "
" + "
" + "
"); + + WebDocument doc = GetMainFrame()->GetDocument(); + auto first_name_element = doc.GetElementById("fname").To(); + auto middle_name_element = doc.GetElementById("mname").To(); + auto last_name_element = doc.GetElementById("lname").To(); + + FormCache form_cache(GetMainFrame()); + std::vector forms = + form_cache.ExtractNewForms(/*field_data_manager=*/nullptr); + const FormData* form_data = GetFormByName(forms, "myForm"); + EXPECT_EQ(3u, form_data->fields.size()); + + // Set the first_name and last_name fields as eligible for manual filling. + std::vector fields_eligible_for_manual_filling; + fields_eligible_for_manual_filling.push_back( + form_data->fields[0].unique_renderer_id); + fields_eligible_for_manual_filling.push_back( + form_data->fields[2].unique_renderer_id); + form_cache.SetFieldsEligibleForManualFilling( + fields_eligible_for_manual_filling); + + EXPECT_TRUE( + form_cache.IsFormElementEligibleForManualFilling(first_name_element)); + EXPECT_FALSE( + form_cache.IsFormElementEligibleForManualFilling(middle_name_element)); + EXPECT_TRUE( + form_cache.IsFormElementEligibleForManualFilling(last_name_element)); +} + } // namespace autofill diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h index 7e2d219b99d04..9eef8679904cf 100644 --- a/components/autofill/core/browser/autofill_driver.h +++ b/components/autofill/core/browser/autofill_driver.h @@ -133,6 +133,11 @@ class AutofillDriver { const gfx::RectF& bounding_box) = 0; virtual net::IsolationInfo IsolationInfo() = 0; + + // Tells the renderer about the form fields that are eligible for triggering + // manual filling on form interaction. + virtual void SendFieldsEligibleForManualFillingToRenderer( + const std::vector& fields) = 0; }; } // namespace autofill diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc index d30aedd707c8e..ec4b069c3f7b1 100644 --- a/components/autofill/core/browser/autofill_manager.cc +++ b/components/autofill/core/browser/autofill_manager.cc @@ -267,6 +267,13 @@ void AutofillManager::OnFormsParsed(const std::vector& forms) { // queryable forms will be updated once the field type query is complete. driver()->SendAutofillTypePredictionsToRenderer(non_queryable_forms); driver()->SendAutofillTypePredictionsToRenderer(queryable_forms); + // Send the fields that are eligible for manual filling to the renderer. If + // server predictions are not yet available for these forms, the eligible + // fields would be updated again once they are available. + driver()->SendFieldsEligibleForManualFillingToRenderer( + FormStructure::FindFieldsEligibleForManualFilling(non_queryable_forms)); + driver()->SendFieldsEligibleForManualFillingToRenderer( + FormStructure::FindFieldsEligibleForManualFilling(queryable_forms)); LogAutofillTypePredictionsAvailable(log_manager_, non_queryable_forms); LogAutofillTypePredictionsAvailable(log_manager_, queryable_forms); @@ -377,7 +384,10 @@ bool AutofillManager::GetCachedFormAndField(const FormData& form, // Annotate the updated form with its predicted types. driver()->SendAutofillTypePredictionsToRenderer({*form_structure}); - + // Update the renderer with the latest set of fields eligible for manual + // filling. + driver()->SendFieldsEligibleForManualFillingToRenderer( + FormStructure::FindFieldsEligibleForManualFilling({*form_structure})); // There is no data to return if there are no auto-fillable fields. if (!(*form_structure)->autofill_count()) return false; @@ -512,6 +522,9 @@ void AutofillManager::OnLoadedServerPredictions( // annotate forms with the predicted types or add console warnings. driver()->SendAutofillTypePredictionsToRenderer(queried_forms); + driver()->SendFieldsEligibleForManualFillingToRenderer( + FormStructure::FindFieldsEligibleForManualFilling(queried_forms)); + LogAutofillTypePredictionsAvailable(log_manager_, queried_forms); // TODO(crbug.com/1176816): Remove the test code after initial integration. diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc index 2b72a5f7a9208..f63b5ba7c6c6b 100644 --- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc +++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc @@ -313,6 +313,8 @@ class MockAutofillDriver : public TestAutofillDriver { MOCK_METHOD1(SendAutofillTypePredictionsToRenderer, void(const std::vector& forms)); + MOCK_METHOD1(SendFieldsEligibleForManualFillingToRenderer, + void(const std::vector& fields)); }; } // namespace @@ -923,6 +925,35 @@ TEST_P(BrowserAutofillManagerStructuredProfileTest, FormsSeen(forms); } +// Test that when forms are seen, the renderer is sent the fields that are +// eligible for manual filling. +TEST_P(BrowserAutofillManagerStructuredProfileTest, + OnFormsSeen_SendFieldsEligibleForManualFillingToRenderer) { + // Set up a queryable form. + FormData form1; + CreateTestCreditCardFormData(&form1, true, false); + + // Set up a non-queryable form. + FormData form2; + FormFieldData field; + test::CreateTestFormField("Querty", "qwerty", "", "text", &field); + form2.host_frame = test::GetLocalFrameToken(); + form2.unique_renderer_id = test::MakeFormRendererId(); + form2.name = u"NonQueryable"; + form2.url = form1.url; + form2.action = GURL("https://myform.com/submit.html"); + form2.fields.push_back(field); + + // Package the forms for observation. + std::vector forms{form1, form2}; + + // Set up expectations. + EXPECT_CALL(*autofill_driver_, + SendFieldsEligibleForManualFillingToRenderer(_)) + .Times(2); + FormsSeen(forms); +} + // Test that no autofill suggestions are returned for a field with an // unrecognized autocomplete attribute. TEST_P(BrowserAutofillManagerStructuredProfileTest, diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc index 0d57e55ef4552..56de2697908d4 100644 --- a/components/autofill/core/browser/form_structure.cc +++ b/components/autofill/core/browser/form_structure.cc @@ -952,6 +952,29 @@ std::vector FormStructure::GetFieldTypePredictions( return forms; } +// static +std::vector FormStructure::FindFieldsEligibleForManualFilling( + const std::vector& forms) { + std::vector fields_eligible_for_manual_filling; + for (const auto* form : forms) { + for (const auto& field : form->fields_) { + FieldTypeGroup field_type_group = + autofill::GroupTypeOfServerFieldType(field->server_type()); + // In order to trigger the payments bottom sheet that assists users to + // manually fill the form, credit card form fields are marked eligible for + // manual filling. Also, if a field is not classified to a type, we can + // assume that the prediction failed and thus mark it eligible for manual + // filling. As more form types support manual filling on form interaction, + // this list may expand in the future. + if (field_type_group == FieldTypeGroup::kCreditCard || + field_type_group == FieldTypeGroup::kNoGroup) { + fields_eligible_for_manual_filling.push_back(field->unique_renderer_id); + } + } + } + return fields_eligible_for_manual_filling; +} + std::unique_ptr FormStructure::CreateForPasswordManagerUpload( FormSignature form_signature, const std::vector& field_signatures) { diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h index 76e5d13d63ddb..e2f747bf277f3 100644 --- a/components/autofill/core/browser/form_structure.h +++ b/components/autofill/core/browser/form_structure.h @@ -211,6 +211,11 @@ class FormStructure { // * NAME_LAST_SECOND heuristic predictions are unconditionally used. void OverrideServerPredictionsWithHeuristics(); + // Returns the FieldRendererId for fields that are eligible for Manual Filling + // on form interaction. + static std::vector FindFieldsEligibleForManualFilling( + const std::vector& forms); + const AutofillField* field(size_t index) const; AutofillField* field(size_t index); size_t field_count() const; @@ -332,6 +337,13 @@ class FormStructure { if (field_index < fields_.size() && type > 0 && type < MAX_VALID_FIELD_TYPE) fields_[field_index]->set_heuristic_type(type); } + // Set the server field type for |fields_[field_index]| to |type| for testing + // purposes. + void set_server_field_type_for_testing(size_t field_index, + ServerFieldType type) { + if (field_index < fields_.size() && type > 0 && type < MAX_VALID_FIELD_TYPE) + fields_[field_index]->set_server_type(type); + } #endif void set_password_symbol_vote(int noisified_symbol) { diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc index 11ab7c6314b43..1d69fb877381d 100644 --- a/components/autofill/core/browser/form_structure_unittest.cc +++ b/components/autofill/core/browser/form_structure_unittest.cc @@ -8390,4 +8390,48 @@ TEST_F(FormStructureTestImpl, IgnoreAribtraryAutocompleteSectionName) { EXPECT_EQ("blue-shipping-default", form_structure.field(1)->section); } +TEST_F(FormStructureTestImpl, FindFieldsEligibleForManualFilling) { + FormData form; + form.url = GURL("http://foo.com"); + FormFieldData field; + field.form_control_type = "text"; + field.max_length = 10000; + + FieldRendererId full_name_renderer_id = MakeFieldRendererId(); + field.label = u"Full Name"; + field.name = u"fullName"; + field.unique_renderer_id = full_name_renderer_id; + form.fields.push_back(field); + + FieldRendererId country_renderer_id = MakeFieldRendererId(); + field.label = u"Country"; + field.name = u"country"; + field.unique_renderer_id = country_renderer_id; + form.fields.push_back(field); + + FieldRendererId unknown_renderer_id = MakeFieldRendererId(); + field.label = u"Unknown"; + field.name = u"unknown"; + field.unique_renderer_id = unknown_renderer_id; + form.fields.push_back(field); + + FormStructure form_structure(form); + + form_structure.set_server_field_type_for_testing(0, CREDIT_CARD_NAME_FULL); + form_structure.set_server_field_type_for_testing(1, ADDRESS_HOME_COUNTRY); + form_structure.set_server_field_type_for_testing(2, UNKNOWN_TYPE); + + std::vector forms; + forms.push_back(&form_structure); + + form_structure.identify_sections_for_testing(); + std::vector expected_result; + // Only credit card related and unknown fields are elible for manual filling. + expected_result.push_back(full_name_renderer_id); + expected_result.push_back(unknown_renderer_id); + + EXPECT_EQ(expected_result, + FormStructure::FindFieldsEligibleForManualFilling(forms)); +} + } // namespace autofill diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc index 1577b463e7fc5..683bc93acf7cf 100644 --- a/components/autofill/core/browser/test_autofill_driver.cc +++ b/components/autofill/core/browser/test_autofill_driver.cc @@ -103,6 +103,9 @@ net::IsolationInfo TestAutofillDriver::IsolationInfo() { return isolation_info_; } +void TestAutofillDriver::SendFieldsEligibleForManualFillingToRenderer( + const std::vector& fields) {} + void TestAutofillDriver::SetIsIncognito(bool is_incognito) { is_incognito_ = is_incognito; } diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h index 0f43f44eca200..a3a298dcdb69a 100644 --- a/components/autofill/core/browser/test_autofill_driver.h +++ b/components/autofill/core/browser/test_autofill_driver.h @@ -65,6 +65,8 @@ class TestAutofillDriver : public ContentAutofillDriver { gfx::RectF TransformBoundingBoxToViewportCoordinates( const gfx::RectF& bounding_box) override; net::IsolationInfo IsolationInfo() override; + void SendFieldsEligibleForManualFillingToRenderer( + const std::vector& fields) override; // Methods unique to TestAutofillDriver that tests can use to specialize // functionality. diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h index c9c4353513a40..8b3426c272fe9 100644 --- a/components/autofill/ios/browser/autofill_driver_ios.h +++ b/components/autofill/ios/browser/autofill_driver_ios.h @@ -58,6 +58,8 @@ class AutofillDriverIOS : public AutofillDriver { void RendererShouldAcceptDataListSuggestion( const FieldGlobalId& field, const std::u16string& value) override; + void SendFieldsEligibleForManualFillingToRenderer( + const std::vector& fields) override; BrowserAutofillManager* autofill_manager() { return &browser_autofill_manager_; diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm index 69c07423ab6cd..683cf1404cb7e 100644 --- a/components/autofill/ios/browser/autofill_driver_ios.mm +++ b/components/autofill/ios/browser/autofill_driver_ios.mm @@ -143,6 +143,9 @@ const FieldGlobalId& field, const std::u16string& value) {} +void AutofillDriverIOS::SendFieldsEligibleForManualFillingToRenderer( + const std::vector& fields) {} + void AutofillDriverIOS::RendererShouldClearFilledSection() {} void AutofillDriverIOS::RendererShouldClearPreviewedForm() {