chromium/components/autofill/core/browser/address_suggestion_generator.cc

// Copyright 2021 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/address_suggestion_generator.h"

#include <functional>
#include <string>
#include <vector>

#include "base/check_deref.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/i18n/case_conversion.h"
#include "base/memory/raw_ptr.h"
#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/address_data_manager.h"
#include "components/autofill/core/browser/autofill_browser_util.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_granular_filling_utils.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/autofill_profile_comparator.h"
#include "components/autofill/core/browser/data_model/borrowed_transliterator.h"
#include "components/autofill/core/browser/field_filling_address_util.h"
#include "components/autofill/core/browser/field_type_utils.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/form_parsing/address_field_parser.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/geo/address_i18n.h"
#include "components/autofill/core/browser/geo/phone_number_i18n.h"
#include "components/autofill/core/browser/metrics/autofill_metrics.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/ui/suggestion.h"
#include "components/autofill/core/browser/ui/suggestion_type.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/grit/components_scaled_resources.h"
#include "components/strings/grit/components_strings.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_formatter.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"

#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
#include "ui/native_theme/native_theme.h"  // nogncheck
#endif

namespace autofill {

namespace {

constexpr DenseSet<SuggestionType> kGroupFillingSuggestions =;

Suggestion CreateSeparator() {}

Suggestion CreateUndoOrClearFormSuggestion() {}

bool ShouldUseNationalFormatPhoneNumber(FieldType trigger_field_type) {}

std::u16string GetFormattedPhoneNumber(const AutofillProfile& profile,
                                       const std::string& app_locale,
                                       bool should_use_national_format) {}

// First, check the comment of
// `GetProfileSuggestionMainTextForNonAddressField()`.
//
// To find the position and length of the first address field, the code
// iterates through all of the possible fields which can be part of the result
// of `AutofillProfile::CreateDifferentiatingLabels()`, and checks which one is
// the closest to the beginning of the string.
//
// Note: Right-to-left languages are only displayed from right to left, the
// characters are stored from left to right.
std::pair<size_t, size_t>
GetFirstAddressFieldPositionAndLengthForNonAddressField(
    const AutofillProfile& profile,
    const std::string& app_locale,
    std::u16string_view suggestion_text) {}

// For a profile containing a full address, the main text is the name, and
// the label is the address. The problem arises when a profile isn't complete
// (aka it doesn't have a name or an address etc.).
//
// `AutofillProfile::CreateDifferentiatingLabels()` generates a text which
// contains 2 values from profile.
//
// Example for a full Autofill profile:
// "Full Name, Address"
//
// Examples where Autofill profiles are incomplete:
// "City, Country"
// "Country, Email"
//
// Note: the separator isn't necessarily ", ", it can be an arabic comma, a
// space or no separator at all, depending on the language.
//
// The main text is located at the beginning of the aforementioned strings. In
// order to extract it, we calculate its position into the string and its length
// using `GetFirstAddressFieldPositionAndLengthForNonAddressField()`. We cannot
// split the string by separators because of two reasons: some languages don't
// have any separator, and because address fields can have commas inside them in
// some countries.
//
// The reason why the position is also needed is because in some languages the
// address can start with something different than an address field.
// For example, in Japanese, addresses can start with the "〒" character, which
// is the Japanese postal mark.
std::u16string GetProfileSuggestionMainTextForNonAddressField(
    const AutofillProfile& profile,
    const std::string& app_locale) {}

// Check comment of `GetProfileSuggestionMainTextForNonAddressField()` method.
std::vector<std::u16string> GetProfileSuggestionLabelForNonAddressField(
    const std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>&
        profiles,
    const std::string& app_locale) {}

// In addition to just getting the values out of the profile, this function
// handles type-specific formatting.
std::u16string GetProfileSuggestionMainText(const AutofillProfile& profile,
                                            const std::string& app_locale,
                                            FieldType trigger_field_type) {}

Suggestion GetEditAddressProfileSuggestion(Suggestion::BackendId backend_id) {}

// Creates the suggestion that will open the delete address profile dialog.
Suggestion GetDeleteAddressProfileSuggestion(Suggestion::BackendId backend_id) {}

// Creates the suggestion that will fill all address related fields.
Suggestion GetFillFullAddressSuggestion(Suggestion::BackendId backend_id) {}

// Creates the suggestion that will fill all name related fields.
Suggestion GetFillFullNameSuggestion(Suggestion::BackendId backend_id) {}

// Creates the suggestion that will fill the whole form for the profile.
Suggestion GetFillEverythingFromAddressProfileSuggestion(
    Suggestion::BackendId backend_id) {}

// Append new suggestions to `suggestions` based on the `FieldType` list
// provided. Suggestions are not added if their info is not found in the
// provided `profile`. Returns true if any suggestion was added.
// Note that adding a new field-by-field filling `FieldType` should be
// reflected in `AutofillFieldByFieldFillingTypes`.
bool AddAddressFieldByFieldSuggestions(
    const std::vector<FieldType>& field_types,
    const AutofillProfile& profile,
    const std::string& app_locale,
    std::vector<Suggestion>& suggestions) {}

// Given an address `type` and `sub_type`, returns whether the `sub_type` info
// stored in `profile` is a substring of the info stored in `profile` for
// `type`.
bool CheckIfTypeContainsSubtype(FieldType type,
                                FieldType sub_type,
                                const AutofillProfile& profile,
                                const std::string& app_locale) {}

// Adds name related child suggestions to build autofill popup submenu.
// The param `type` refers to the triggering field type (clicked by the users)
// and is used to define  whether the `SuggestionType::kFillFullName`
// suggestion will be available.
void AddNameChildSuggestions(FieldTypeGroup trigger_field_type_group,
                             const AutofillProfile& profile,
                             const std::string& app_locale,
                             Suggestion& suggestion) {}

// Adds address line suggestions (ADDRESS_HOME_LINE1 and/or
// ADDRESS_HOME_LINE2) to `suggestions.children`. It potentially includes
// sub-children if one of the added suggestions contains
// ADDRESS_HOME_HOUSE_NUMBER and/or ADDRESS_HOME_STREET_NAME. Returns true if at
// least one suggestion was appended to `suggestions.children`.
bool AddAddressLineChildSuggestions(const AutofillProfile& profile,
                                    const std::string& app_locale,
                                    std::vector<Suggestion>& suggestions) {}

// Adds address related child suggestions to build autofill popup submenu.
// The param `trigger_field_type_group` refers to the type of the field clicked
// by the user and is used to define whether the
// `SuggestionType::kFillFullAddress` suggestion will be available. Note that
// `FieldTypeGroup::kCompany` is also included into the address group.
void AddAddressChildSuggestions(FieldTypeGroup trigger_field_type_group,
                                const AutofillProfile& profile,
                                const std::string& app_locale,
                                Suggestion& suggestion) {}

// Adds contact related child suggestions (i.e email and phone number) to
// build autofill popup submenu. The param `trigger_field_type` refers to the
// field clicked by the user and affects whether international or local phone
// number will be shown to the user in the suggestion. The field type group of
// the `trigger_field_type` is used to define whether the phone number and email
// suggestions will behave as `SuggestionType::kAddressFieldByFieldFilling` or
// as
// `SuggestionType::kFillFullPhoneNumber`/`SuggestionType::kFillFullEmail`
// respectively. When the triggering field group matches the type of the field
// we are adding, the suggestion will be of group filling type, other than field
// by field.
void AddContactChildSuggestions(FieldType trigger_field_type,
                                const AutofillProfile& profile,
                                const std::string& app_locale,
                                Suggestion& suggestion) {}

// Adds footer child suggestions for editing and deleting a profile from the
// popup submenu. Note that these footer suggestions are not added in incognito
// mode (`is_off_the_record`).
void AddFooterChildSuggestions(const AutofillProfile& profile,
                               FieldType trigger_field_type,
                               bool is_off_the_record,
                               Suggestion& suggestion) {}

// Creates a specific granular filling label which will be used for each
// `AutofillProfile` in `profiles` for group filling suggestions. This is done
// to give users feedback about the filling behaviour. Returns an empty string
// when no granular filling label needs to be applied for a profile.
std::u16string GetGranularFillingLabels(SuggestionType suggestion_type) {}

// Returns whether the `ADDRESS_HOME_LINE1` should be included into the labels
// of the suggestion. Returns true if `trigger_field_type` is an address field
// (actual address field: ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY, etc.; not
// NAME_FULL or PHONE_HOME_NUMBER) that usually does not allow users to easily
// identify their address.
bool ShouldAddAddressLine1ToSuggestionLabels(FieldType trigger_field_type) {}

// Returns the minimum number of fields that should be returned by
// `AutofillProfile::CreateInferredLabels()`, based on the type of the
// triggering field and on the filling granularity.
int GetNumberOfMinimalFieldsToShow(FieldType trigger_field_type,
                                   SuggestionType suggestion_type) {}

// Returns for each profile in `profiles` a differentiating label string to be
// used as a secondary text in the corresponding suggestion bubble.
// `field_types` the types of the fields that will be filled by the suggestion.
std::vector<std::u16string> GetProfileSuggestionLabels(
    const std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>&
        profiles,
    const FieldTypeSet& field_types,
    FieldType trigger_field_type,
    SuggestionType suggestion_type,
    const std::string& app_locale) {}

// For each profile in `profiles`, returns a vector of `Suggestion::labels` to
// be applied. Takes into account the `suggestion_type` and the
// `trigger_field_type` to add specific granular filling labels. Optionally adds
// a differentiating label if the Suggestion::main_text + granular filling label
// is not unique.
std::vector<std::vector<Suggestion::Text>>
CreateSuggestionLabelsWithGranularFillingDetails(
    const std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>&
        profiles,
    const FieldTypeSet& field_types,
    SuggestionType suggestion_type,
    FieldType trigger_field_type,
    const std::string& app_locale) {}

// Returns whether the `suggestion_canon` is a valid match given
// `field_contents_canon`. To be used for address suggestions
bool IsValidAddressSuggestionForFieldContents(
    std::u16string suggestion_canon,
    std::u16string field_contents_canon,
    FieldType trigger_field_type) {}

// Normalizes text for comparison based on the type of the field `text` was
// entered into.
std::u16string NormalizeForComparisonForType(const std::u16string& text,
                                             FieldType type) {}

std::optional<Suggestion> GetSuggestionForTestAddresses(
    base::span<const AutofillProfile> test_addresses,
    const std::string& locale) {}

// Dedupes the given profiles based on if one is a subset of the other for
// suggestions represented by `field_types`. The function returns at most
// `kMaxDeduplicatedProfilesForSuggestion` profiles. `field_types` stores all
// of the FieldTypes relevant for the current suggestions, including that of
// the field on which the user is currently focused.
std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>
DeduplicatedProfilesForSuggestions(
    const std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>&
        matched_profiles,
    FieldType trigger_field_type,
    const FieldTypeSet& field_types,
    const AutofillProfileComparator& comparator) {}

// Matches based on prefix search, and limits number of profiles.
// Returns the top matching profiles based on prefix search. At most
// `kMaxPrefixMatchedProfilesForSuggestion` are returned.
std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>
GetPrefixMatchedProfiles(const std::vector<const AutofillProfile*>& profiles,
                         FieldType trigger_field_type,
                         const std::u16string& raw_field_contents,
                         const std::u16string& field_contents_canon,
                         bool field_is_autofilled,
                         const std::string& app_locale) {}

// Removes profiles that haven't been used after `kDisusedDataModelTimeDelta`
// from `profiles`. Note that the goal of this filtering strategy is only to
// reduce visual noise for users that have many profiles, and therefore in
// some cases, some disused profiles might be kept in the list, to avoid
// filtering out all profiles, leading to no suggestions being shown. The
// relative ordering of `profiles` is maintained.
void RemoveDisusedSuggestions(
    std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>& profiles) {}

// Creates nested/child suggestions for `suggestion` with the `profile`
// information. Uses `trigger_field_type` to define what group filling
// suggestion to add (name, address or phone). The existence of child
// suggestions defines whether the autofill popup will have submenus.
void AddAddressGranularFillingChildSuggestions(FieldType trigger_field_type,
                                               const AutofillProfile& profile,
                                               Suggestion& suggestion,
                                               bool is_off_the_record,
                                               const std::string& app_locale) {}

// Returns non address suggestions which are displayed below address
// suggestions in the Autofill popup. `is_autofilled` is used to conditionally
// add suggestion for clearing all autofilled fields.
std::vector<Suggestion> GetAddressFooterSuggestions(bool is_autofilled) {}

ProfilesToSuggestOptions GetProfilesToSuggestOptions(
    FieldType trigger_field_type,
    const std::u16string& trigger_field_contents,
    bool trigger_field_is_autofilled,
    AutofillSuggestionTriggerSource trigger_source) {}

// Returns a list of profiles that will be displayed as suggestions to the user,
// sorted by their relevance. This involves many steps from fetching the
// profiles to matching with `field_contents`, and deduplicating based on
// `field_types`, which are the relevant types for the current suggestion.
// `options` defines what strategies to follow by the function in order to
// filter the list or returned profiles.
std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>
GetProfilesToSuggest(const AddressDataManager& address_data,
                     FieldType trigger_field_type,
                     const std::u16string& field_contents,
                     bool field_is_autofilled,
                     const FieldTypeSet& field_types,
                     ProfilesToSuggestOptions options) {}

// Returns a list of Suggestion objects, each representing an element in
// `profiles`.
// `field_types` holds the type of fields relevant for the current suggestion.
// The profiles passed to this function should already have been matched on
// `trigger_field_contents_canon` and deduplicated.
std::vector<Suggestion> CreateSuggestionsFromProfiles(
    const std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>&
        profiles,
    const FieldTypeSet& field_types,
    SuggestionType suggestion_type,
    FieldType trigger_field_type,
    uint64_t trigger_field_max_length,
    bool is_off_the_record,
    const std::string& app_locale) {}

}  // namespace

std::vector<Suggestion> GetSuggestionsForProfiles(
    const AutofillClient& client,
    const FieldTypeSet& field_types,
    const FormFieldData& trigger_field,
    FieldType trigger_field_type,
    SuggestionType suggestion_type,
    AutofillSuggestionTriggerSource trigger_source) {}

Suggestion CreateManageAddressesSuggestion() {}

std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>
GetProfilesToSuggestForTest(const AddressDataManager& address_data,
                            FieldType trigger_field_type,
                            const std::u16string& field_contents,
                            bool field_is_autofilled,
                            const FieldTypeSet& field_types,
                            AutofillSuggestionTriggerSource trigger_source) {}

std::vector<Suggestion> CreateSuggestionsFromProfilesForTest(
    const std::vector<raw_ptr<const AutofillProfile, VectorExperimental>>&
        profiles,
    const FieldTypeSet& field_types,
    SuggestionType suggestion_type,
    FieldType trigger_field_type,
    uint64_t trigger_field_max_length,
    bool is_off_the_record,
    const std::string& app_locale) {}

}  // namespace autofill