Skip to content

Commit

Permalink
[VirtualCards] Store fields eligible for manual filling in form cache
Browse files Browse the repository at this point in the history
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 <siashah@chromium.org>
Reviewed-by: Jared Saul <jsaul@google.com>
Reviewed-by: Christoph Schwering <schwering@google.com>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#880619}
  • Loading branch information
siashah authored and Chromium LUCI CQ committed May 7, 2021
1 parent 7086b0b commit 6301ca4
Show file tree
Hide file tree
Showing 19 changed files with 229 additions and 2 deletions.
11 changes: 10 additions & 1 deletion components/autofill/content/browser/content_autofill_driver.cc
Expand Up @@ -193,12 +193,21 @@ void ContentAutofillDriver::SendAutofillTypePredictionsToRenderer(
const std::vector<FormStructure*>& forms) {
if (!RendererIsAvailable())
return;

// TODO(crbug.com/1185232) Send the FormDataPredictions object only if the
// debugging flag is enabled.
std::vector<FormDataPredictions> type_predictions =
FormStructure::GetFieldTypePredictions(forms);
GetAutofillAgent()->FieldTypePredictionsAvailable(type_predictions);
}

void ContentAutofillDriver::SendFieldsEligibleForManualFillingToRenderer(
const std::vector<FieldRendererId>& fields) {
if (!RendererIsAvailable())
return;

GetAutofillAgent()->SetFieldsEligibleForManualFilling(fields);
}

void ContentAutofillDriver::RendererShouldAcceptDataListSuggestion(
const FieldGlobalId& field,
const std::u16string& value) {
Expand Down
2 changes: 2 additions & 0 deletions components/autofill/content/browser/content_autofill_driver.h
Expand Up @@ -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<FieldRendererId>& fields) override;

// mojom::AutofillDriver:
void SetFormToBeProbablySubmitted(
Expand Down
Expand Up @@ -235,6 +235,9 @@ class FakeAutofillAgent : public mojom::AutofillAgent {

void SetAssistantActionState(bool running) override {}

void SetFieldsEligibleForManualFilling(
const std::vector<FieldRendererId>& fields) override {}

mojo::AssociatedReceiverSet<mojom::AutofillAgent> receivers_;

base::OnceClosure quit_closure_;
Expand Down
3 changes: 3 additions & 0 deletions components/autofill/content/common/mojom/autofill_agent.mojom
Expand Up @@ -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<FieldRendererId> fields);
};

// There is one instance of this interface per render frame in the render
Expand Down
5 changes: 5 additions & 0 deletions components/autofill/content/renderer/autofill_agent.cc
Expand Up @@ -754,6 +754,11 @@ void AutofillAgent::EnableHeavyFormDataScraping() {
is_heavy_form_data_scraping_enabled_ = true;
}

void AutofillAgent::SetFieldsEligibleForManualFilling(
const std::vector<FieldRendererId>& fields) {
form_cache_.SetFieldsEligibleForManualFilling(fields);
}

void AutofillAgent::QueryAutofillSuggestions(
const WebFormControlElement& element,
bool autoselect_first_suggestion) {
Expand Down
2 changes: 2 additions & 0 deletions components/autofill/content/renderer/autofill_agent.h
Expand Up @@ -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<FieldRendererId>& fields) override;

void FormControlElementClicked(const blink::WebFormControlElement& element,
bool was_focused);
Expand Down
14 changes: 14 additions & 0 deletions components/autofill/content/renderer/form_cache.cc
Expand Up @@ -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,
Expand Down Expand Up @@ -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<FieldRendererId>& fields_eligible_for_manual_filling) {
fields_eligible_for_manual_filling_ = base::flat_set<FieldRendererId>(
std::move(fields_eligible_for_manual_filling));
}

size_t FormCache::ScanFormControlElements(
const std::vector<WebFormControlElement>& control_elements,
bool log_deprecation_messages) {
Expand Down
14 changes: 14 additions & 0 deletions components/autofill/content/renderer/form_cache.h
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<FieldRendererId>& fields_eligible_for_manual_filling);

private:
FRIEND_TEST_ALL_PREFIXES(FormCacheTest,
ShouldShowAutocompleteConsoleWarnings_Enabled);
Expand Down Expand Up @@ -108,6 +119,9 @@ class FormCache {
// keyed by the unique_renderer_form_control_id of the WebInputElements.
std::map<FieldRendererId, bool> initial_checked_state_;

// Fields that are eligible to show manual filling on form interaction.
base::flat_set<FieldRendererId> fields_eligible_for_manual_filling_;

DISALLOW_COPY_AND_ASSIGN(FormCache);
};

