Home | History | Annotate | Download | only in autofill
      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