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