blob: 38e0e88dea4d00c77e50da9d03f79b7b795bb318 [file]
// Copyright 2023 The Chromium Authors
// 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_structure_rationalization_engine.h"
#include <algorithm>
#include <array>
#include "base/containers/flat_map.h"
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/form_parsing/form_field_parser.h"
#include "components/autofill/core/browser/form_parsing/regex_patterns.h"
#include "components/autofill/core/browser/logging/log_manager.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/form_field_data.h"
#include "components/autofill/core/common/logging/log_buffer.h"
#include "components/autofill/core/common/logging/log_macros.h"
namespace autofill::rationalization {
EnvironmentCondition::EnvironmentCondition() = default;
EnvironmentCondition::EnvironmentCondition(EnvironmentCondition&&) = default;
EnvironmentCondition::~EnvironmentCondition() = default;
EnvironmentCondition& EnvironmentCondition::operator=(EnvironmentCondition&&) =
default;
EnvironmentConditionBuilder::EnvironmentConditionBuilder() = default;
EnvironmentConditionBuilder::~EnvironmentConditionBuilder() = default;
EnvironmentConditionBuilder& EnvironmentConditionBuilder::SetCountryList(
std::vector<GeoIpCountryCode> country_list) & {
env_.country_list = std::move(country_list);
return *this;
}
EnvironmentConditionBuilder&& EnvironmentConditionBuilder::SetCountryList(
std::vector<GeoIpCountryCode> country_list) && {
return std::move(this->SetCountryList(std::move(country_list)));
}
EnvironmentConditionBuilder& EnvironmentConditionBuilder::SetFeature(
const base::Feature* feature) & {
env_.feature = feature;
return *this;
}
EnvironmentConditionBuilder&& EnvironmentConditionBuilder::SetFeature(
const base::Feature* feature) && {
return std::move(this->SetFeature(feature));
}
EnvironmentCondition EnvironmentConditionBuilder::Build() && {
return std::move(env_);
}
RationalizationRule::RationalizationRule() = default;
RationalizationRule::~RationalizationRule() = default;
RationalizationRuleBuilder::RationalizationRuleBuilder() = default;
RationalizationRuleBuilder::~RationalizationRuleBuilder() = default;
RationalizationRule::RationalizationRule(RationalizationRule&&) = default;
RationalizationRule& RationalizationRule::operator=(RationalizationRule&&) =
default;
RationalizationRuleBuilder& RationalizationRuleBuilder::SetRuleName(
std::string_view rule_name) & {
rule.rule_name = rule_name;
return *this;
}
RationalizationRuleBuilder&& RationalizationRuleBuilder::SetRuleName(
std::string_view rule_name) && {
return std::move(this->SetRuleName(rule_name));
}
RationalizationRuleBuilder& RationalizationRuleBuilder::SetEnvironmentCondition(
EnvironmentCondition environment_condition) & {
rule.environment_condition = std::move(environment_condition);
return *this;
}
RationalizationRuleBuilder&&
RationalizationRuleBuilder::SetEnvironmentCondition(
EnvironmentCondition environment_condition) && {
return std::move(
this->SetEnvironmentCondition(std::move(environment_condition)));
}
RationalizationRuleBuilder& RationalizationRuleBuilder::SetTriggerField(
FieldCondition trigger_field) & {
rule.trigger_field = std::move(trigger_field);
return *this;
}
RationalizationRuleBuilder&& RationalizationRuleBuilder::SetTriggerField(
FieldCondition trigger_field) && {
return std::move(this->SetTriggerField(std::move(trigger_field)));
}
RationalizationRuleBuilder& RationalizationRuleBuilder::SetOtherFieldConditions(
std::vector<FieldCondition> other_field_conditions) & {
rule.other_field_conditions = std::move(other_field_conditions);
return *this;
}
RationalizationRuleBuilder&&
RationalizationRuleBuilder::SetOtherFieldConditions(
std::vector<FieldCondition> other_field_conditions) && {
return std::move(
this->SetOtherFieldConditions(std::move(other_field_conditions)));
}
RationalizationRuleBuilder&
RationalizationRuleBuilder::SetFieldsWithConditionsDoNotExist(
std::vector<FieldCondition> fields_with_conditions_do_not_exist) & {
rule.fields_with_conditions_do_not_exist =
std::move(fields_with_conditions_do_not_exist);
return *this;
}
RationalizationRuleBuilder&&
RationalizationRuleBuilder::SetFieldsWithConditionsDoNotExist(
std::vector<FieldCondition> fields_with_conditions_do_not_exist) && {
return std::move(this->SetFieldsWithConditionsDoNotExist(
std::move(fields_with_conditions_do_not_exist)));
}
RationalizationRuleBuilder& RationalizationRuleBuilder::SetActions(
std::vector<SetTypeAction> actions) & {
rule.actions = std::move(actions);
return *this;
}
RationalizationRuleBuilder&& RationalizationRuleBuilder::SetActions(
std::vector<SetTypeAction> actions) && {
return std::move(this->SetActions(std::move(actions)));
}
RationalizationRule RationalizationRuleBuilder::Build() && {
return std::move(rule);
}
namespace internal {
bool IsEnvironmentConditionFulfilled(ParsingContext& context,
const EnvironmentCondition& env) {
if (!env.country_list.empty() &&
!std::ranges::contains(env.country_list, context.client_country)) {
return false;
}
if (env.feature && !base::FeatureList::IsEnabled(*env.feature)) {
return false;
}
return true;
}
bool IsFieldConditionFulfilledIgnoringLocation(ParsingContext& context,
const FieldCondition& condition,
const AutofillField& field) {
if (condition.possible_overall_types.has_value() &&
!condition.possible_overall_types->contains_any(
field.Type().GetTypes())) {
return false;
}
if (condition.regex_reference_match.has_value()) {
if (!FormFieldParser::FieldMatchesMatchPatternRef(
context, field, condition.regex_reference_match.value())) {
return false;
}
}
return true;
}
std::optional<size_t> FindFieldMeetingCondition(
ParsingContext& context,
base::span<const std::unique_ptr<AutofillField>> fields,
size_t start_index,
const FieldCondition& condition) {
// This function is called with `start_index` pointing to the trigger field.
// It will search in the direction of `condition.location` for a field that
// meets the `condition`, and returns whether it found one.
auto search = [&](int direction) -> std::optional<size_t> {
for (int i = start_index + direction;
i >= 0 && i < static_cast<int>(fields.size()); i += direction) {
const AutofillField& candidate_field = *fields[i];
if (IsFieldConditionFulfilledIgnoringLocation(context, condition,
candidate_field)) {
return static_cast<size_t>(i);
}
if (!candidate_field.Type().GetTypes().contains(UNKNOWN_TYPE) &&
((direction > 0 &&
condition.location == FieldLocation::kNextClassifiedSuccessor) ||
(direction < 0 &&
condition.location == FieldLocation::kLastClassifiedPredecessor))) {
// Don't try any further once we have checked the last/next classified
// field.
break;
}
}
return std::nullopt;
};
// Determine search strategy based on `condition.location`.
switch (condition.location) {
case FieldLocation::kPredecessor:
case FieldLocation::kLastClassifiedPredecessor:
return search(/*direction=*/-1); // Backward search.
case FieldLocation::kTriggerField:
NOTREACHED();
case FieldLocation::kNextClassifiedSuccessor:
case FieldLocation::kSuccessor:
return search(/*direction=*/1); // Forward search.
case FieldLocation::kAnywhere:
// Try forward first, then backward.
const std::optional<size_t> forward_result = search(/*direction=*/1);
return forward_result ? forward_result : search(/*direction=*/-1);
}
return std::nullopt;
}
void ApplyRuleIfApplicable(
ParsingContext& context,
const RationalizationRule& rule,
base::span<const std::unique_ptr<AutofillField>> fields,
LogManager* log_manager) {
if (rule.environment_condition.has_value() &&
!IsEnvironmentConditionFulfilled(context,
rule.environment_condition.value())) {
return;
}
for (size_t i = 0; i < fields.size(); ++i) {
const std::unique_ptr<AutofillField>& trigger_field = fields[i];
// Check whether we have found a trigger field at index i.
if (!IsFieldConditionFulfilledIgnoringLocation(context, rule.trigger_field,
*trigger_field)) {
continue;
}
// Check whether all the other conditions are also met for the surrounding
// fields.
base::flat_map<FieldLocation, size_t> found_fields;
for (const FieldCondition& other_field_condition :
rule.other_field_conditions) {
CHECK_NE(other_field_condition.location, FieldLocation::kTriggerField);
std::optional<size_t> match_index =
FindFieldMeetingCondition(context, fields, i, other_field_condition);
if (!match_index.has_value()) {
break;
}
found_fields[other_field_condition.location] = match_index.value();
}
// Only proceed if all other conditions were met.
if (found_fields.size() != rule.other_field_conditions.size()) {
continue;
}
bool found_field_with_condition_that_must_not_exist = false;
for (const FieldCondition& field_with_condition_do_not_exist :
rule.fields_with_conditions_do_not_exist) {
CHECK_NE(field_with_condition_do_not_exist.location,
FieldLocation::kTriggerField);
if (FindFieldMeetingCondition(context, fields, i,
field_with_condition_do_not_exist)) {
found_field_with_condition_that_must_not_exist = true;
break;
}
}
// Don't proceed if the condition which shouldn't be met was satisfied.
if (found_field_with_condition_that_must_not_exist) {
continue;
}
found_fields[FieldLocation::kTriggerField] = i;
// Apply actions.
LogBuffer buffer(IsLoggingActive(log_manager));
for (const SetTypeAction& action : rule.actions) {
// Actions can only happen for fields that are bound via conditions.
CHECK(found_fields.find(action.target) != found_fields.end());
AutofillField& field = *fields[found_fields[action.target]];
buffer << ", changing field " << found_fields[action.target] << " from "
<< FieldTypeSetToString(field.Type().GetTypes()) << " to "
<< FieldTypeToStringView(action.set_overall_type);
field.SetTypeTo(AutofillType(action.set_overall_type),
AutofillPredictionSource::kRationalization);
}
LOG_AF(log_manager) << LoggingScope::kRationalization
<< LogMessage::kRationalization << rule.rule_name
<< " applies, performing changes: "
<< std::move(buffer);
}
}
} // namespace internal
void ApplyRationalizationEngineRules(
ParsingContext& context,
base::span<const std::unique_ptr<AutofillField>> fields,
LogManager* log_manager) {
auto create_rules = [] {
return std::to_array({
RationalizationRuleBuilder()
// A name for the rule (for logging purposes).
.SetRuleName("Fix colonia as address-line2 in MX")
// Only if the requirements specified in the environment are all
// met, the RationalizationRule is executed.
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("MX")})
.Build())
// This is the core field to which the rule applies.
.SetTriggerField(FieldCondition{
// The trigger field needs to be an ADDRESS_HOME_LINE2.
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE2},
// Lookup in legacy_regex_patterns.
.regex_reference_match = "ADDRESS_HOME_DEPENDENT_LOCALITY",
})
// All of the following conditions need to be met for actions to be
// executed. The .location specifies which fields to consider. It
// also binds the fields to a label that is later referenced in
// actions.
.SetOtherFieldConditions({
FieldCondition{
.location = FieldLocation::kLastClassifiedPredecessor,
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE1},
},
})
// What actions to perform on the trigger fields and other fields
// that had conditions.
.SetActions({
SetTypeAction{
.target = FieldLocation::kLastClassifiedPredecessor,
.set_overall_type = ADDRESS_HOME_STREET_ADDRESS,
},
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type = ADDRESS_HOME_DEPENDENT_LOCALITY,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName("Fix address-overflow as address-line2 in DE")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("DE")})
.Build())
.SetTriggerField(FieldCondition{
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_OVERFLOW}})
.SetOtherFieldConditions({
FieldCondition{
.location = FieldLocation::kPredecessor,
.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_STREET_ADDRESS,
ADDRESS_HOME_STREET_LOCATION,
ADDRESS_HOME_LINE1},
},
})
.SetActions({
SetTypeAction{
.target = FieldLocation::kPredecessor,
.set_overall_type = ADDRESS_HOME_LINE1,
},
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type = ADDRESS_HOME_LINE2,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName("Rationalize ADDRESS_HOME_LINE1 into "
"ADDRESS_HOME_STREET_ADDRESS for DE")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("DE")})
.Build())
.SetTriggerField(FieldCondition{
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE1}})
.SetFieldsWithConditionsDoNotExist({
FieldCondition{
.location = FieldLocation::kSuccessor,
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE2}},
})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type = ADDRESS_HOME_STREET_ADDRESS,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName("Fix ADDRESS_HOME_HOUSE_NUMBER_AND_APT for PL")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("PL")})
.Build())
.SetTriggerField(
FieldCondition{.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_HOUSE_NUMBER}})
.SetOtherFieldConditions({
FieldCondition{
.location = FieldLocation::kLastClassifiedPredecessor,
.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_STREET_NAME},
},
})
.SetFieldsWithConditionsDoNotExist({
FieldCondition{
.location = FieldLocation::kNextClassifiedSuccessor,
.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_APT_NUM,
ADDRESS_HOME_APT_TYPE, UNKNOWN_TYPE,
ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2,
ADDRESS_HOME_LINE3}},
})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type = ADDRESS_HOME_HOUSE_NUMBER_AND_APT,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName("Fix ADDRESS_HOME_LINE1 for PL")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("PL")})
.Build())
.SetTriggerField(FieldCondition{
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE1}})
.SetFieldsWithConditionsDoNotExist({
FieldCondition{
.location = FieldLocation::kNextClassifiedSuccessor,
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE2}},
})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type = ADDRESS_HOME_STREET_ADDRESS,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName("Fix consecutive ADDRESS_HOME_LINE1 for IT")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("IT")})
.Build())
.SetTriggerField(FieldCondition{
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE1}})
.SetOtherFieldConditions({
FieldCondition{
.location = FieldLocation::kNextClassifiedSuccessor,
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE1},
},
})
.SetActions({
SetTypeAction{
.target = FieldLocation::kNextClassifiedSuccessor,
.set_overall_type = ADDRESS_HOME_LINE2,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName("Fix ADDRESS_HOME_LINE1 without following "
"ADDRESS_HOME_LINE2 for IT")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("IT")})
.Build())
.SetTriggerField(FieldCondition{
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE1}})
.SetFieldsWithConditionsDoNotExist({
FieldCondition{
.location = FieldLocation::kNextClassifiedSuccessor,
.possible_overall_types =
FieldTypeSet{UNKNOWN_TYPE, ADDRESS_HOME_LINE2}},
})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type = ADDRESS_HOME_STREET_ADDRESS,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName("Fix ADDRESS_HOME_HOUSE_NUMBER_AND_APT for NL")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("NL")})
.Build())
.SetTriggerField(
FieldCondition{.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_HOUSE_NUMBER}})
.SetFieldsWithConditionsDoNotExist({
FieldCondition{
.location = FieldLocation::kNextClassifiedSuccessor,
.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_APT_NUM}},
FieldCondition{
.location = FieldLocation::kLastClassifiedPredecessor,
.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_APT_NUM}},
})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type = ADDRESS_HOME_HOUSE_NUMBER_AND_APT,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName("Fix ADDRESS_HOME_STREET_LOCATION_AND_LOCALITY for IN")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("IN")})
.SetFeature(&features::kAutofillUseINAddressModel)
.Build())
.SetTriggerField(
FieldCondition{.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_LINE1,
ADDRESS_HOME_STREET_LOCATION,
ADDRESS_HOME_STREET_ADDRESS}})
.SetFieldsWithConditionsDoNotExist({FieldCondition{
.location = FieldLocation::kAnywhere,
.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_DEPENDENT_LOCALITY,
ADDRESS_HOME_LINE2}}})
.SetOtherFieldConditions({FieldCondition{
.location = FieldLocation::kAnywhere,
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LANDMARK}}})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type =
ADDRESS_HOME_STREET_LOCATION_AND_LOCALITY,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName("Fix ADDRESS_HOME_STREET_ADDRESS for IN")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("IN")})
.SetFeature(&features::kAutofillUseINAddressModel)
.Build())
.SetTriggerField(FieldCondition{
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LINE1}})
.SetFieldsWithConditionsDoNotExist({FieldCondition{
.location = FieldLocation::kAnywhere,
.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_DEPENDENT_LOCALITY,
ADDRESS_HOME_LINE2, ADDRESS_HOME_LANDMARK}}})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type = ADDRESS_HOME_STREET_ADDRESS,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName(
"Support ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK for IN")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("IN")})
.SetFeature(&features::kAutofillUseINAddressModel)
.Build())
.SetTriggerField(
FieldCondition{.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_DEPENDENT_LOCALITY,
ADDRESS_HOME_STREET_ADDRESS}})
.SetFieldsWithConditionsDoNotExist({FieldCondition{
.location = FieldLocation::kAnywhere,
.possible_overall_types = FieldTypeSet{ADDRESS_HOME_LANDMARK}}})
.SetOtherFieldConditions({FieldCondition{
.location = FieldLocation::kAnywhere,
.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_STREET_LOCATION}}})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type =
ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName(
"Support ADDRESS_HOME_STREET_LOCATION_AND_LANDMARK for IN")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("IN")})
.SetFeature(&features::kAutofillUseINAddressModel)
.Build())
.SetTriggerField(
FieldCondition{.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_STREET_ADDRESS,
ADDRESS_HOME_STREET_LOCATION,
ADDRESS_HOME_LINE1}})
.SetFieldsWithConditionsDoNotExist({FieldCondition{
.location = FieldLocation::kAnywhere,
.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_LANDMARK, ADDRESS_HOME_LINE2}}})
.SetOtherFieldConditions({FieldCondition{
.location = FieldLocation::kAnywhere,
.possible_overall_types =
FieldTypeSet{ADDRESS_HOME_DEPENDENT_LOCALITY}}})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type =
ADDRESS_HOME_STREET_LOCATION_AND_LANDMARK,
},
})
.Build(),
// This rules aim to fix the scenario where both name related fields are
// detected as regular names:
// |name last||name first|
// |name last||name first|->|phonetic family name||phonetic given name|
// The rules for first and last name are split to support also the rare
// case where the name first field precedes the name last field.
RationalizationRuleBuilder()
.SetRuleName("Improve phonetic family names field detection in JP")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("JP")})
.SetFeature(&features::kAutofillSupportPhoneticNameForJP)
.Build())
.SetTriggerField(
FieldCondition{.possible_overall_types =
FieldTypeSet{NAME_LAST, NAME_LAST_SECOND}})
.SetFieldsWithConditionsDoNotExist(
{FieldCondition{.location = FieldLocation::kAnywhere,
.possible_overall_types =
FieldTypeSet{ALTERNATIVE_FAMILY_NAME}}})
.SetOtherFieldConditions(
{FieldCondition{.location = FieldLocation::kPredecessor,
.possible_overall_types =
FieldTypeSet{NAME_LAST, NAME_LAST_SECOND}}})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type = ALTERNATIVE_FAMILY_NAME,
},
})
.Build(),
RationalizationRuleBuilder()
.SetRuleName("Improve phonetic given names field detection in JP")
.SetEnvironmentCondition(
EnvironmentConditionBuilder()
.SetCountryList({GeoIpCountryCode("JP")})
.SetFeature(&features::kAutofillSupportPhoneticNameForJP)
.Build())
.SetTriggerField(FieldCondition{.possible_overall_types =
FieldTypeSet{NAME_FIRST}})
.SetFieldsWithConditionsDoNotExist(
{FieldCondition{.location = FieldLocation::kAnywhere,
.possible_overall_types =
FieldTypeSet{ALTERNATIVE_GIVEN_NAME}}})
.SetOtherFieldConditions({FieldCondition{
.location = FieldLocation::kPredecessor,
.possible_overall_types = FieldTypeSet{NAME_FIRST}}})
.SetActions({
SetTypeAction{
.target = FieldLocation::kTriggerField,
.set_overall_type = ALTERNATIVE_GIVEN_NAME,
},
})
.Build(),
});
};
static const base::NoDestructor<decltype(create_rules())>
kRationalizationRules(create_rules());
for (const RationalizationRule& rule : *kRationalizationRules) {
internal::ApplyRuleIfApplicable(context, rule, fields, log_manager);
}
}
} // namespace autofill::rationalization