Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / autofill / core / browser / name_field.cc
blob8532de13c3a1b1f7bd25d04c3a3b90fdd7856a2a
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/memory/scoped_ptr.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "components/autofill/core/browser/autofill_regex_constants.h"
11 #include "components/autofill/core/browser/autofill_scanner.h"
12 #include "components/autofill/core/browser/autofill_type.h"
14 using base::UTF8ToUTF16;
16 namespace autofill {
17 namespace {
19 // A form field that can parse a full name field.
20 class FullNameField : public NameField {
21 public:
22 static scoped_ptr<FullNameField> Parse(AutofillScanner* scanner);
24 protected:
25 // FormField:
26 bool ClassifyField(ServerFieldTypeMap* map) const override;
28 private:
29 explicit FullNameField(AutofillField* field);
31 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 {
38 public:
39 static scoped_ptr<FirstLastNameField> ParseSpecificName(
40 AutofillScanner* scanner);
41 static scoped_ptr<FirstLastNameField> ParseComponentNames(
42 AutofillScanner* scanner);
43 static scoped_ptr<FirstLastNameField> Parse(AutofillScanner* scanner);
45 protected:
46 // FormField:
47 bool ClassifyField(ServerFieldTypeMap* map) const override;
49 private:
50 FirstLastNameField();
52 AutofillField* first_name_;
53 AutofillField* middle_name_; // Optional.
54 AutofillField* last_name_;
55 bool middle_initial_; // True if middle_name_ is a middle initial.
57 DISALLOW_COPY_AND_ASSIGN(FirstLastNameField);
60 } // namespace
62 // static
63 scoped_ptr<FormField> NameField::Parse(AutofillScanner* scanner) {
64 if (scanner->IsEnd())
65 return NULL;
67 // Try FirstLastNameField first since it's more specific.
68 scoped_ptr<FormField> field = FirstLastNameField::Parse(scanner);
69 if (!field)
70 field = FullNameField::Parse(scanner);
71 return field;
74 // This is overriden in concrete subclasses.
75 bool NameField::ClassifyField(ServerFieldTypeMap* map) const {
76 return false;
79 // static
80 scoped_ptr<FullNameField> FullNameField::Parse(AutofillScanner* scanner) {
81 // Exclude e.g. "username" or "nickname" fields.
82 scanner->SaveCursor();
83 bool should_ignore = ParseField(scanner, UTF8ToUTF16(kNameIgnoredRe), NULL);
84 scanner->Rewind();
85 if (should_ignore)
86 return NULL;
88 // Searching for any label containing the word "name" is too general;
89 // for example, Travelocity_Edit travel profile.html contains a field
90 // "Travel Profile Name".
91 AutofillField* field = NULL;
92 if (ParseField(scanner, UTF8ToUTF16(kNameRe), &field))
93 return make_scoped_ptr(new FullNameField(field));
95 return NULL;
98 bool FullNameField::ClassifyField(ServerFieldTypeMap* map) const {
99 return AddClassification(field_, NAME_FULL, map);
102 FullNameField::FullNameField(AutofillField* field) : field_(field) {
105 scoped_ptr<FirstLastNameField> FirstLastNameField::ParseSpecificName(
106 AutofillScanner* scanner) {
107 // Some pages (e.g. Overstock_comBilling.html, SmithsonianCheckout.html)
108 // have the label "Name" followed by two or three text fields.
109 scoped_ptr<FirstLastNameField> v(new FirstLastNameField);
110 scanner->SaveCursor();
112 AutofillField* next = NULL;
113 if (ParseField(scanner, UTF8ToUTF16(kNameSpecificRe), &v->first_name_) &&
114 ParseEmptyLabel(scanner, &next)) {
115 if (ParseEmptyLabel(scanner, &v->last_name_)) {
116 // There are three name fields; assume that the middle one is a
117 // middle initial (it is, at least, on SmithsonianCheckout.html).
118 v->middle_name_ = next;
119 v->middle_initial_ = true;
120 } else { // only two name fields
121 v->last_name_ = next;
124 return v.Pass();
127 scanner->Rewind();
128 return NULL;
131 // static
132 scoped_ptr<FirstLastNameField> FirstLastNameField::ParseComponentNames(
133 AutofillScanner* scanner) {
134 scoped_ptr<FirstLastNameField> v(new FirstLastNameField);
135 scanner->SaveCursor();
137 // A fair number of pages use the names "fname" and "lname" for naming
138 // first and last name fields (examples from the test suite:
139 // BESTBUY_COM - Sign In2.html; Crate and Barrel Check Out.html;
140 // dell_checkout1.html). At least one UK page (The China Shop2.html)
141 // asks, in stuffy English style, for just initials and a surname,
142 // so we match "initials" here (and just fill in a first name there,
143 // American-style).
144 // The ".*first$" matches fields ending in "first" (example in sample8.html).
145 // The ".*last$" matches fields ending in "last" (example in sample8.html).
147 // Allow name fields to appear in any order.
148 while (!scanner->IsEnd()) {
149 // Skip over any unrelated fields, e.g. "username" or "nickname".
150 if (ParseFieldSpecifics(scanner, UTF8ToUTF16(kNameIgnoredRe),
151 MATCH_DEFAULT | MATCH_SELECT, NULL)) {
152 continue;
155 if (!v->first_name_ &&
156 ParseField(scanner, UTF8ToUTF16(kFirstNameRe), &v->first_name_)) {
157 continue;
160 // We check for a middle initial before checking for a middle name
161 // because at least one page (PC Connection.html) has a field marked
162 // as both (the label text is "MI" and the element name is
163 // "txtmiddlename"); such a field probably actually represents a
164 // middle initial.
165 if (!v->middle_name_ &&
166 ParseField(scanner, UTF8ToUTF16(kMiddleInitialRe), &v->middle_name_)) {
167 v->middle_initial_ = true;
168 continue;
171 if (!v->middle_name_ &&
172 ParseField(scanner, UTF8ToUTF16(kMiddleNameRe), &v->middle_name_)) {
173 continue;
176 if (!v->last_name_ &&
177 ParseField(scanner, UTF8ToUTF16(kLastNameRe), &v->last_name_)) {
178 continue;
181 break;
184 // Consider the match to be successful if we detected both first and last name
185 // fields.
186 if (v->first_name_ && v->last_name_)
187 return v.Pass();
189 scanner->Rewind();
190 return NULL;
193 // static
194 scoped_ptr<FirstLastNameField> FirstLastNameField::Parse(
195 AutofillScanner* scanner) {
196 scoped_ptr<FirstLastNameField> field = ParseSpecificName(scanner);
197 if (!field)
198 field = ParseComponentNames(scanner);
199 return field;
202 FirstLastNameField::FirstLastNameField()
203 : first_name_(NULL),
204 middle_name_(NULL),
205 last_name_(NULL),
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);
214 return ok;
217 } // namespace autofill