Commit 252324d8 authored by Matthias Körber's avatar Matthias Körber Committed by Commit Bot

[Autofill][Leipzig] Add support for address lines

This CL adds support for address lines as a special view on street
addresses.

Change-Id: Ifade7edd874321f7f65788a3091ffdee5199febf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2410017Reviewed-by: default avatarChristoph Schwering <schwering@google.com>
Commit-Queue: Matthias Körber <koerber@google.com>
Cr-Commit-Position: refs/heads/master@{#806953}
parent 5e41d184
......@@ -105,11 +105,125 @@ base::string16 StreetAddress::GetBestFormatString() const {
"${ADDRESS_HOME_FLOOR;FL } ${ADDRESS_HOME_APT_NUM;APT }");
}
void StreetAddress::UnsetValue() {
AddressComponent::UnsetValue();
address_lines_.clear();
}
void StreetAddress::SetValue(base::string16 value, VerificationStatus status) {
AddressComponent::SetValue(value, status);
CalculateAddressLines();
}
void StreetAddress::CalculateAddressLines() {
// Recalculate |address_lines_| after changing the street address.
address_lines_ =
base::SplitString(GetValue(), base::ASCIIToUTF16("\n"),
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
// If splitting of the address line results in more than 3 entries, join the
// additional entries into the third line.
if (address_lines_.size() > 3) {
address_lines_[2] =
base::JoinString(std::vector<base::string16>(address_lines_.begin() + 2,
address_lines_.end()),
base::ASCIIToUTF16(" "));
// Drop the addition address lines.
while (address_lines_.size() > 3)
address_lines_.pop_back();
}
}
bool StreetAddress::IsValueValid() const {
return !base::Contains(address_lines_, base::string16());
}
bool StreetAddress::ConvertAndGetTheValueForAdditionalFieldTypeName(
const std::string& type_name,
base::string16* value) const {
if (type_name == AutofillType(ADDRESS_HOME_LINE1).ToString()) {
if (value) {
*value =
address_lines_.size() > 0 ? address_lines_.at(0) : base::string16();
}
return true;
}
if (type_name == AutofillType(ADDRESS_HOME_LINE2).ToString()) {
if (value) {
*value =
address_lines_.size() > 1 ? address_lines_.at(1) : base::string16();
}
return true;
}
if (type_name == AutofillType(ADDRESS_HOME_LINE3).ToString()) {
if (value) {
*value =
address_lines_.size() > 2 ? address_lines_.at(2) : base::string16();
}
return true;
}
return false;
}
// Implements support for setting the value of the individual address lines.
bool StreetAddress::ConvertAndSetValueForAdditionalFieldTypeName(
const std::string& type_name,
const base::string16& value,
const VerificationStatus& status) {
size_t index = 0;
if (type_name == AutofillType(ADDRESS_HOME_LINE1).ToString()) {
index = 0;
} else if (type_name == AutofillType(ADDRESS_HOME_LINE2).ToString()) {
index = 1;
} else if (type_name == AutofillType(ADDRESS_HOME_LINE3).ToString()) {
index = 2;
} else {
return false;
}
// Make sure that there are three address lines stored.
if (index >= address_lines_.size())
address_lines_.resize(index + 1, base::string16());
bool change = address_lines_[index] != value;
if (change)
address_lines_[index] = value;
while (!address_lines_.empty() && address_lines_.back().empty())
address_lines_.pop_back();
// By calling the base class implementation, the recreation of the address
// lines from the street address is omitted.
if (change) {
AddressComponent::SetValue(
base::JoinString(address_lines_, base::ASCIIToUTF16("\n")), status);
}
return true;
}
void StreetAddress::PostAssignSanitization() {
CalculateAddressLines();
}
void StreetAddress::GetAdditionalSupportedFieldTypes(
ServerFieldTypeSet* supported_types) const {
supported_types->insert(ADDRESS_HOME_LINE1);
supported_types->insert(ADDRESS_HOME_LINE2);
supported_types->insert(ADDRESS_HOME_LINE3);
}
CountryCode::CountryCode(AddressComponent* parent)
: AddressComponent(ADDRESS_HOME_COUNTRY, parent) {}
CountryCode::~CountryCode() = default;
DependentLocality::DependentLocality(AddressComponent* parent)
: AddressComponent(ADDRESS_HOME_DEPENDENT_LOCALITY, parent) {}
DependentLocality::~DependentLocality() = default;
City::City(AddressComponent* parent)
: AddressComponent(ADDRESS_HOME_CITY, parent) {}
......@@ -125,16 +239,36 @@ PostalCode::PostalCode(AddressComponent* parent)
PostalCode::~PostalCode() = default;
SortingCode::SortingCode(AddressComponent* parent)
: AddressComponent(ADDRESS_HOME_SORTING_CODE, parent) {}
SortingCode::~SortingCode() = default;
Address::Address() : Address{nullptr} {}
Address::Address(const Address& other) : Address() {
*this = other;
}
Address::Address(AddressComponent* parent)
: AddressComponent(
ADDRESS_HOME_ADDRESS,
parent,
{&street_address_, &postal_code_, &city_, &state_, &country_code_}) {}
{&street_address_, &postal_code_, &sorting_code_,
&dependent_locality_, &city_, &state_, &country_code_}) {}
Address::~Address() = default;
void Address::MigrateLegacyStructure(bool is_verified_profile) {
VerificationStatus status = is_verified_profile
? VerificationStatus::kUserVerified
: VerificationStatus::kObserved;
if (GetVerificationStatus() == VerificationStatus::kNoStatus &&
!GetValue().empty()) {
SetValue(GetValue(), status);
}
}
} // namespace structured_address
} // namespace autofill
......@@ -88,6 +88,13 @@ class StreetAddress : public AddressComponent {
explicit StreetAddress(AddressComponent* parent);
~StreetAddress() override;
void GetAdditionalSupportedFieldTypes(
ServerFieldTypeSet* supported_types) const override;
void SetValue(base::string16 value, VerificationStatus status) override;
void UnsetValue() override;
protected:
std::vector<const re2::RE2*> GetParseRegularExpressionsByRelevance()
const override;
......@@ -95,11 +102,36 @@ class StreetAddress : public AddressComponent {
// Returns the format string to create the full name from its subcomponents.
base::string16 GetBestFormatString() const override;
// Recalculates the address line after an assignment.
void PostAssignSanitization() override;
protected:
// Implements support for getting the value of the individual address lines.
bool ConvertAndGetTheValueForAdditionalFieldTypeName(
const std::string& type_name,
base::string16* value) const override;
// Implements support for setting the value of the individual address lines.
bool ConvertAndSetValueForAdditionalFieldTypeName(
const std::string& type_name,
const base::string16& value,
const VerificationStatus& status) override;
// Returns true of the address lines do not contain an empty line.
bool IsValueValid() const override;
private:
// Calculates the address line from the street address.
void CalculateAddressLines();
StreetAndDependentStreetName streets_{this};
HouseNumber number_{this};
Premise premise_{this};
SubPremise sub_premise_{this};
// Holds the values of the individual address lines.
// Must be recalculated if the value of the component changes.
std::vector<base::string16> address_lines_;
};
// Stores the country code of an address profile.
......@@ -109,6 +141,13 @@ class CountryCode : public AddressComponent {
~CountryCode() override;
};
// Stores the city of an address.
class DependentLocality : public AddressComponent {
public:
explicit DependentLocality(AddressComponent* parent);
~DependentLocality() override;
};
// Stores the city of an address.
class City : public AddressComponent {
public:
......@@ -130,17 +169,31 @@ class PostalCode : public AddressComponent {
~PostalCode() override;
};
// Stores the sorting code.
class SortingCode : public AddressComponent {
public:
explicit SortingCode(AddressComponent* parent);
~SortingCode() override;
};
// Stores the overall Address that contains the StreetAddress, the PostalCode
// the City, the State and the CountryCode.
class Address : public AddressComponent {
public:
Address();
Address(const Address& other);
explicit Address(AddressComponent* parent);
~Address() override;
// Migrates from a legacy structure in which name tokens are imported without
// a status.
void MigrateLegacyStructure(bool is_verified_profile);
private:
StreetAddress street_address_{this};
PostalCode postal_code_{this};
SortingCode sorting_code_{this};
DependentLocality dependent_locality_{this};
City city_{this};
State state_{this};
CountryCode country_code_{this};
......
......@@ -5,7 +5,6 @@
#include "components/autofill/core/browser/data_model/autofill_structured_address_component.h"
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <utility>
......@@ -70,6 +69,8 @@ AddressComponent& AddressComponent::operator=(const AddressComponent& right) {
for (size_t i = 0; i < right.subcomponents_.size(); i++)
*subcomponents_[i] = *right.subcomponents_[i];
PostAssignSanitization();
return *this;
}
......@@ -80,9 +81,10 @@ bool AddressComponent::operator==(const AddressComponent& right) const {
if (GetStorageType() != right.GetStorageType())
return false;
if (value_ != right.value_ ||
value_verification_status_ != right.value_verification_status_)
if (GetValue() != right.GetValue() ||
value_verification_status_ != right.value_verification_status_) {
return false;
}
DCHECK(right.subcomponents_.size() == subcomponents_.size());
for (size_t i = 0; i < right.subcomponents_.size(); i++)
......@@ -99,6 +101,43 @@ bool AddressComponent::IsAtomic() const {
return subcomponents_.empty();
}
bool AddressComponent::IsValueValid() const {
return true;
}
bool AddressComponent::IsValueForTypeValid(const std::string& field_type_name,
bool wipe_if_not) {
bool validity_status;
if (GetIsValueForTypeValidIfPossible(field_type_name, &validity_status,
wipe_if_not))
return validity_status;
return false;
}
bool AddressComponent::IsValueForTypeValid(ServerFieldType field_type,
bool wipe_if_not) {
return IsValueForTypeValid(AutofillType(field_type).ToString(), wipe_if_not);
}
bool AddressComponent::GetIsValueForTypeValidIfPossible(
const std::string& field_type_name,
bool* validity_status,
bool wipe_if_not) {
if (field_type_name == GetStorageTypeName()) {
*validity_status = IsValueValid();
if (!(*validity_status) && wipe_if_not)
UnsetValue();
return true;
}
for (auto* subcomponent : subcomponents_) {
if (subcomponent->GetIsValueForTypeValidIfPossible(
field_type_name, validity_status, wipe_if_not))
return true;
}
return false;
}
VerificationStatus AddressComponent::GetVerificationStatus() const {
return value_verification_status_;
}
......@@ -188,6 +227,17 @@ bool AddressComponent::SetValueForTypeIfPossible(
invalidate_parent_nodes);
}
bool AddressComponent::SetValueForTypeIfPossible(
const ServerFieldType& type,
const std::string& value,
const VerificationStatus& verification_status,
bool invalidate_child_nodes,
bool invalidate_parent_nodes) {
return SetValueForTypeIfPossible(type, base::UTF8ToUTF16(value),
verification_status, invalidate_child_nodes,
invalidate_parent_nodes);
}
bool AddressComponent::SetValueForTypeIfPossible(
const std::string& type_name,
const base::string16& value,
......@@ -226,6 +276,17 @@ bool AddressComponent::SetValueForTypeIfPossible(
return false;
}
bool AddressComponent::SetValueForTypeIfPossible(
const std::string& type_name,
const std::string& value,
const VerificationStatus& verification_status,
bool invalidate_child_nodes,
bool invalidate_parent_nodes) {
return SetValueForTypeIfPossible(type_name, base::UTF8ToUTF16(value),
verification_status, invalidate_child_nodes,
invalidate_parent_nodes);
}
void AddressComponent::UnsetAddressComponentAndItsSubcomponents() {
UnsetValue();
UnsetSubcomponents();
......@@ -283,8 +344,8 @@ base::string16 AddressComponent::GetValueForType(
base::string16 value;
bool success = GetValueAndStatusForTypeIfPossible(type_name, &value, nullptr);
// TODO(crbug.com/1113617): Honorifics are temporally disabled.
DCHECK(success ||
type_name == AutofillType(NAME_HONORIFIC_PREFIX).ToString());
DCHECK(success || type_name == AutofillType(NAME_HONORIFIC_PREFIX).ToString())
<< type_name;
return value;
}
......
......@@ -129,11 +129,11 @@ class AddressComponent {
bool IsValueAssigned() const;
// Sets the value corresponding to the storage type of this AddressComponent.
void SetValue(base::string16 value, VerificationStatus status);
virtual void SetValue(base::string16 value, VerificationStatus status);
// Sets the value to an empty string, marks it unassigned and sets the
// verification status to |kNoStatus|.
void UnsetValue();
virtual void UnsetValue();
// The method sets the value of the current node if its |storage_type_| is
// |type| or if |ConvertAndGetTheValueForAdditionalFieldTypeName()| supports
......@@ -158,6 +158,20 @@ class AddressComponent {
bool invalidate_child_nodes = false,
bool invalidate_parent_nodes = false);
// Convenience wrapper to allow setting the value using a std::string.
bool SetValueForTypeIfPossible(const ServerFieldType& type,
const std::string& value,
const VerificationStatus& verification_status,
bool invalidate_child_nodes = false,
bool invalidate_parent_nodes = false);
// Convenience wrapper to allow setting the value using a std::string.
bool SetValueForTypeIfPossible(const std::string& type_name,
const std::string& value,
const VerificationStatus& verification_status,
bool invalidate_child_nodes = false,
bool invalidate_parent_nodes = false);
// Convenience method to get the value of |type|.
// Returns an empty string if |type| is not supported.
base::string16 GetValueForType(const ServerFieldType& type) const;
......@@ -222,7 +236,7 @@ class AddressComponent {
// Completes the full tree by calling |RecursivelyCompleteTree()| starting
// form the root node. Returns true if the completion was successful.
bool CompleteFullTree();
virtual bool CompleteFullTree();
// Checks if a tree is completable in the sense that there are no conflicting
// observed or verified types. This means that there is not more than one
......@@ -279,6 +293,23 @@ class AddressComponent {
// Recursively unsets all subcomponents.
void RecursivelyUnsetSubcomponents();
// Return if the value associated with |field_type_name| is valid.
// If |wipe_if_not|, the value is unset if invalid.
bool IsValueForTypeValid(const std::string& field_type_name,
bool wipe_if_not = false);
// Convenience wrapper to work the ServerFieldTypes.
bool IsValueForTypeValid(ServerFieldType field_type,
bool wipe_if_not = false);
// Recursively determines the validity status of a component value associated
// with |field_type_name|. If |wipe_if_not|, the value is unset if invalid.
// Returns true if it is possible to determine the validity status of the
// value in this subcomponent.
bool GetIsValueForTypeValidIfPossible(const std::string& field_type_name,
bool* validity_status,
bool wipe_if_not = false);
#ifdef UNIT_TEST
// Initiates the formatting of the values from the subcomponents.
void FormatValueFromSubcomponentsForTesting() {
......@@ -379,6 +410,13 @@ class AddressComponent {
// Returns a reference to the root node of the tree.
const AddressComponent& GetRootNode() const;
// Function to determine if the value stored in this component is valid.
// Return true be default but can be overloaded by a subclass.
virtual bool IsValueValid() const;
// Function to be called post assign to do sanitization.
virtual void PostAssignSanitization() {}
private:
// Unsets the node and all of its children.
void UnsetAddressComponentAndItsSubcomponents();
......
......@@ -43,7 +43,7 @@ class NameMiddle : public AddressComponent {
ServerFieldTypeSet* supported_types) const override;
protected:
// Implements support for getting for a value for the |MIDDLE_NAME_INITIAL|
// Implements support for getting the value for the |MIDDLE_NAME_INITIAL|
// type.
bool ConvertAndGetTheValueForAdditionalFieldTypeName(
const std::string& type_name,
......
......@@ -207,6 +207,114 @@ TEST(AutofillStructuredAddress, TestStreetAddressFormatting) {
TestAddressLineFormatting(test_case);
}
// Test setting the first address line.
TEST(AutofillStructuredAddress, TestSettingsAddressLine1) {
Address address;
AddressComponentTestValues test_values = {
{.type = ADDRESS_HOME_LINE1,
.value = "line1",
.status = VerificationStatus::kObserved}};
SetTestValues(&address, test_values);
AddressComponentTestValues expectation = {
{.type = ADDRESS_HOME_LINE1,
.value = "line1",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_STREET_ADDRESS,
.value = "line1",
.status = VerificationStatus::kObserved}};
VerifyTestValues(&address, expectation);
}
// Test settings all three address lines.
TEST(AutofillStructuredAddress, TestSettingsAddressLines) {
Address address;
AddressComponentTestValues test_values = {
{.type = ADDRESS_HOME_LINE1,
.value = "line1",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_LINE2,
.value = "line2",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_LINE3,
.value = "line3",
.status = VerificationStatus::kObserved}};
SetTestValues(&address, test_values);
AddressComponentTestValues expectation = {
{.type = ADDRESS_HOME_LINE1,
.value = "line1",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_LINE2,
.value = "line2",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_LINE3,
.value = "line3",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_STREET_ADDRESS,
.value = "line1\nline2\nline3",
.status = VerificationStatus::kObserved}};
VerifyTestValues(&address, expectation);
}
// Test setting the home street address and retrieving the address lines.
TEST(AutofillStructuredAddress, TestGettingAddressLines) {
Address address;
AddressComponentTestValues test_values = {
{.type = ADDRESS_HOME_STREET_ADDRESS,
.value = "line1\nline2\nline3",
.status = VerificationStatus::kObserved}};
SetTestValues(&address, test_values);
AddressComponentTestValues expectation = {
{.type = ADDRESS_HOME_LINE1,
.value = "line1",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_LINE2,
.value = "line2",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_LINE3,
.value = "line3",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_STREET_ADDRESS,
.value = "line1\nline2\nline3",
.status = VerificationStatus::kObserved}};
VerifyTestValues(&address, expectation);
}
// Test setting the home street address and retrieving the address lines.
TEST(AutofillStructuredAddress, TestGettingAddressLines_JoinedAdditionalLines) {
Address address;
AddressComponentTestValues test_values = {
{.type = ADDRESS_HOME_STREET_ADDRESS,
.value = "line1\nline2\nline3\nline4",
.status = VerificationStatus::kObserved}};
SetTestValues(&address, test_values);
AddressComponentTestValues expectation = {
{.type = ADDRESS_HOME_LINE1,
.value = "line1",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_LINE2,
.value = "line2",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_LINE3,
.value = "line3 line4",
.status = VerificationStatus::kObserved},
{.type = ADDRESS_HOME_STREET_ADDRESS,
.value = "line1\nline2\nline3\nline4",
.status = VerificationStatus::kObserved}};
VerifyTestValues(&address, expectation);
}
} // namespace
} // namespace structured_address
} // namespace autofill
......@@ -41,6 +41,11 @@ bool StructuredNamesEnabled() {
features::kAutofillEnableSupportForMoreStructureInNames);
}
bool StructuredAddressesEnabled() {
return base::FeatureList::IsEnabled(
features::kAutofillEnableSupportForMoreStructureInAddresses);
}
Re2RegExCache::Re2RegExCache() = default;
// static
......
......@@ -93,6 +93,9 @@ struct CaptureOptions {
// Returns true if the structured names feature is enabled.
bool StructuredNamesEnabled();
// Returns true if the structured address feature is enabled.
bool StructuredAddressesEnabled();
// A cache for compiled RE2 regular expressions.
class Re2RegExCache {
public:
......
......@@ -97,6 +97,12 @@ const base::Feature kAutofillEnableSupportForMoreStructureInNames{
"AutofillEnableSupportForMoreStructureInNames",
base::FEATURE_DISABLED_BY_DEFAULT};
// Controls if Autofill supports new structure in addresses.
// TODO(crbug.com/1098943): Remove once launched.
const base::Feature kAutofillEnableSupportForMoreStructureInAddresses{
"AutofillEnableSupportForMoreStructureInAddresses",
base::FEATURE_DISABLED_BY_DEFAULT};
// Controls if Autofill supports merging subset names.
// TODO(crbug.com/1098943): Remove once launched.
const base::Feature kAutofillEnableSupportForMergingSubsetNames{
......
......@@ -35,6 +35,7 @@ extern const base::Feature kAutofillEnableAugmentedPhoneCountryCode;
extern const base::Feature kAutofillEnableCompanyName;
extern const base::Feature kAutofillEnableHideSuggestionsUI;
extern const base::Feature kAutofillEnableSupportForMoreStructureInNames;
extern const base::Feature kAutofillEnableSupportForMoreStructureInAddresses;
extern const base::Feature kAutofillEnableSupportForMergingSubsetNames;
extern const base::Feature kAutofillEnableSupportForHouseNumbers;
extern const base::Feature kAutofillEnforceMinRequiredFieldsForHeuristics;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment