Bug 1413487 - [Form Autofill] Split "cc-exp" into "cc-exp-month" and "cc-exp-year" in the storage. r=seanlee draft
authorLuke Chang <lchang@mozilla.com>
Thu, 02 Nov 2017 12:19:22 +0800
changeset 693924 b370bb0592a060ddfebb606fdf4e02137b6eda7d
parent 693830 c2fe4b3b1b930b3e7fdb84eae44cec165394f322
child 739199 8a6a85445ef1bc8290c6ca33c22123e5b5a8c1a8
push id87981
push userbmo:lchang@mozilla.com
push dateTue, 07 Nov 2017 04:21:55 +0000
reviewersseanlee
bugs1413487
milestone58.0a1
Bug 1413487 - [Form Autofill] Split "cc-exp" into "cc-exp-month" and "cc-exp-year" in the storage. r=seanlee MozReview-Commit-ID: 1JKJFudYWHb
browser/extensions/formautofill/ProfileStorage.jsm
browser/extensions/formautofill/test/unit/test_transformFields.js
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -1519,51 +1519,116 @@ class CreditCards extends AutofillRecord
   _stripComputedFields(creditCard) {
     if (creditCard["cc-number-encrypted"]) {
       creditCard["cc-number"] = MasterPassword.decryptSync(creditCard["cc-number-encrypted"]);
     }
     super._stripComputedFields(creditCard);
   }
 
   _normalizeFields(creditCard) {
-    // Normalize name
+    this._normalizeCCName(creditCard);
+    this._normalizeCCExpirationDate(creditCard);
+  }
+
+  _normalizeCCName(creditCard) {
     if (creditCard["cc-given-name"] || creditCard["cc-additional-name"] || creditCard["cc-family-name"]) {
       if (!creditCard["cc-name"]) {
         creditCard["cc-name"] = FormAutofillNameUtils.joinNameParts({
           given: creditCard["cc-given-name"],
           middle: creditCard["cc-additional-name"],
           family: creditCard["cc-family-name"],
         });
       }
 
       delete creditCard["cc-given-name"];
       delete creditCard["cc-additional-name"];
       delete creditCard["cc-family-name"];
     }
+  }
 
-    // Validate expiry date
+  _normalizeCCExpirationDate(creditCard) {
     if (creditCard["cc-exp-month"]) {
       let expMonth = parseInt(creditCard["cc-exp-month"], 10);
       if (isNaN(expMonth) || expMonth < 1 || expMonth > 12) {
         delete creditCard["cc-exp-month"];
       } else {
         creditCard["cc-exp-month"] = expMonth;
       }
     }
+
     if (creditCard["cc-exp-year"]) {
       let expYear = parseInt(creditCard["cc-exp-year"], 10);
       if (isNaN(expYear) || expYear < 0) {
         delete creditCard["cc-exp-year"];
       } else if (expYear < 100) {
         // Enforce 4 digits years.
         creditCard["cc-exp-year"] = expYear + 2000;
       } else {
         creditCard["cc-exp-year"] = expYear;
       }
     }
+
+    if (creditCard["cc-exp"] && (!creditCard["cc-exp-month"] || !creditCard["cc-exp-year"])) {
+      let rules = [
+        {
+          regex: "(\\d{4})[-/](\\d{1,2})",
+          yearIndex: 1,
+          monthIndex: 2,
+        },
+        {
+          regex: "(\\d{1,2})[-/](\\d{4})",
+          yearIndex: 2,
+          monthIndex: 1,
+        },
+        {
+          regex: "(\\d{1,2})[-/](\\d{1,2})",
+        },
+        {
+          regex: "(\\d{2})(\\d{2})",
+        },
+      ];
+
+      for (let rule of rules) {
+        let result = new RegExp(`(?:^|\\D)${rule.regex}(?!\\d)`).exec(creditCard["cc-exp"]);
+        if (!result) {
+          continue;
+        }
+
+        let expYear, expMonth;
+
+        if (!rule.yearIndex || !rule.monthIndex) {
+          expMonth = parseInt(result[1], 10);
+          if (expMonth > 12) {
+            expYear = parseInt(result[1], 10);
+            expMonth = parseInt(result[2], 10);
+          } else {
+            expYear = parseInt(result[2], 10);
+          }
+        } else {
+          expYear = parseInt(result[rule.yearIndex], 10);
+          expMonth = parseInt(result[rule.monthIndex], 10);
+        }
+
+        if (expMonth < 1 || expMonth > 12) {
+          continue;
+        }
+
+        if (expYear < 100) {
+          expYear += 2000;
+        } else if (expYear < 2000) {
+          continue;
+        }
+
+        creditCard["cc-exp-month"] = expMonth;
+        creditCard["cc-exp-year"] = expYear;
+        break;
+      }
+    }
+
+    delete creditCard["cc-exp"];
   }
 }
 
 function ProfileStorage(path) {
   this._path = path;
   this._initializePromise = null;
   this.INTERNAL_FIELDS = INTERNAL_FIELDS;
 }
