diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc index e5a63aa169e038..6e2021c7fc9b44 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 ca0f934d40c17d..413adf8b9eae27 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 ddeafa7552e0f0..080e9d9d3a15e5 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 3bad5270f5f7f2..1e8bab83e2723d 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 7d66cce3a53da8..d8c31ad9dfb724 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 eec8d77ed93a30..e2a0d87aa2afba 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 32d2eb4bf50a70..4e50f9d735eb63 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 c2458830d07d13..d3fd4d21f8068a 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 7ed7e510c8eb47..1e21a2254d04d3 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 7e2d219b99d041..9eef8679904cf5 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 d30aedd707c8e8..ec4b069c3f7b10 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 2b72a5f7a92088..f63b5ba7c6c6b9 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 0d57e55ef45524..56de2697908d40 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 76e5d13d63ddb9..e2f747bf277f3e 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 11ab7c6314b432..1d69fb877381dc 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 1577b463e7fc59..683bc93acf7cf4 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 0f43f44eca200e..a3a298dcdb69a7 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 c9c4353513a40e..8b3426c272fe91 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 69c07423ab6cd3..683cf1404cb7ea 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() {