普通文本  |  213行  |  7.91 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/autofill/credit_card_field.h"

#include <stddef.h>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/autofill_field.h"
#include "chrome/browser/autofill/field_types.h"
#include "grit/autofill_resources.h"
#include "ui/base/l10n/l10n_util.h"

bool CreditCardField::GetFieldInfo(FieldTypeMap* field_type_map) const {
  bool ok = Add(field_type_map, number_, AutofillType(CREDIT_CARD_NUMBER));
  DCHECK(ok);

  // If the heuristics detected first and last name in separate fields,
  // then ignore both fields. Putting them into separate fields is probably
  // wrong, because the credit card can also contain a middle name or middle
  // initial.
  if (cardholder_last_ == NULL) {
    // Add() will check if cardholder_ is != NULL.
    ok = ok && Add(field_type_map, cardholder_, AutofillType(CREDIT_CARD_NAME));
    DCHECK(ok);
  }

  ok = ok && Add(field_type_map, type_, AutofillType(CREDIT_CARD_TYPE));
  DCHECK(ok);
  ok = ok && Add(field_type_map, expiration_month_,
      AutofillType(CREDIT_CARD_EXP_MONTH));
  DCHECK(ok);
  ok = ok && Add(field_type_map, expiration_year_,
      AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR));
  DCHECK(ok);

  return ok;
}

FormFieldType CreditCardField::GetFormFieldType() const {
  return kCreditCardType;
}

// static
CreditCardField* CreditCardField::Parse(
    std::vector<AutofillField*>::const_iterator* iter,
    bool is_ecml) {
  scoped_ptr<CreditCardField> credit_card_field(new CreditCardField);
  std::vector<AutofillField*>::const_iterator q = *iter;
  string16 pattern;

  // Credit card fields can appear in many different orders.
  // We loop until no more credit card related fields are found, see |break| at
  // bottom of the loop.
  for (int fields = 0; true; ++fields) {
    // Sometimes the cardholder field is just labeled "name". Unfortunately this
    // is a dangerously generic word to search for, since it will often match a
    // name (not cardholder name) field before or after credit card fields. So
    // we search for "name" only when we've already parsed at least one other
    // credit card field and haven't yet parsed the expiration date (which
    // usually appears at the end).
    if (credit_card_field->cardholder_ == NULL) {
      string16 name_pattern;
      if (is_ecml) {
        name_pattern = GetEcmlPattern(kEcmlCardHolder);
      } else {
        if (fields == 0 || credit_card_field->expiration_month_) {
          // at beginning or end
          name_pattern = l10n_util::GetStringUTF16(
              IDS_AUTOFILL_NAME_ON_CARD_RE);
        } else {
          name_pattern = l10n_util::GetStringUTF16(
              IDS_AUTOFILL_NAME_ON_CARD_CONTEXTUAL_RE);
        }
      }

      if (ParseText(&q, name_pattern, &credit_card_field->cardholder_))
        continue;

      // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html
      // and ExpediaBilling.html in our test suite), recognize separate fields
      // for the cardholder's first and last name if they have the labels "cfnm"
      // and "clnm".
      std::vector<AutofillField*>::const_iterator p = q;
      AutofillField* first;
      if (!is_ecml && ParseText(&p, ASCIIToUTF16("^cfnm"), &first) &&
          ParseText(&p, ASCIIToUTF16("^clnm"),
                    &credit_card_field->cardholder_last_)) {
        credit_card_field->cardholder_ = first;
        q = p;
        continue;
      }
    }

    // We look for a card security code before we look for a credit
    // card number and match the general term "number".  The security code
    // has a plethora of names; we've seen "verification #",
    // "verification number", "card identification number" and others listed
    // in the |pattern| below.
    if (is_ecml) {
      pattern = GetEcmlPattern(kEcmlCardVerification);
    } else {
      pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_CVC_RE);
    }

    if (credit_card_field->verification_ == NULL &&
        ParseText(&q, pattern, &credit_card_field->verification_))
      continue;

    // TODO(jhawkins): Parse the type select control.

    if (is_ecml)
      pattern = GetEcmlPattern(kEcmlCardNumber);
    else
      pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_NUMBER_RE);

    if (credit_card_field->number_ == NULL && ParseText(&q, pattern,
        &credit_card_field->number_))
      continue;

    if ((*q) && LowerCaseEqualsASCII((*q)->form_control_type, "month")) {
      credit_card_field->expiration_month_ = *q++;
    } else {
      // "Expiration date" is the most common label here, but some pages have
      // "Expires", "exp. date" or "exp. month" and "exp. year".  We also look
      // for the field names ccmonth and ccyear, which appear on at least 4 of
      // our test pages.
      //
      // -> On at least one page (The China Shop2.html) we find only the labels
      // "month" and "year".  So for now we match these words directly; we'll
      // see if this turns out to be too general.
      //
      // Toolbar Bug 51451: indeed, simply matching "month" is too general for
      //   https://rps.fidelity.com/ftgw/rps/RtlCust/CreatePIN/Init.
      // Instead, we match only words beginning with "month".
      if (is_ecml)
        pattern = GetEcmlPattern(kEcmlCardExpireMonth);
      else
        pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_MONTH_RE);

      if ((!credit_card_field->expiration_month_ ||
          credit_card_field->expiration_month_->IsEmpty()) &&
        ParseText(&q, pattern, &credit_card_field->expiration_month_)) {

        if (is_ecml)
          pattern = GetEcmlPattern(kEcmlCardExpireYear);
        else
          pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_DATE_RE);

        if (!ParseText(&q, pattern, &credit_card_field->expiration_year_)) {
          return NULL;
        }
        continue;
      }
    }

    if (ParseText(&q, GetEcmlPattern(kEcmlCardExpireDay)))
      continue;

    // Some pages (e.g. ExpediaBilling.html) have a "card description"
    // field; we parse this field but ignore it.
    // We also ignore any other fields within a credit card block that
    // start with "card", under the assumption that they are related to
    // the credit card section being processed but are uninteresting to us.
    if (ParseText(&q, l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_IGNORED_RE)))
      continue;

    break;
  }

  // Some pages have a billing address field after the cardholder name field.
  // For that case, allow only just the cardholder name field.  The remaining
  // CC fields will be picked up in a following CreditCardField.
  if (credit_card_field->cardholder_) {
    *iter = q;
    return credit_card_field.release();
  }

  // On some pages, the user selects a card type using radio buttons
  // (e.g. test page Apple Store Billing.html).  We can't handle that yet,
  // so we treat the card type as optional for now.
  // The existence of a number or cvc in combination with expiration date is
  // a strong enough signal that this is a credit card.  It is possible that
  // the number and name were parsed in a separate part of the form.  So if
  // the cvc and date were found independently they are returned.
  if ((credit_card_field->number_ || credit_card_field->verification_) &&
      credit_card_field->expiration_month_ &&
      (credit_card_field->expiration_year_ ||
      (LowerCaseEqualsASCII(
           credit_card_field->expiration_month_->form_control_type,
           "month")))) {
      *iter = q;
      return credit_card_field.release();
  }

  return NULL;
}

CreditCardField::CreditCardField()
    : cardholder_(NULL),
      cardholder_last_(NULL),
      type_(NULL),
      number_(NULL),
      verification_(NULL),
      expiration_month_(NULL),
      expiration_year_(NULL) {
}