1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/autofill/core/browser/rationalization_util.h"
6
7 #include "base/logging.h"
8 #include "components/autofill/core/browser/autofill_field.h"
9 #include "components/autofill/core/browser/autofill_type.h"
10
11 namespace autofill {
12 namespace rationalization_util {
13
RationalizePhoneNumberFields(std::vector<AutofillField * > & fields_in_section)14 void RationalizePhoneNumberFields(
15 std::vector<AutofillField*>& fields_in_section) {
16 AutofillField* found_number_field = nullptr;
17 AutofillField* found_number_field_second = nullptr;
18 AutofillField* found_city_code_field = nullptr;
19 AutofillField* found_country_code_field = nullptr;
20 AutofillField* found_city_and_number_field = nullptr;
21 AutofillField* found_whole_number_field = nullptr;
22 bool phone_number_found = false;
23 bool phone_number_separate_fields = false;
24 // Iterate through all given fields. Iteration stops when it first finds a
25 // valid set of fields that can compose a whole number. The |found_*| pointers
26 // will be set to that set of fields when iteration finishes.
27 for (AutofillField* field : fields_in_section) {
28 if (!field->is_focusable)
29 continue;
30 ServerFieldType current_field_type = field->Type().GetStorableType();
31 switch (current_field_type) {
32 case PHONE_HOME_NUMBER:
33 case PHONE_BILLING_NUMBER:
34 if (!found_number_field) {
35 found_number_field = field;
36 if (field->max_length < 5) {
37 phone_number_separate_fields = true;
38 } else {
39 phone_number_found = true;
40 }
41 break;
42 }
43 // If the form has phone number separated into exchange and subscriber
44 // number we mark both of them as number fields.
45 // TODO(wuandy): A less hacky solution to have dedicated enum for
46 // exchange and subscriber number.
47 DCHECK(phone_number_separate_fields);
48 DCHECK(!found_number_field_second);
49 found_number_field_second = field;
50 phone_number_found = true;
51 break;
52 case PHONE_HOME_CITY_CODE:
53 case PHONE_BILLING_CITY_CODE:
54 if (!found_city_code_field)
55 found_city_code_field = field;
56 break;
57 case PHONE_HOME_COUNTRY_CODE:
58 case PHONE_BILLING_COUNTRY_CODE:
59 if (!found_country_code_field)
60 found_country_code_field = field;
61 break;
62 case PHONE_HOME_CITY_AND_NUMBER:
63 case PHONE_BILLING_CITY_AND_NUMBER:
64 DCHECK(!phone_number_found && !found_city_and_number_field);
65 found_city_and_number_field = field;
66 phone_number_found = true;
67 break;
68 case PHONE_HOME_WHOLE_NUMBER:
69 case PHONE_BILLING_WHOLE_NUMBER:
70 DCHECK(!phone_number_found && !found_whole_number_field);
71 found_whole_number_field = field;
72 phone_number_found = true;
73 break;
74 default:
75 break;
76 }
77 if (phone_number_found)
78 break;
79 }
80
81 // The first number of found may be the whole number field, the
82 // city and number field, or neither. But it cannot be both.
83 DCHECK(!(found_whole_number_field && found_city_and_number_field));
84
85 // Prefer to fill the first complete phone number found. The whole number
86 // and city_and_number fields are only set if they represent the first
87 // complete number found; otherwise, a complete number is present as
88 // component input fields. These scenarios are mutually exclusive, so
89 // clean up any inconsistencies.
90 if (found_whole_number_field) {
91 found_number_field = nullptr;
92 found_number_field_second = nullptr;
93 found_city_code_field = nullptr;
94 found_country_code_field = nullptr;
95 } else if (found_city_and_number_field) {
96 found_number_field = nullptr;
97 found_number_field_second = nullptr;
98 found_city_code_field = nullptr;
99 }
100
101 // A second update pass.
102 // At this point, either |phone_number_found| is false and we should do a
103 // best-effort filling for the field whose types we have seen a first time.
104 // Or |phone_number_found| is true and the pointers to the fields that
105 // compose the first valid phone number are set to not-NULL, specifically:
106 // 1. |found_whole_number_field| is not NULL, other pointers set to NULL, or
107 // 2. |found_city_and_number_field| is not NULL, |found_country_code_field| is
108 // probably not NULL, and other pointers set to NULL, or
109 // 3. |found_city_code_field| and |found_number_field| are not NULL,
110 // |found_country_code_field| is probably not NULL, and other pointers are
111 // NULL.
112 // 4. |found_city_code_field|, |found_number_field| and
113 // |found_number_field_second|
114 // are not NULL, |found_country_code_field| is probably not NULL, and other
115 // pointers are NULL.
116
117 // For all above cases, in the update pass, if one field is phone
118 // number related but not one of the found fields from first pass, set their
119 // |only_fill_when_focused| field to true.
120 for (auto it = fields_in_section.begin(); it != fields_in_section.end();
121 ++it) {
122 AutofillField* field = *it;
123 ServerFieldType current_field_type = field->Type().GetStorableType();
124 switch (current_field_type) {
125 case PHONE_HOME_NUMBER:
126 case PHONE_BILLING_NUMBER:
127 if (field != found_number_field && field != found_number_field_second)
128 field->set_only_fill_when_focused(true);
129 break;
130 case PHONE_HOME_CITY_CODE:
131 case PHONE_BILLING_CITY_CODE:
132 if (field != found_city_code_field)
133 field->set_only_fill_when_focused(true);
134 break;
135 case PHONE_HOME_COUNTRY_CODE:
136 case PHONE_BILLING_COUNTRY_CODE:
137 if (field != found_country_code_field)
138 field->set_only_fill_when_focused(true);
139 break;
140 case PHONE_HOME_CITY_AND_NUMBER:
141 case PHONE_BILLING_CITY_AND_NUMBER:
142 if (field != found_city_and_number_field)
143 field->set_only_fill_when_focused(true);
144 break;
145 case PHONE_HOME_WHOLE_NUMBER:
146 case PHONE_BILLING_WHOLE_NUMBER:
147 if (field != found_whole_number_field)
148 field->set_only_fill_when_focused(true);
149 break;
150 default:
151 break;
152 }
153 }
154 }
155
156 } // namespace rationalization_util
157 } // namespace autofill
158