1 // Copyright 2013 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.
5 #include "components/autofill/core/browser/name_field.h"
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/core/browser/autofill_regex_constants.h"
12 #include "components/autofill/core/browser/autofill_scanner.h"
13 #include "components/autofill/core/browser/autofill_type.h"
14 #include "ui/base/l10n/l10n_util.h"
19 // A form field that can parse a full name field.
20 class FullNameField : public NameField {
22 static FullNameField* Parse(AutofillScanner* scanner);
26 virtual bool ClassifyField(ServerFieldTypeMap* map) const OVERRIDE;
29 explicit FullNameField(const AutofillField* field);
31 const AutofillField* field_;
33 DISALLOW_COPY_AND_ASSIGN(FullNameField);
36 // A form field that can parse a first and last name field.
37 class FirstLastNameField : public NameField {
39 static FirstLastNameField* ParseSpecificName(AutofillScanner* scanner);
40 static FirstLastNameField* ParseComponentNames(AutofillScanner* scanner);
41 static FirstLastNameField* Parse(AutofillScanner* scanner);
45 virtual bool ClassifyField(ServerFieldTypeMap* map) const OVERRIDE;
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.
55 DISALLOW_COPY_AND_ASSIGN(FirstLastNameField);
60 FormField* NameField::Parse(AutofillScanner* scanner) {
64 // Try FirstLastNameField first since it's more specific.
65 NameField* field = FirstLastNameField::Parse(scanner);
67 field = FullNameField::Parse(scanner);
71 // This is overriden in concrete subclasses.
72 bool NameField::ClassifyField(ServerFieldTypeMap* map) const {
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);
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);
95 bool FullNameField::ClassifyField(ServerFieldTypeMap* map) const {
96 return AddClassification(field_, NAME_FULL, map);
99 FullNameField::FullNameField(const AutofillField* field)
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();
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;
130 FirstLastNameField* FirstLastNameField::ParseComponentNames(
131 AutofillScanner* scanner) {
132 scoped_ptr<FirstLastNameField> v(new FirstLastNameField);
133 scanner->SaveCursor();
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,
142 // The ".*first$" matches fields ending in "first" (example in sample8.html).
143 // The ".*last$" matches fields ending in "last" (example in sample8.html).
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)) {
153 if (!v->first_name_ &&
154 ParseField(scanner, UTF8ToUTF16(autofill::kFirstNameRe),
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
164 if (!v->middle_name_ &&
165 ParseField(scanner, UTF8ToUTF16(autofill::kMiddleInitialRe),
167 v->middle_initial_ = true;
171 if (!v->middle_name_ &&
172 ParseField(scanner, UTF8ToUTF16(autofill::kMiddleNameRe),
177 if (!v->last_name_ &&
178 ParseField(scanner, UTF8ToUTF16(autofill::kLastNameRe),
186 // Consider the match to be successful if we detected both first and last name
188 if (v->first_name_ && v->last_name_)
195 FirstLastNameField* FirstLastNameField::Parse(AutofillScanner* scanner) {
196 FirstLastNameField* field = ParseSpecificName(scanner);
198 field = ParseComponentNames(scanner);
202 FirstLastNameField::FirstLastNameField()
206 middle_initial_(false) {
209 bool FirstLastNameField::ClassifyField(ServerFieldTypeMap* map) const {
210 bool ok = AddClassification(first_name_, NAME_FIRST, map);
211 ok = ok && AddClassification(last_name_, NAME_LAST, map);
212 ServerFieldType type = middle_initial_ ? NAME_MIDDLE_INITIAL : NAME_MIDDLE;
213 ok = ok && AddClassification(middle_name_, type, map);
217 } // namespace autofill