Skip to content

Commit

Permalink
[Autofill] Parameterize the new sectioning algorithm
Browse files Browse the repository at this point in the history
This change incorporates in the new sectioning algorithm three newly introduced feature parameters that can be combined to experiment with different sectioning techniques.

See details about the newly added parameters in form_structure_sectioning_util.h.

Bug: 1153539
Change-Id: Ia6cc26b83febabab41973cc89d95cb76fae04034
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3916401
Commit-Queue: Eva Herencsarova <evih@google.com>
Reviewed-by: Christoph Schwering <schwering@google.com>
Reviewed-by: Florian Leimgruber <fleimgruber@google.com>
Cr-Commit-Position: refs/heads/main@{#1052296}
  • Loading branch information
Eva Herencsarova authored and Chromium LUCI CQ committed Sep 28, 2022
1 parent f9a2383 commit 62cae5a
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 17 deletions.
60 changes: 45 additions & 15 deletions components/autofill/core/browser/form_structure_sectioning_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/common/autofill_features.h"

namespace autofill {

Expand Down Expand Up @@ -60,10 +61,10 @@ bool IsSectionable(const AutofillField& field) {
// Assign all credit card fields without a valid autocomplete attribute section
// to one, separate section based on the first credit card field.
void AssignCreditCardSections(
base::span<std::unique_ptr<AutofillField>> fields,
base::span<const std::unique_ptr<AutofillField>> fields,
base::flat_map<LocalFrameToken, size_t>& frame_token_ids) {
auto first_cc_field = base::ranges::find_if(
fields, [&](const std::unique_ptr<AutofillField>& field) {
fields, [](const std::unique_ptr<AutofillField>& field) {
return field->Type().group() == FieldTypeGroup::kCreditCard &&
!field->section;
});
Expand All @@ -78,7 +79,7 @@ void AssignCreditCardSections(
}

void AssignAutocompleteSections(
base::span<std::unique_ptr<AutofillField>> fields) {
base::span<const std::unique_ptr<AutofillField>> fields) {
for (const auto& field : fields) {
if (field->parsed_autocomplete) {
Section autocomplete_section = Section::FromAutocomplete(
Expand All @@ -91,7 +92,7 @@ void AssignAutocompleteSections(
}

void AssignFieldIdentifierSections(
base::span<std::unique_ptr<AutofillField>> section,
base::span<const std::unique_ptr<AutofillField>> section,
base::flat_map<LocalFrameToken, size_t>& frame_token_ids) {
Section s = Section::FromFieldIdentifier(**section.begin(), frame_token_ids);
for (const auto& field : section) {
Expand All @@ -100,11 +101,22 @@ void AssignFieldIdentifierSections(
}
}

void ExpandSections(base::span<const std::unique_ptr<AutofillField>> fields) {
Section previous_section;
for (const auto& field : fields) {
if (!IsSectionable(*field))
continue;
if (!field->section && previous_section)
field->section = previous_section;
previous_section = field->section;
}
}

bool ShouldStartNewSection(const ServerFieldTypeSet& seen_types,
const AutofillField& current_field,
const AutofillField& previous_field) {
DCHECK(!previous_field.section);
DCHECK(seen_types.contains(previous_field.Type().GetStorableType()));
if (current_field.section)
return features::kAutofillSectioningModeCreateGaps.Get();

const ServerFieldType current_type = current_field.Type().GetStorableType();
if (current_type == UNKNOWN_TYPE)
Expand All @@ -126,43 +138,61 @@ bool ShouldStartNewSection(const ServerFieldTypeSet& seen_types,
return HaveSeenSimilarType(current_type, seen_types);
}

// Finds the first sectionable field that doesn't have a section assigned.
base::span<const std::unique_ptr<AutofillField>>::iterator
FindBeginOfNextSection(
base::span<const std::unique_ptr<AutofillField>>::iterator begin,
base::span<const std::unique_ptr<AutofillField>>::iterator end) {
while (begin != end && ((*begin)->section || !IsSectionable(**begin)))
begin++;
return begin;
}

// Finds the longest prefix of [begin, end) that belongs to the same section,
// according to `ShouldStartNewSection()`.
base::span<std::unique_ptr<AutofillField>>::iterator FindEndOfNextSection(
base::span<std::unique_ptr<AutofillField>>::iterator begin,
base::span<std::unique_ptr<AutofillField>>::iterator end) {
base::span<const std::unique_ptr<AutofillField>>::iterator FindEndOfNextSection(
base::span<const std::unique_ptr<AutofillField>>::iterator begin,
base::span<const std::unique_ptr<AutofillField>>::iterator end) {
// Keeps track of the focusable types we've seen in this section.
ServerFieldTypeSet seen_types;
// The `prev_field` is from the section whose end we are currently searching.
const AutofillField* prev_field = nullptr;
for (auto it = begin; it != end; it++) {
const AutofillField& field = **it;
if (field.section || !IsSectionable(field))
if (!IsSectionable(field))
continue;
if (prev_field && ShouldStartNewSection(seen_types, field, *prev_field))
return it;
seen_types.insert(field.Type().GetStorableType());
prev_field = &field;
if (!field.section) {
seen_types.insert(field.Type().GetStorableType());
prev_field = &field;
}
}
return end;
}

} // namespace

void AssignSections(base::span<std::unique_ptr<AutofillField>> fields) {
void AssignSections(base::span<const std::unique_ptr<AutofillField>> fields) {
for (const auto& field : fields)
field->section = Section();

// Create a unique identifier based on the field for the section.
base::flat_map<LocalFrameToken, size_t> frame_token_ids;

AssignAutocompleteSections(fields);
if (!features::kAutofillSectioningModeIgnoreAutocomplete.Get())
AssignAutocompleteSections(fields);
AssignCreditCardSections(fields, frame_token_ids);
if (features::kAutofillSectioningModeExpand.Get())
ExpandSections(fields);

auto begin = fields.begin();
while (begin != fields.end()) {
begin = FindBeginOfNextSection(begin, fields.end());
auto end = FindEndOfNextSection(begin, fields.end());
AssignFieldIdentifierSections({begin, end}, frame_token_ids);
DCHECK(begin != end || end == fields.end());
if (begin != end)
AssignFieldIdentifierSections({begin, end}, frame_token_ids);
begin = end;
}
}
Expand Down
64 changes: 62 additions & 2 deletions components/autofill/core/browser/form_structure_sectioning_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,74 @@ namespace autofill {
// Name: <input id=1> | field 1 based
// Country: <input id=2> | field 1 based
// Name: <input id=3 autocomplete=”section-A name”> | A
// Country: <input id=4> | field 1 based
// Street: <input id=4> | field 1 based
// CC number: <input id=5> | field 5 based
// CC number: <input id=6 style="display:none"> | field 5 based
// Name: <input id=7> | field 7 based
// Country: <input id=8> | field 7 based
// CC number: <input id=9> | field 5 based
// ------------------------------------------------------+-------------------
//
// The `kAutofillUseParameterizedSectioning` has boolean feature parameters
// which assign sections in the following way:
// a. `kAutofillSectioningModeIgnoreAutocomplete`: Disables condition 1, i.e.
// ignores the autocomplete attribute section. Otherwise, conditions 2 to 4
// apply.
//
// Example:
// ------------------------------------------------------+-------------------
// HTML code | Section
// ------------------------------------------------------+-------------------
// Name: <input id=1> | field 1 based
// Country: <input id=2> | field 1 based
// Name: <input id=3 autocomplete=”section-A name”> | field 3 based
// Street: <input id=4> | field 3 based
// CC number: <input id=5> | field 5 based
// CC number: <input id=6 style="display:none"> | field 5 based
// Name: <input id=7> | field 7 based
// Country: <input id=8> | field 7 based
// CC number: <input id=9> | field 5 based
// ------------------------------------------------------+-------------------
//
// b. `kAutofillSectioningModeCreateGaps`: The conditions from above apply.
// Additionally, in condition 3, intervals are split by fields that were
// sectioned based on conditions 1 and 2.
//
// Example:
// ------------------------------------------------------+-------------------
// HTML code | Section
// ------------------------------------------------------+-------------------
// Name: <input id=1> | field 1 based
// Country: <input id=2> | field 1 based
// Name: <input id=3 autocomplete=”section-A name”> | A
// Street: <input id=4> | field 4 based
// CC number: <input id=5> | field 5 based
// CC number: <input id=6 style="display:none"> | field 5 based
// Name: <input id=7> | field 7 based
// Country: <input id=8> | field 7 based
// CC number: <input id=9> | field 5 based
// ------------------------------------------------------+-------------------
//
// c. `kAutofillSectioningModeExpand`: The conditions from above apply.
// Additionally, sections based on conditions 1 and 2 are propagated
// downwards to focusable or <select> fields, ignoring all the usual
// sectioning rules.
//
// Example:
// ------------------------------------------------------+-------------------
// HTML code | Section
// ------------------------------------------------------+-------------------
// Name: <input id=1> | field 1 based
// Country: <input id=2> | field 1 based
// Name: <input id=3 autocomplete=”section-A name”> | A
// Street: <input id=4> | A
// CC number: <input id=5> | field 5 based
// CC number: <input id=6 style="display:none"> | field 5 based
// Name: <input id=7> | field 5 based
// Country: <input id=8> | field 5 based
// CC number: <input id=9> | field 5 based
// ------------------------------------------------------+-------------------
void AssignSections(base::span<std::unique_ptr<AutofillField>> fields);
void AssignSections(base::span<const std::unique_ptr<AutofillField>> fields);

} // namespace autofill

Expand Down
7 changes: 7 additions & 0 deletions components/autofill/core/common/autofill_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,13 @@ BASE_FEATURE(kAutofillUseNewSectioningMethod,
BASE_FEATURE(kAutofillUseParameterizedSectioning,
"AutofillUseParameterizedSectioning",
base::FEATURE_DISABLED_BY_DEFAULT);
// In the experiment, we test different combinations of these parameters.
const base::FeatureParam<bool> kAutofillSectioningModeIgnoreAutocomplete{
&kAutofillUseParameterizedSectioning, "ignore_autocomplete", false};
const base::FeatureParam<bool> kAutofillSectioningModeCreateGaps{
&kAutofillUseParameterizedSectioning, "create_gaps", false};
const base::FeatureParam<bool> kAutofillSectioningModeExpand{
&kAutofillUseParameterizedSectioning, "expand_assigned_sections", false};

// Controls whether to use form renderer IDs to find the form which contains the
// field that was last interacted with in
Expand Down
6 changes: 6 additions & 0 deletions components/autofill/core/common/autofill_features.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ BASE_DECLARE_FEATURE(kAutofillUseNewSectioningMethod);
COMPONENT_EXPORT(AUTOFILL)
BASE_DECLARE_FEATURE(kAutofillUseParameterizedSectioning);
COMPONENT_EXPORT(AUTOFILL)
extern const base::FeatureParam<bool> kAutofillSectioningModeIgnoreAutocomplete;
COMPONENT_EXPORT(AUTOFILL)
extern const base::FeatureParam<bool> kAutofillSectioningModeCreateGaps;
COMPONENT_EXPORT(AUTOFILL)
extern const base::FeatureParam<bool> kAutofillSectioningModeExpand;
COMPONENT_EXPORT(AUTOFILL)
BASE_DECLARE_FEATURE(kAutofillRefillByFormRendererId);
COMPONENT_EXPORT(AUTOFILL)
BASE_DECLARE_FEATURE(kAutofillUseConsistentPopupSettingsIcons);
Expand Down

0 comments on commit 62cae5a

Please sign in to comment.