--- a/browser/extensions/formautofill/test/unit/test_transformFields.js
+++ b/browser/extensions/formautofill/test/unit/test_transformFields.js
@@ -522,46 +522,241 @@ const CREDIT_CARD_COMPUTE_TESTCASES = [
   },
 ];
 
 const CREDIT_CARD_NORMALIZE_TESTCASES = [
   // Empty
   {
     description: "No normalizable field",
     creditCard: {
-      "cc-number": "1234123412341234", // cc-number won't be verified
     },
     expectedResult: {
     },
   },
 
   // Name
   {
     description: "Has both \"cc-name\" and the split name fields",
     creditCard: {
       "cc-name": "Timothy John Berners-Lee",
       "cc-given-name": "John",
       "cc-family-name": "Doe",
-      "cc-number": "1234123412341234", // cc-number won't be verified
     },
     expectedResult: {
       "cc-name": "Timothy John Berners-Lee",
     },
   },
   {
     description: "Has only the split name fields",
     creditCard: {
       "cc-given-name": "John",
       "cc-family-name": "Doe",
-      "cc-number": "1234123412341234", // cc-number won't be verified
     },
     expectedResult: {
       "cc-name": "John Doe",
     },
   },
+
+  // Expiration Date
+  {
+    description: "Has \"cc-exp\" formatted \"yyyy-mm\"",
+    creditCard: {
+      "cc-exp": "2022-12",
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"yyyy/mm\"",
+    creditCard: {
+      "cc-exp": "2022/12",
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"yyyy-m\"",
+    creditCard: {
+      "cc-exp": "2022-3",
+    },
+    expectedResult: {
+      "cc-exp-month": 3,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"yyyy/m\"",
+    creditCard: {
+      "cc-exp": "2022/3",
+    },
+    expectedResult: {
+      "cc-exp-month": 3,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"mm-yyyy\"",
+    creditCard: {
+      "cc-exp": "12-2022",
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"mm/yyyy\"",
+    creditCard: {
+      "cc-exp": "12/2022",
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"m-yyyy\"",
+    creditCard: {
+      "cc-exp": "3-2022",
+    },
+    expectedResult: {
+      "cc-exp-month": 3,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"m/yyyy\"",
+    creditCard: {
+      "cc-exp": "3/2022",
+    },
+    expectedResult: {
+      "cc-exp-month": 3,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"mm-yy\"",
+    creditCard: {
+      "cc-exp": "12-22",
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"mm/yy\"",
+    creditCard: {
+      "cc-exp": "12/22",
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"yy-mm\"",
+    creditCard: {
+      "cc-exp": "22-12",
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"yy/mm\"",
+    creditCard: {
+      "cc-exp": "22/12",
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"mmyy\"",
+    creditCard: {
+      "cc-exp": "1222",
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" formatted \"yymm\"",
+    creditCard: {
+      "cc-exp": "2212",
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has \"cc-exp\" with spaces",
+    creditCard: {
+      "cc-exp": "  2033-11  ",
+    },
+    expectedResult: {
+      "cc-exp-month": 11,
+      "cc-exp-year": 2033,
+    },
+  },
+  {
+    description: "Has invalid \"cc-exp\"",
+    creditCard: {
+      "cc-exp": "99-9999",
+    },
+    expectedResult: {
+      "cc-exp-month": undefined,
+      "cc-exp-year": undefined,
+    },
+  },
+  {
+    description: "Has both \"cc-exp-*\" and \"cc-exp\"",
+    creditCard: {
+      "cc-exp": "2022-12",
+      "cc-exp-month": 3,
+      "cc-exp-year": 2030,
+    },
+    expectedResult: {
+      "cc-exp-month": 3,
+      "cc-exp-year": 2030,
+    },
+  },
+  {
+    description: "Has only \"cc-exp-year\" and \"cc-exp\"",
+    creditCard: {
+      "cc-exp": "2022-12",
+      "cc-exp-year": 2030,
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+  {
+    description: "Has only \"cc-exp-month\" and \"cc-exp\"",
+    creditCard: {
+      "cc-exp": "2022-12",
+      "cc-exp-month": 3,
+    },
+    expectedResult: {
+      "cc-exp-month": 12,
+      "cc-exp-year": 2022,
+    },
+  },
+
+  // Card Number
   {
     description: "Number should be encrypted and masked",
     creditCard: {
       "cc-number": "1234123412341234",
     },
     expectedResult: {
       "cc-number": "************1234",
     },