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