OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/browser/name_field.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "base/strings/string_util.h" | |
10 #include "base/strings/utf_string_conversions.h" | |
11 #include "components/autofill/browser/autofill_regex_constants.h" | |
12 #include "components/autofill/browser/autofill_scanner.h" | |
13 #include "components/autofill/browser/autofill_type.h" | |
14 #include "ui/base/l10n/l10n_util.h" | |
15 | |
16 namespace autofill { | |
17 namespace { | |
18 | |
19 // A form field that can parse a full name field. | |
20 class FullNameField : public NameField { | |
21 public: | |
22 static FullNameField* Parse(AutofillScanner* scanner); | |
23 | |
24 protected: | |
25 // FormField: | |
26 virtual bool ClassifyField(FieldTypeMap* map) const OVERRIDE; | |
27 | |
28 private: | |
29 explicit FullNameField(const AutofillField* field); | |
30 | |
31 const AutofillField* field_; | |
32 | |
33 DISALLOW_COPY_AND_ASSIGN(FullNameField); | |
34 }; | |
35 | |
36 // A form field that can parse a first and last name field. | |
37 class FirstLastNameField : public NameField { | |
38 public: | |
39 static FirstLastNameField* ParseSpecificName(AutofillScanner* scanner); | |
40 static FirstLastNameField* ParseComponentNames(AutofillScanner* scanner); | |
41 static FirstLastNameField* Parse(AutofillScanner* scanner); | |
42 | |
43 protected: | |
44 // FormField: | |
45 virtual bool ClassifyField(FieldTypeMap* map) const OVERRIDE; | |
46 | |
47 private: | |
48 FirstLastNameField(); | |
49 | |
50 const AutofillField* first_name_; | |
51 const AutofillField* middle_name_; // Optional. | |
52 const AutofillField* last_name_; | |
53 bool middle_initial_; // True if middle_name_ is a middle initial. | |
54 | |
55 DISALLOW_COPY_AND_ASSIGN(FirstLastNameField); | |
56 }; | |
57 | |
58 } // namespace | |
59 | |
60 FormField* NameField::Parse(AutofillScanner* scanner) { | |
61 if (scanner->IsEnd()) | |
62 return NULL; | |
63 | |
64 // Try FirstLastNameField first since it's more specific. | |
65 NameField* field = FirstLastNameField::Parse(scanner); | |
66 if (!field) | |
67 field = FullNameField::Parse(scanner); | |
68 return field; | |
69 } | |
70 | |
71 // This is overriden in concrete subclasses. | |
72 bool NameField::ClassifyField(FieldTypeMap* map) const { | |
73 return false; | |
74 } | |
75 | |
76 FullNameField* FullNameField::Parse(AutofillScanner* scanner) { | |
77 // Exclude e.g. "username" or "nickname" fields. | |
78 scanner->SaveCursor(); | |
79 bool should_ignore = ParseField(scanner, | |
80 UTF8ToUTF16(autofill::kNameIgnoredRe), NULL); | |
81 scanner->Rewind(); | |
82 if (should_ignore) | |
83 return NULL; | |
84 | |
85 // Searching for any label containing the word "name" is too general; | |
86 // for example, Travelocity_Edit travel profile.html contains a field | |
87 // "Travel Profile Name". | |
88 const AutofillField* field = NULL; | |
89 if (ParseField(scanner, UTF8ToUTF16(autofill::kNameRe), &field)) | |
90 return new FullNameField(field); | |
91 | |
92 return NULL; | |
93 } | |
94 | |
95 bool FullNameField::ClassifyField(FieldTypeMap* map) const { | |
96 return AddClassification(field_, NAME_FULL, map); | |
97 } | |
98 | |
99 FullNameField::FullNameField(const AutofillField* field) | |
100 : field_(field) { | |
101 } | |
102 | |
103 FirstLastNameField* FirstLastNameField::ParseSpecificName( | |
104 AutofillScanner* scanner) { | |
105 // Some pages (e.g. Overstock_comBilling.html, SmithsonianCheckout.html) | |
106 // have the label "Name" followed by two or three text fields. | |
107 scoped_ptr<FirstLastNameField> v(new FirstLastNameField); | |
108 scanner->SaveCursor(); | |
109 | |
110 const AutofillField* next = NULL; | |
111 if (ParseField(scanner, | |
112 UTF8ToUTF16(autofill::kNameSpecificRe), &v->first_name_) && | |
113 ParseEmptyLabel(scanner, &next)) { | |
114 if (ParseEmptyLabel(scanner, &v->last_name_)) { | |
115 // There are three name fields; assume that the middle one is a | |
116 // middle initial (it is, at least, on SmithsonianCheckout.html). | |
117 v->middle_name_ = next; | |
118 v->middle_initial_ = true; | |
119 } else { // only two name fields | |
120 v->last_name_ = next; | |
121 } | |
122 | |
123 return v.release(); | |
124 } | |
125 | |
126 scanner->Rewind(); | |
127 return NULL; | |
128 } | |
129 | |
130 FirstLastNameField* FirstLastNameField::ParseComponentNames( | |
131 AutofillScanner* scanner) { | |
132 scoped_ptr<FirstLastNameField> v(new FirstLastNameField); | |
133 scanner->SaveCursor(); | |
134 | |
135 // A fair number of pages use the names "fname" and "lname" for naming | |
136 // first and last name fields (examples from the test suite: | |
137 // BESTBUY_COM - Sign In2.html; Crate and Barrel Check Out.html; | |
138 // dell_checkout1.html). At least one UK page (The China Shop2.html) | |
139 // asks, in stuffy English style, for just initials and a surname, | |
140 // so we match "initials" here (and just fill in a first name there, | |
141 // American-style). | |
142 // The ".*first$" matches fields ending in "first" (example in sample8.html). | |
143 // The ".*last$" matches fields ending in "last" (example in sample8.html). | |
144 | |
145 // Allow name fields to appear in any order. | |
146 while (!scanner->IsEnd()) { | |
147 // Skip over any unrelated fields, e.g. "username" or "nickname". | |
148 if (ParseFieldSpecifics(scanner, UTF8ToUTF16(autofill::kNameIgnoredRe), | |
149 MATCH_DEFAULT | MATCH_SELECT, NULL)) { | |
150 continue; | |
151 } | |
152 | |
153 if (!v->first_name_ && | |
154 ParseField(scanner, UTF8ToUTF16(autofill::kFirstNameRe), | |
155 &v->first_name_)) { | |
156 continue; | |
157 } | |
158 | |
159 // We check for a middle initial before checking for a middle name | |
160 // because at least one page (PC Connection.html) has a field marked | |
161 // as both (the label text is "MI" and the element name is | |
162 // "txtmiddlename"); such a field probably actually represents a | |
163 // middle initial. | |
164 if (!v->middle_name_ && | |
165 ParseField(scanner, UTF8ToUTF16(autofill::kMiddleInitialRe), | |
166 &v->middle_name_)) { | |
167 v->middle_initial_ = true; | |
168 continue; | |
169 } | |
170 | |
171 if (!v->middle_name_ && | |
172 ParseField(scanner, UTF8ToUTF16(autofill::kMiddleNameRe), | |
173 &v->middle_name_)) { | |
174 continue; | |
175 } | |
176 | |
177 if (!v->last_name_ && | |
178 ParseField(scanner, UTF8ToUTF16(autofill::kLastNameRe), | |
179 &v->last_name_)) { | |
180 continue; | |
181 } | |
182 | |
183 break; | |
184 } | |
185 | |
186 // Consider the match to be successful if we detected both first and last name | |
187 // fields. | |
188 if (v->first_name_ && v->last_name_) | |
189 return v.release(); | |
190 | |
191 scanner->Rewind(); | |
192 return NULL; | |
193 } | |
194 | |
195 FirstLastNameField* FirstLastNameField::Parse(AutofillScanner* scanner) { | |
196 FirstLastNameField* field = ParseSpecificName(scanner); | |
197 if (!field) | |
198 field = ParseComponentNames(scanner); | |
199 return field; | |
200 } | |
201 | |
202 FirstLastNameField::FirstLastNameField() | |
203 : first_name_(NULL), | |
204 middle_name_(NULL), | |
205 last_name_(NULL), | |
206 middle_initial_(false) { | |
207 } | |
208 | |
209 bool FirstLastNameField::ClassifyField(FieldTypeMap* map) const { | |
210 bool ok = AddClassification(first_name_, NAME_FIRST, map); | |
211 ok = ok && AddClassification(last_name_, NAME_LAST, map); | |
212 AutofillFieldType type = middle_initial_ ? NAME_MIDDLE_INITIAL : NAME_MIDDLE; | |
213 ok = ok && AddClassification(middle_name_, type, map); | |
214 return ok; | |
215 } | |
216 | |
217 } // namespace autofill | |
OLD | NEW |