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/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/string16.h" 12 #include "base/string_util.h" 13 #include "base/utf_string_conversions.h" 14 #include "chrome/browser/autofill/autofill_field.h" 15 #include "chrome/browser/autofill/field_types.h" 16 #include "grit/autofill_resources.h" 17 #include "ui/base/l10n/l10n_util.h" 18 19 bool CreditCardField::GetFieldInfo(FieldTypeMap* field_type_map) const { 20 bool ok = Add(field_type_map, number_, AutofillType(CREDIT_CARD_NUMBER)); 21 DCHECK(ok); 22 23 // If the heuristics detected first and last name in separate fields, 24 // then ignore both fields. Putting them into separate fields is probably 25 // wrong, because the credit card can also contain a middle name or middle 26 // initial. 27 if (cardholder_last_ == NULL) { 28 // Add() will check if cardholder_ is != NULL. 29 ok = ok && Add(field_type_map, cardholder_, AutofillType(CREDIT_CARD_NAME)); 30 DCHECK(ok); 31 } 32 33 ok = ok && Add(field_type_map, type_, AutofillType(CREDIT_CARD_TYPE)); 34 DCHECK(ok); 35 ok = ok && Add(field_type_map, expiration_month_, 36 AutofillType(CREDIT_CARD_EXP_MONTH)); 37 DCHECK(ok); 38 ok = ok && Add(field_type_map, expiration_year_, 39 AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)); 40 DCHECK(ok); 41 42 return ok; 43 } 44 45 FormFieldType CreditCardField::GetFormFieldType() const { 46 return kCreditCardType; 47 } 48 49 // static 50 CreditCardField* CreditCardField::Parse( 51 std::vector<AutofillField*>::const_iterator* iter, 52 bool is_ecml) { 53 scoped_ptr<CreditCardField> credit_card_field(new CreditCardField); 54 std::vector<AutofillField*>::const_iterator q = *iter; 55 string16 pattern; 56 57 // Credit card fields can appear in many different orders. 58 // We loop until no more credit card related fields are found, see |break| at 59 // bottom of the loop. 60 for (int fields = 0; true; ++fields) { 61 // Sometimes the cardholder field is just labeled "name". Unfortunately this 62 // is a dangerously generic word to search for, since it will often match a 63 // name (not cardholder name) field before or after credit card fields. So 64 // we search for "name" only when we've already parsed at least one other 65 // credit card field and haven't yet parsed the expiration date (which 66 // usually appears at the end). 67 if (credit_card_field->cardholder_ == NULL) { 68 string16 name_pattern; 69 if (is_ecml) { 70 name_pattern = GetEcmlPattern(kEcmlCardHolder); 71 } else { 72 if (fields == 0 || credit_card_field->expiration_month_) { 73 // at beginning or end 74 name_pattern = l10n_util::GetStringUTF16( 75 IDS_AUTOFILL_NAME_ON_CARD_RE); 76 } else { 77 name_pattern = l10n_util::GetStringUTF16( 78 IDS_AUTOFILL_NAME_ON_CARD_CONTEXTUAL_RE); 79 } 80 } 81 82 if (ParseText(&q, name_pattern, &credit_card_field->cardholder_)) 83 continue; 84 85 // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html 86 // and ExpediaBilling.html in our test suite), recognize separate fields 87 // for the cardholder's first and last name if they have the labels "cfnm" 88 // and "clnm". 89 std::vector<AutofillField*>::const_iterator p = q; 90 AutofillField* first; 91 if (!is_ecml && ParseText(&p, ASCIIToUTF16("^cfnm"), &first) && 92 ParseText(&p, ASCIIToUTF16("^clnm"), 93 &credit_card_field->cardholder_last_)) { 94 credit_card_field->cardholder_ = first; 95 q = p; 96 continue; 97 } 98 } 99 100 // We look for a card security code before we look for a credit 101 // card number and match the general term "number". The security code 102 // has a plethora of names; we've seen "verification #", 103 // "verification number", "card identification number" and others listed 104 // in the |pattern| below. 105 if (is_ecml) { 106 pattern = GetEcmlPattern(kEcmlCardVerification); 107 } else { 108 pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_CVC_RE); 109 } 110 111 if (credit_card_field->verification_ == NULL && 112 ParseText(&q, pattern, &credit_card_field->verification_)) 113 continue; 114 115 // TODO(jhawkins): Parse the type select control. 116 117 if (is_ecml) 118 pattern = GetEcmlPattern(kEcmlCardNumber); 119 else 120 pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_NUMBER_RE); 121 122 if (credit_card_field->number_ == NULL && ParseText(&q, pattern, 123 &credit_card_field->number_)) 124 continue; 125 126 if ((*q) && LowerCaseEqualsASCII((*q)->form_control_type, "month")) { 127 credit_card_field->expiration_month_ = *q++; 128 } else { 129 // "Expiration date" is the most common label here, but some pages have 130 // "Expires", "exp. date" or "exp. month" and "exp. year". We also look 131 // for the field names ccmonth and ccyear, which appear on at least 4 of 132 // our test pages. 133 // 134 // -> On at least one page (The China Shop2.html) we find only the labels 135 // "month" and "year". So for now we match these words directly; we'll 136 // see if this turns out to be too general. 137 // 138 // Toolbar Bug 51451: indeed, simply matching "month" is too general for 139 // https://rps.fidelity.com/ftgw/rps/RtlCust/CreatePIN/Init. 140 // Instead, we match only words beginning with "month". 141 if (is_ecml) 142 pattern = GetEcmlPattern(kEcmlCardExpireMonth); 143 else 144 pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_MONTH_RE); 145 146 if ((!credit_card_field->expiration_month_ || 147 credit_card_field->expiration_month_->IsEmpty()) && 148 ParseText(&q, pattern, &credit_card_field->expiration_month_)) { 149 150 if (is_ecml) 151 pattern = GetEcmlPattern(kEcmlCardExpireYear); 152 else 153 pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_DATE_RE); 154 155 if (!ParseText(&q, pattern, &credit_card_field->expiration_year_)) { 156 return NULL; 157 } 158 continue; 159 } 160 } 161 162 if (ParseText(&q, GetEcmlPattern(kEcmlCardExpireDay))) 163 continue; 164 165 // Some pages (e.g. ExpediaBilling.html) have a "card description" 166 // field; we parse this field but ignore it. 167 // We also ignore any other fields within a credit card block that 168 // start with "card", under the assumption that they are related to 169 // the credit card section being processed but are uninteresting to us. 170 if (ParseText(&q, l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_IGNORED_RE))) 171 continue; 172 173 break; 174 } 175 176 // Some pages have a billing address field after the cardholder name field. 177 // For that case, allow only just the cardholder name field. The remaining 178 // CC fields will be picked up in a following CreditCardField. 179 if (credit_card_field->cardholder_) { 180 *iter = q; 181 return credit_card_field.release(); 182 } 183 184 // On some pages, the user selects a card type using radio buttons 185 // (e.g. test page Apple Store Billing.html). We can't handle that yet, 186 // so we treat the card type as optional for now. 187 // The existence of a number or cvc in combination with expiration date is 188 // a strong enough signal that this is a credit card. It is possible that 189 // the number and name were parsed in a separate part of the form. So if 190 // the cvc and date were found independently they are returned. 191 if ((credit_card_field->number_ || credit_card_field->verification_) && 192 credit_card_field->expiration_month_ && 193 (credit_card_field->expiration_year_ || 194 (LowerCaseEqualsASCII( 195 credit_card_field->expiration_month_->form_control_type, 196 "month")))) { 197 *iter = q; 198 return credit_card_field.release(); 199 } 200 201 return NULL; 202 } 203 204 CreditCardField::CreditCardField() 205 : cardholder_(NULL), 206 cardholder_last_(NULL), 207 type_(NULL), 208 number_(NULL), 209 verification_(NULL), 210 expiration_month_(NULL), 211 expiration_year_(NULL) { 212 } 213