blob: 45342f7a6ca338b37cc45a3b3eb967e557ab31e6 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// 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/rationalization_util.h"
#include "base/logging.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_type.h"
namespace autofill {
namespace rationalization_util {
void RationalizePhoneNumberFields(
std::vector<AutofillField*>& fields_in_section) {
AutofillField* found_number_field = nullptr;
AutofillField* found_number_field_second = nullptr;
AutofillField* found_city_code_field = nullptr;
AutofillField* found_country_code_field = nullptr;
AutofillField* found_city_and_number_field = nullptr;
AutofillField* found_whole_number_field = nullptr;
bool phone_number_found = false;
bool phone_number_separate_fields = false;
// Iterate through all given fields. Iteration stops when it first finds a
// valid set of fields that can compose a whole number. The |found_*| pointers
// will be set to that set of fields when iteration finishes.
for (AutofillField* field : fields_in_section) {
if (!field->is_focusable)
continue;
ServerFieldType current_field_type = field->Type().GetStorableType();
switch (current_field_type) {
case PHONE_HOME_NUMBER:
case PHONE_BILLING_NUMBER:
if (!found_number_field) {
found_number_field = field;
if (field->max_length < 5) {
phone_number_separate_fields = true;
} else {
phone_number_found = true;
}
break;
}
// If the form has phone number separated into exchange and subscriber
// number we mark both of them as number fields.
// TODO(wuandy): A less hacky solution to have dedicated enum for
// exchange and subscriber number.
DCHECK(phone_number_separate_fields);
DCHECK(!found_number_field_second);
found_number_field_second = field;
phone_number_found = true;
break;
case PHONE_HOME_CITY_CODE:
case PHONE_BILLING_CITY_CODE:
if (!found_city_code_field)
found_city_code_field = field;
break;
case PHONE_HOME_COUNTRY_CODE:
case PHONE_BILLING_COUNTRY_CODE:
if (!found_country_code_field)
found_country_code_field = field;
break;
case PHONE_HOME_CITY_AND_NUMBER:
case PHONE_BILLING_CITY_AND_NUMBER:
DCHECK(!phone_number_found && !found_city_and_number_field);
found_city_and_number_field = field;
phone_number_found = true;
break;
case PHONE_HOME_WHOLE_NUMBER:
case PHONE_BILLING_WHOLE_NUMBER:
DCHECK(!phone_number_found && !found_whole_number_field);
found_whole_number_field = field;
phone_number_found = true;
break;
default:
break;
}
if (phone_number_found)
break;
}
// The first number of found may be the whole number field, the
// city and number field, or neither. But it cannot be both.
DCHECK(!(found_whole_number_field && found_city_and_number_field));
// Prefer to fill the first complete phone number found. The whole number
// and city_and_number fields are only set if they represent the first
// complete number found; otherwise, a complete number is present as
// component input fields. These scenarios are mutually exclusive, so
// clean up any inconsistencies.
if (found_whole_number_field) {
found_number_field = nullptr;
found_number_field_second = nullptr;
found_city_code_field = nullptr;
found_country_code_field = nullptr;
} else if (found_city_and_number_field) {
found_number_field = nullptr;
found_number_field_second = nullptr;
found_city_code_field = nullptr;
}
// A second update pass.
// At this point, either |phone_number_found| is false and we should do a
// best-effort filling for the field whose types we have seen a first time.
// Or |phone_number_found| is true and the pointers to the fields that
// compose the first valid phone number are set to not-NULL, specifically:
// 1. |found_whole_number_field| is not NULL, other pointers set to NULL, or
// 2. |found_city_and_number_field| is not NULL, |found_country_code_field| is
// probably not NULL, and other pointers set to NULL, or
// 3. |found_city_code_field| and |found_number_field| are not NULL,
// |found_country_code_field| is probably not NULL, and other pointers are
// NULL.
// 4. |found_city_code_field|, |found_number_field| and
// |found_number_field_second|
// are not NULL, |found_country_code_field| is probably not NULL, and other
// pointers are NULL.
// For all above cases, in the update pass, if one field is phone
// number related but not one of the found fields from first pass, set their
// |only_fill_when_focused| field to true.
for (auto it = fields_in_section.begin(); it != fields_in_section.end();
++it) {
AutofillField* field = *it;
ServerFieldType current_field_type = field->Type().GetStorableType();
switch (current_field_type) {
case PHONE_HOME_NUMBER:
case PHONE_BILLING_NUMBER:
if (field != found_number_field && field != found_number_field_second)
field->set_only_fill_when_focused(true);
break;
case PHONE_HOME_CITY_CODE:
case PHONE_BILLING_CITY_CODE:
if (field != found_city_code_field)
field->set_only_fill_when_focused(true);
break;
case PHONE_HOME_COUNTRY_CODE:
case PHONE_BILLING_COUNTRY_CODE:
if (field != found_country_code_field)
field->set_only_fill_when_focused(true);
break;
case PHONE_HOME_CITY_AND_NUMBER:
case PHONE_BILLING_CITY_AND_NUMBER:
if (field != found_city_and_number_field)
field->set_only_fill_when_focused(true);
break;
case PHONE_HOME_WHOLE_NUMBER:
case PHONE_BILLING_WHOLE_NUMBER:
if (field != found_whole_number_field)
field->set_only_fill_when_focused(true);
break;
default:
break;
}
}
}
} // namespace rationalization_util
} // namespace autofill