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