PageRenderTime 46ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/chrome/browser/autofill/credit_card_field.cc

https://bitbucket.org/kennethendfinger/chromium
C++ | 219 lines | 145 code | 25 blank | 49 comment | 40 complexity | 4c52e22db34bdc14675589da21e05ec6 MD5 | raw file
Possible License(s): AGPL-3.0, GPL-3.0, LGPL-2.1, CC-BY-SA-3.0, BSD-2-Clause, CC-BY-3.0, AGPL-1.0, Unlicense, 0BSD, MPL-2.0, LGPL-2.0, Apache-2.0, GPL-2.0, LGPL-3.0, BSD-3-Clause, MPL-2.0-no-copyleft-exception, MIT
  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. #include "chrome/browser/autofill/credit_card_field.h"
  5. #include <stddef.h>
  6. #include "base/logging.h"
  7. #include "base/memory/scoped_ptr.h"
  8. #include "base/string16.h"
  9. #include "base/string_util.h"
  10. #include "base/utf_string_conversions.h"
  11. #include "chrome/browser/autofill/autofill_field.h"
  12. #include "chrome/browser/autofill/autofill_regex_constants.h"
  13. #include "chrome/browser/autofill/autofill_scanner.h"
  14. #include "chrome/browser/autofill/field_types.h"
  15. #include "ui/base/l10n/l10n_util.h"
  16. // static
  17. FormField* CreditCardField::Parse(AutofillScanner* scanner) {
  18. if (scanner->IsEnd())
  19. return NULL;
  20. scoped_ptr<CreditCardField> credit_card_field(new CreditCardField);
  21. size_t saved_cursor = scanner->SaveCursor();
  22. // Credit card fields can appear in many different orders.
  23. // We loop until no more credit card related fields are found, see |break| at
  24. // bottom of the loop.
  25. for (int fields = 0; !scanner->IsEnd(); ++fields) {
  26. // Ignore gift card fields.
  27. if (ParseField(scanner, UTF8ToUTF16(autofill::kGiftCardRe), NULL))
  28. break;
  29. // Sometimes the cardholder field is just labeled "name". Unfortunately this
  30. // is a dangerously generic word to search for, since it will often match a
  31. // name (not cardholder name) field before or after credit card fields. So
  32. // we search for "name" only when we've already parsed at least one other
  33. // credit card field and haven't yet parsed the expiration date (which
  34. // usually appears at the end).
  35. if (credit_card_field->cardholder_ == NULL) {
  36. string16 name_pattern;
  37. if (fields == 0 || credit_card_field->expiration_month_) {
  38. // at beginning or end
  39. name_pattern = UTF8ToUTF16(autofill::kNameOnCardRe);
  40. } else {
  41. name_pattern = UTF8ToUTF16(autofill::kNameOnCardContextualRe);
  42. }
  43. if (ParseField(scanner, name_pattern, &credit_card_field->cardholder_))
  44. continue;
  45. // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html
  46. // and ExpediaBilling.html in our test suite), recognize separate fields
  47. // for the cardholder's first and last name if they have the labels "cfnm"
  48. // and "clnm".
  49. scanner->SaveCursor();
  50. const AutofillField* first;
  51. if (ParseField(scanner, ASCIIToUTF16("^cfnm"), &first) &&
  52. ParseField(scanner, ASCIIToUTF16("^clnm"),
  53. &credit_card_field->cardholder_last_)) {
  54. credit_card_field->cardholder_ = first;
  55. continue;
  56. }
  57. scanner->Rewind();
  58. }
  59. // We look for a card security code before we look for a credit
  60. // card number and match the general term "number". The security code
  61. // has a plethora of names; we've seen "verification #",
  62. // "verification number", "card identification number" and others listed
  63. // in the |pattern| below.
  64. string16 pattern = UTF8ToUTF16(autofill::kCardCvcRe);
  65. if (!credit_card_field->verification_ &&
  66. ParseField(scanner, pattern, &credit_card_field->verification_)) {
  67. continue;
  68. }
  69. // TODO(jhawkins): Parse the type select control.
  70. pattern = UTF8ToUTF16(autofill::kCardNumberRe);
  71. if (!credit_card_field->number_ &&
  72. ParseField(scanner, pattern, &credit_card_field->number_)) {
  73. continue;
  74. }
  75. if (LowerCaseEqualsASCII(scanner->Cursor()->form_control_type, "month")) {
  76. credit_card_field->expiration_month_ = scanner->Cursor();
  77. scanner->Advance();
  78. } else {
  79. // First try to parse split month/year expiration fields.
  80. scanner->SaveCursor();
  81. pattern = UTF8ToUTF16(autofill::kExpirationMonthRe);
  82. if (!credit_card_field->expiration_month_ &&
  83. ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_SELECT,
  84. &credit_card_field->expiration_month_)) {
  85. pattern = UTF8ToUTF16(autofill::kExpirationYearRe);
  86. if (ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_SELECT,
  87. &credit_card_field->expiration_year_)) {
  88. continue;
  89. }
  90. }
  91. // If that fails, try to parse a combined expiration field.
  92. if (!credit_card_field->expiration_date_) {
  93. // Look for a 2-digit year first.
  94. scanner->Rewind();
  95. pattern = UTF8ToUTF16(autofill::kExpirationDate2DigitYearRe);
  96. // We allow <select> fields, because they're used e.g. on qvc.com.
  97. if (ParseFieldSpecifics(scanner, pattern,
  98. MATCH_LABEL | MATCH_VALUE | MATCH_TEXT |
  99. MATCH_SELECT,
  100. &credit_card_field->expiration_date_)) {
  101. credit_card_field->is_two_digit_year_ = true;
  102. continue;
  103. }
  104. pattern = UTF8ToUTF16(autofill::kExpirationDateRe);
  105. if (ParseFieldSpecifics(scanner, pattern,
  106. MATCH_LABEL | MATCH_VALUE | MATCH_TEXT |
  107. MATCH_SELECT,
  108. &credit_card_field->expiration_date_)) {
  109. continue;
  110. }
  111. }
  112. if (credit_card_field->expiration_month_ &&
  113. !credit_card_field->expiration_year_ &&
  114. !credit_card_field->expiration_date_) {
  115. // Parsed a month but couldn't parse a year; give up.
  116. scanner->RewindTo(saved_cursor);
  117. return NULL;
  118. }
  119. }
  120. // Some pages (e.g. ExpediaBilling.html) have a "card description"
  121. // field; we parse this field but ignore it.
  122. // We also ignore any other fields within a credit card block that
  123. // start with "card", under the assumption that they are related to
  124. // the credit card section being processed but are uninteresting to us.
  125. if (ParseField(scanner, UTF8ToUTF16(autofill::kCardIgnoredRe), NULL))
  126. continue;
  127. break;
  128. }
  129. // Some pages have a billing address field after the cardholder name field.
  130. // For that case, allow only just the cardholder name field. The remaining
  131. // CC fields will be picked up in a following CreditCardField.
  132. if (credit_card_field->cardholder_)
  133. return credit_card_field.release();
  134. // On some pages, the user selects a card type using radio buttons
  135. // (e.g. test page Apple Store Billing.html). We can't handle that yet,
  136. // so we treat the card type as optional for now.
  137. // The existence of a number or cvc in combination with expiration date is
  138. // a strong enough signal that this is a credit card. It is possible that
  139. // the number and name were parsed in a separate part of the form. So if
  140. // the cvc and date were found independently they are returned.
  141. if ((credit_card_field->number_ || credit_card_field->verification_) &&
  142. (credit_card_field->expiration_date_ ||
  143. (credit_card_field->expiration_month_ &&
  144. (credit_card_field->expiration_year_ ||
  145. (LowerCaseEqualsASCII(
  146. credit_card_field->expiration_month_->form_control_type,
  147. "month")))))) {
  148. return credit_card_field.release();
  149. }
  150. scanner->RewindTo(saved_cursor);
  151. return NULL;
  152. }
  153. CreditCardField::CreditCardField()
  154. : cardholder_(NULL),
  155. cardholder_last_(NULL),
  156. type_(NULL),
  157. number_(NULL),
  158. verification_(NULL),
  159. expiration_month_(NULL),
  160. expiration_year_(NULL),
  161. expiration_date_(NULL),
  162. is_two_digit_year_(false) {
  163. }
  164. bool CreditCardField::ClassifyField(FieldTypeMap* map) const {
  165. bool ok = AddClassification(number_, CREDIT_CARD_NUMBER, map);
  166. // If the heuristics detected first and last name in separate fields,
  167. // then ignore both fields. Putting them into separate fields is probably
  168. // wrong, because the credit card can also contain a middle name or middle
  169. // initial.
  170. if (cardholder_last_ == NULL)
  171. ok = ok && AddClassification(cardholder_, CREDIT_CARD_NAME, map);
  172. ok = ok && AddClassification(type_, CREDIT_CARD_TYPE, map);
  173. if (expiration_date_) {
  174. if (is_two_digit_year_) {
  175. ok = ok && AddClassification(expiration_date_,
  176. CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, map);
  177. } else {
  178. ok = ok && AddClassification(expiration_date_,
  179. CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, map);
  180. }
  181. } else {
  182. ok = ok && AddClassification(expiration_month_, CREDIT_CARD_EXP_MONTH, map);
  183. if (is_two_digit_year_) {
  184. ok = ok && AddClassification(expiration_year_,
  185. CREDIT_CARD_EXP_2_DIGIT_YEAR,
  186. map);
  187. } else {
  188. ok = ok && AddClassification(expiration_year_,
  189. CREDIT_CARD_EXP_4_DIGIT_YEAR,
  190. map);
  191. }
  192. }
  193. return ok;
  194. }