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. 4 5#include "components/autofill/core/browser/credit_card_field.h" 6 7#include <stddef.h> 8 9#include "base/logging.h" 10#include "base/memory/scoped_ptr.h" 11#include "base/strings/string16.h" 12#include "base/strings/string_util.h" 13#include "base/strings/utf_string_conversions.h" 14#include "components/autofill/core/browser/autofill_field.h" 15#include "components/autofill/core/browser/autofill_regex_constants.h" 16#include "components/autofill/core/browser/autofill_scanner.h" 17#include "components/autofill/core/browser/field_types.h" 18#include "ui/base/l10n/l10n_util.h" 19 20namespace autofill { 21 22// Credit card numbers are at most 19 digits in length. 23// [Ref: http://en.wikipedia.org/wiki/Bank_card_number] 24static const size_t kMaxValidCardNumberSize = 19; 25 26// static 27FormField* CreditCardField::Parse(AutofillScanner* scanner) { 28 if (scanner->IsEnd()) 29 return NULL; 30 31 scoped_ptr<CreditCardField> credit_card_field(new CreditCardField); 32 size_t saved_cursor = scanner->SaveCursor(); 33 bool form_has_valid_card_number_fields = true; 34 35 // Credit card fields can appear in many different orders. 36 // We loop until no more credit card related fields are found, see |break| at 37 // bottom of the loop. 38 for (int fields = 0; !scanner->IsEnd(); ++fields) { 39 // Ignore gift card fields. 40 if (ParseField(scanner, base::UTF8ToUTF16(autofill::kGiftCardRe), NULL)) 41 break; 42 43 // Sometimes the cardholder field is just labeled "name". Unfortunately this 44 // is a dangerously generic word to search for, since it will often match a 45 // name (not cardholder name) field before or after credit card fields. So 46 // we search for "name" only when we've already parsed at least one other 47 // credit card field and haven't yet parsed the expiration date (which 48 // usually appears at the end). 49 if (credit_card_field->cardholder_ == NULL) { 50 base::string16 name_pattern; 51 if (fields == 0 || credit_card_field->expiration_month_) { 52 // at beginning or end 53 name_pattern = base::UTF8ToUTF16(autofill::kNameOnCardRe); 54 } else { 55 name_pattern = base::UTF8ToUTF16(autofill::kNameOnCardContextualRe); 56 } 57 58 if (ParseField(scanner, name_pattern, &credit_card_field->cardholder_)) 59 continue; 60 61 // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html 62 // and ExpediaBilling.html in our test suite), recognize separate fields 63 // for the cardholder's first and last name if they have the labels "cfnm" 64 // and "clnm". 65 scanner->SaveCursor(); 66 AutofillField* first; 67 if (ParseField(scanner, base::ASCIIToUTF16("^cfnm"), &first) && 68 ParseField(scanner, 69 base::ASCIIToUTF16("^clnm"), 70 &credit_card_field->cardholder_last_)) { 71 credit_card_field->cardholder_ = first; 72 continue; 73 } 74 scanner->Rewind(); 75 } 76 77 // Check for a credit card type (Visa, MasterCard, etc.) field. 78 base::string16 type_pattern = base::UTF8ToUTF16(autofill::kCardTypeRe); 79 if (!credit_card_field->type_ && 80 ParseFieldSpecifics(scanner, 81 type_pattern, 82 MATCH_DEFAULT | MATCH_SELECT, 83 &credit_card_field->type_)) { 84 continue; 85 } 86 87 // We look for a card security code before we look for a credit 88 // card number and match the general term "number". The security code 89 // has a plethora of names; we've seen "verification #", 90 // "verification number", "card identification number" and others listed 91 // in the |pattern| below. 92 base::string16 pattern = base::UTF8ToUTF16(autofill::kCardCvcRe); 93 if (!credit_card_field->verification_ && 94 ParseField(scanner, pattern, &credit_card_field->verification_)) { 95 continue; 96 } 97 98 pattern = base::UTF8ToUTF16(autofill::kCardNumberRe); 99 AutofillField* current_number_field; 100 if (ParseField(scanner, pattern, ¤t_number_field)) { 101 // Avoid autofilling any credit card number field having very low or high 102 // |start_index| on the HTML form. 103 size_t start_index = 0; 104 if (!credit_card_field->numbers_.empty()) { 105 size_t last_number_field_size = 106 credit_card_field->numbers_.back()->credit_card_number_offset() + 107 credit_card_field->numbers_.back()->max_length; 108 109 // In some cases, HTML form may have credit card number split across 110 // multiple input fields and either one or cumulatively having 111 // |max_length| more than |kMaxValidCardNumberSize|, mark these input 112 // form fields as invalid and skip autofilling them. 113 if (last_number_field_size == 0U || 114 last_number_field_size >= kMaxValidCardNumberSize) { 115 // Mark that the credit card number splits are invalid. But keep 116 // scanning HTML form so that cursor moves beyond related fields. 117 form_has_valid_card_number_fields = false; 118 } 119 120 start_index = last_number_field_size; 121 } 122 123 current_number_field->set_credit_card_number_offset(start_index); 124 credit_card_field->numbers_.push_back(current_number_field); 125 continue; 126 } 127 128 if (LowerCaseEqualsASCII(scanner->Cursor()->form_control_type, "month")) { 129 credit_card_field->expiration_date_ = scanner->Cursor(); 130 scanner->Advance(); 131 } else { 132 // First try to parse split month/year expiration fields. 133 scanner->SaveCursor(); 134 pattern = base::UTF8ToUTF16(autofill::kExpirationMonthRe); 135 if (!credit_card_field->expiration_month_ && 136 ParseFieldSpecifics(scanner, 137 pattern, 138 MATCH_DEFAULT | MATCH_SELECT, 139 &credit_card_field->expiration_month_)) { 140 pattern = base::UTF8ToUTF16(autofill::kExpirationYearRe); 141 if (ParseFieldSpecifics(scanner, 142 pattern, 143 MATCH_DEFAULT | MATCH_SELECT, 144 &credit_card_field->expiration_year_)) { 145 continue; 146 } 147 } 148 149 // If that fails, try to parse a combined expiration field. 150 if (!credit_card_field->expiration_date_) { 151 // Look for a 2-digit year first. 152 scanner->Rewind(); 153 pattern = base::UTF8ToUTF16(autofill::kExpirationDate2DigitYearRe); 154 // We allow <select> fields, because they're used e.g. on qvc.com. 155 if (ParseFieldSpecifics( 156 scanner, 157 pattern, 158 MATCH_LABEL | MATCH_VALUE | MATCH_TEXT | MATCH_SELECT, 159 &credit_card_field->expiration_date_)) { 160 credit_card_field->exp_year_type_ = CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR; 161 continue; 162 } 163 164 pattern = base::UTF8ToUTF16(autofill::kExpirationDateRe); 165 if (ParseFieldSpecifics( 166 scanner, 167 pattern, 168 MATCH_LABEL | MATCH_VALUE | MATCH_TEXT | MATCH_SELECT, 169 &credit_card_field->expiration_date_)) { 170 continue; 171 } 172 } 173 174 if (credit_card_field->expiration_month_ && 175 !credit_card_field->expiration_year_ && 176 !credit_card_field->expiration_date_) { 177 // Parsed a month but couldn't parse a year; give up. 178 scanner->RewindTo(saved_cursor); 179 return NULL; 180 } 181 } 182 183 // Some pages (e.g. ExpediaBilling.html) have a "card description" 184 // field; we parse this field but ignore it. 185 // We also ignore any other fields within a credit card block that 186 // start with "card", under the assumption that they are related to 187 // the credit card section being processed but are uninteresting to us. 188 if (ParseField(scanner, base::UTF8ToUTF16(autofill::kCardIgnoredRe), NULL)) 189 continue; 190 191 break; 192 } 193 194 // Cases where heuristic misinterprets input field as credit card number 195 // field, refuse to autofill credit card number fields. 196 if (!form_has_valid_card_number_fields) 197 credit_card_field->numbers_.clear(); 198 199 // Some pages have a billing address field after the cardholder name field. 200 // For that case, allow only just the cardholder name field. The remaining 201 // CC fields will be picked up in a following CreditCardField. 202 if (credit_card_field->cardholder_) 203 return credit_card_field.release(); 204 205 // On some pages, the user selects a card type using radio buttons 206 // (e.g. test page Apple Store Billing.html). We can't handle that yet, 207 // so we treat the card type as optional for now. 208 // The existence of a number or cvc in combination with expiration date is 209 // a strong enough signal that this is a credit card. It is possible that 210 // the number and name were parsed in a separate part of the form. So if 211 // the cvc and date were found independently they are returned. 212 if ((!credit_card_field->numbers_.empty() || 213 credit_card_field->verification_ || 214 !form_has_valid_card_number_fields) && 215 (credit_card_field->expiration_date_ || 216 (credit_card_field->expiration_month_ && 217 credit_card_field->expiration_year_))) { 218 return credit_card_field.release(); 219 } 220 221 scanner->RewindTo(saved_cursor); 222 return NULL; 223} 224 225CreditCardField::CreditCardField() 226 : cardholder_(NULL), 227 cardholder_last_(NULL), 228 type_(NULL), 229 verification_(NULL), 230 expiration_month_(NULL), 231 expiration_year_(NULL), 232 expiration_date_(NULL), 233 exp_year_type_(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR) { 234} 235 236CreditCardField::~CreditCardField() { 237} 238 239bool CreditCardField::ClassifyField(ServerFieldTypeMap* map) const { 240 bool ok = true; 241 for (size_t index = 0; index < numbers_.size(); ++index) { 242 ok = ok && AddClassification(numbers_[index], CREDIT_CARD_NUMBER, map); 243 } 244 245 ok = ok && AddClassification(type_, CREDIT_CARD_TYPE, map); 246 ok = ok && 247 AddClassification(verification_, CREDIT_CARD_VERIFICATION_CODE, map); 248 249 // If the heuristics detected first and last name in separate fields, 250 // then ignore both fields. Putting them into separate fields is probably 251 // wrong, because the credit card can also contain a middle name or middle 252 // initial. 253 if (cardholder_last_ == NULL) 254 ok = ok && AddClassification(cardholder_, CREDIT_CARD_NAME, map); 255 256 if (expiration_date_) { 257 ok = 258 ok && AddClassification(expiration_date_, GetExpirationYearType(), map); 259 } else { 260 ok = ok && AddClassification(expiration_month_, CREDIT_CARD_EXP_MONTH, map); 261 ok = 262 ok && AddClassification(expiration_year_, GetExpirationYearType(), map); 263 } 264 265 return ok; 266} 267 268ServerFieldType CreditCardField::GetExpirationYearType() const { 269 return (expiration_date_ 270 ? exp_year_type_ 271 : ((expiration_year_ && expiration_year_->max_length == 2) 272 ? CREDIT_CARD_EXP_2_DIGIT_YEAR 273 : CREDIT_CARD_EXP_4_DIGIT_YEAR)); 274} 275 276} // namespace autofill 277