| 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 |