Expand Down
37 changes: 37 additions & 0 deletions components/autofill/content/renderer/form_cache_browsertest.cc
Expand Up @@ -327,4 +327,41 @@ TEST_F(FormCacheBrowserTest, ClearFormSelectElementEditedStateReset) {
EXPECT_TRUE(select_month.UserHasEditedTheField());
}

TEST_F(FormCacheBrowserTest, IsFormElementEligibleForManualFilling) {
// Load a form.
LoadHTML(
"<html><form id='myForm'>"
"<label>First Name:</label><input id='fname' name='0'/><br/>"
"<label>Middle Name:</label> <input id='mname' name='1'/><br/>"
"<label>Last Name:</label> <input id='lname' name='2'/><br/>"
"</form></html>");

WebDocument doc = GetMainFrame()->GetDocument();
auto first_name_element = doc.GetElementById("fname").To<WebInputElement>();
auto middle_name_element = doc.GetElementById("mname").To<WebInputElement>();
auto last_name_element = doc.GetElementById("lname").To<WebInputElement>();

FormCache form_cache(GetMainFrame());
std::vector<FormData> 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<FieldRendererId> 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
5 changes: 5 additions & 0 deletions components/autofill/core/browser/autofill_driver.h
Expand Up @@ -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<FieldRendererId>& fields) = 0;
};

} // namespace autofill
Expand Down
15 changes: 14 additions & 1 deletion components/autofill/core/browser/autofill_manager.cc
Expand Up @@ -267,6 +267,13 @@ void AutofillManager::OnFormsParsed(const std::vector<const FormData*>& 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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
Expand Up @@ -313,6 +313,8 @@ class MockAutofillDriver : public TestAutofillDriver {

MOCK_METHOD1(SendAutofillTypePredictionsToRenderer,
void(const std::vector<FormStructure*>& forms));
MOCK_METHOD1(SendFieldsEligibleForManualFillingToRenderer,
void(const std::vector<FieldRendererId>& fields));
};

} // namespace
Expand Down Expand Up @@ -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<FormData> 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,
Expand Down
23 changes: 23 additions & 0 deletions components/autofill/core/browser/form_structure.cc
Expand Up @@ -952,6 +952,29 @@ std::vector<FormDataPredictions> FormStructure::GetFieldTypePredictions(
return forms;
}

// static
std::vector<FieldRendererId> FormStructure::FindFieldsEligibleForManualFilling(
const std::vector<FormStructure*>& forms) {
std::vector<FieldRendererId> 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> FormStructure::CreateForPasswordManagerUpload(
FormSignature form_signature,
const std::vector<FieldSignature>& field_signatures) {
Expand Down
12 changes: 12 additions & 0 deletions components/autofill/core/browser/form_structure.h
Expand Up @@ -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<FieldRendererId> FindFieldsEligibleForManualFilling(
const std::vector<FormStructure*>& forms);

const AutofillField* field(size_t index) const;
AutofillField* field(size_t index);
size_t field_count() const;
Expand Down Expand Up @@ -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) {
Expand Down
44 changes: 44 additions & 0 deletions components/autofill/core/browser/form_structure_unittest.cc
Expand Up @@ -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<FormStructure*> forms;
forms.push_back(&form_structure);

form_structure.identify_sections_for_testing();
std::vector<FieldRendererId> 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
3 changes: 3 additions & 0 deletions components/autofill/core/browser/test_autofill_driver.cc
Expand Up @@ -103,6 +103,9 @@ net::IsolationInfo TestAutofillDriver::IsolationInfo() {
return isolation_info_;
}

void TestAutofillDriver::SendFieldsEligibleForManualFillingToRenderer(
const std::vector<FieldRendererId>& fields) {}

void TestAutofillDriver::SetIsIncognito(bool is_incognito) {
is_incognito_ = is_incognito;
}
Expand Down
2 changes: 2 additions & 0 deletions components/autofill/core/browser/test_autofill_driver.h
Expand Up @@ -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<FieldRendererId>& fields) override;

// Methods unique to TestAutofillDriver that tests can use to specialize
// functionality.
Expand Down

0 comments on commit 6301ca4

Please sign in to comment.