Bug 1370429 - Part 4: Implement grammar list matching algorithm for telephone fields. r=MattN draft
authorSean Lee <selee@mozilla.com>
Wed, 19 Jul 2017 10:18:06 +0800
changeset 617511 1ecfcd2e225deb285bfd197dec9080bddce01adb
parent 617510 30d016b9a060633895f91b94c83a7e349cfe900c
child 617512 5cdbca6e37e46889745b300a1687486f73755c2f
push id71067
push userbmo:selee@mozilla.com
push dateFri, 28 Jul 2017 15:17:36 +0000
reviewersMattN
bugs1370429
milestone56.0a1
Bug 1370429 - Part 4: Implement grammar list matching algorithm for telephone fields. r=MattN MozReview-Commit-ID: K81o3XSqxKO
browser/extensions/formautofill/FormAutofillHeuristics.jsm
--- a/browser/extensions/formautofill/FormAutofillHeuristics.jsm
+++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm
@@ -167,16 +167,20 @@ class FieldScanner {
    *
    * @returns {Array<Object>}
    *          The array with the field details without invalid field name and
    *          duplicated fields.
    */
   get trimmedFieldDetail() {
     return this.fieldDetails.filter(f => f.fieldName && !f._duplicated);
   }
+
+  elementExisting(index) {
+    return index < this._elements.length;
+  }
 }
 
 /**
  * Returns the autocomplete information of fields according to heuristics.
  */
 this.FormAutofillHeuristics = {
   FIELD_GROUPS: {
     NAME: [
@@ -198,16 +202,82 @@ this.FormAutofillHeuristics = {
     ],
     TEL: ["tel"],
     EMAIL: ["email"],
   },
 
   RULES: null,
 
   /**
+   * Try to match the telephone related fields to the grammar
+   * list to see if there is any valid telephone set and correct their
+   * field names.
+   *
+   * @param {FieldScanner} fieldScanner
+   *        The current parsing status for all elements
+   * @returns {boolean}
+   *          Return true if there is any field can be recognized in the parser,
+   *          otherwise false.
+   */
+  _parsePhoneFields(fieldScanner) {
+    let matchingResult;
+
+    const GRAMMARS = this.PHONE_FIELD_GRAMMARS;
+    for (let i = 0; i < GRAMMARS.length; i++) {
+      let detailStart = fieldScanner.parsingIndex;
+      let ruleStart = i;
+      for (; i < GRAMMARS.length && GRAMMARS[i][0] && fieldScanner.elementExisting(detailStart); i++, detailStart++) {
+        let detail = fieldScanner.getFieldDetailByIndex(detailStart);
+        if (!detail || GRAMMARS[i][0] != detail.fieldName) {
+          break;
+        }
+        let element = detail.elementWeakRef.get();
+        if (!element) {
+          break;
+        }
+        if (GRAMMARS[i][2] && (!element.maxLength || GRAMMARS[i][2] < element.maxLength)) {
+          break;
+        }
+      }
+      if (i >= GRAMMARS.length) {
+        break;
+      }
+
+      if (!GRAMMARS[i][0]) {
+        matchingResult = {
+          ruleFrom: ruleStart,
+          ruleTo: i,
+        };
+        break;
+      }
+
+      // Fast rewinding to the next rule.
+      for (; i < GRAMMARS.length; i++) {
+        if (!GRAMMARS[i][0]) {
+          break;
+        }
+      }
+    }
+
+    let parsedField = false;
+    if (matchingResult) {
+      let {ruleFrom, ruleTo} = matchingResult;
+      let detailStart = fieldScanner.parsingIndex;
+      for (let i = ruleFrom; i < ruleTo; i++) {
+        fieldScanner.updateFieldName(detailStart, GRAMMARS[i][1]);
+        fieldScanner.parsingIndex++;
+        detailStart++;
+        parsedField = true;
+      }
+    }
+
+    return parsedField;
+  },
+
+  /**
    * Try to find the correct address-line[1-3] sequence and correct their field
    * names.
    *
    * @param {FieldScanner} fieldScanner
    *        The current parsing status for all elements
    * @returns {boolean}
    *          Return true if there is any field can be recognized in the parser,
    *          otherwise false.
@@ -232,21 +302,22 @@ this.FormAutofillHeuristics = {
 
   getFormInfo(form) {
     if (form.autocomplete == "off" || form.elements.length <= 0) {
       return [];
     }
 
     let fieldScanner = new FieldScanner(form.elements);
     while (!fieldScanner.parsingFinished) {
+      let parsedPhoneFields = this._parsePhoneFields(fieldScanner);
       let parsedAddressFields = this._parseAddressFields(fieldScanner);
 
       // If there is no any field parsed, the parsing cursor can be moved
       // forward to the next one.
-      if (!parsedAddressFields) {
+      if (!parsedPhoneFields && !parsedAddressFields) {
         fieldScanner.parsingIndex++;
       }
     }
     return fieldScanner.trimmedFieldDetail;
   },
 
   /**
    * Get the autocomplete info (e.g. fieldName) determined by the regexp
@@ -374,21 +445,21 @@ this.FormAutofillHeuristics = {
     // Phone: <cc> <ac>:3 - <phone>:3 - <suffix>:4 (Ext: <ext>)?
       // {REGEX_PHONE, FIELD_COUNTRY_CODE, 0},
       // {REGEX_PHONE, FIELD_AREA_CODE, 3},
       // {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3},
       // {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 4},
       // {REGEX_SEPARATOR, FIELD_NONE, 0},
 
     // Phone: <cc>:3 <ac>:3 <phone>:3 <suffix>:4 (Ext: <ext>)?
-      // {REGEX_PHONE, FIELD_COUNTRY_CODE, 3},
-      // {REGEX_PHONE, FIELD_AREA_CODE, 3},
-      // {REGEX_PHONE, FIELD_PHONE, 3},
-      // {REGEX_PHONE, FIELD_SUFFIX, 4},
-      // {REGEX_SEPARATOR, FIELD_NONE, 0},
+    ["tel", "tel-country-code", 3],
+    ["tel", "tel-area-code", 3],
+    ["tel", "tel-local-prefix", 3],
+    ["tel", "tel-local-suffix", 4],
+    [null, null, 0],
 
     // Area Code: <ac> Phone: <phone> (- <suffix> (Ext: <ext>)?)?
       // {REGEX_AREA, FIELD_AREA_CODE, 0},
       // {REGEX_PHONE, FIELD_PHONE, 0},
       // {REGEX_SEPARATOR, FIELD_NONE, 0},
 
     // Phone: <ac> <phone>:3 <suffix>:4 (Ext: <ext>)?
       // {REGEX_PHONE, FIELD_AREA_CODE, 0},
@@ -423,20 +494,20 @@ this.FormAutofillHeuristics = {
 
     // Phone: <ac> Prefix: <phone> Suffix: <suffix> (Ext: <ext>)?
       // {REGEX_PHONE, FIELD_AREA_CODE, 0},
       // {REGEX_PREFIX, FIELD_PHONE, 0},
       // {REGEX_SUFFIX, FIELD_SUFFIX, 0},
       // {REGEX_SEPARATOR, FIELD_NONE, 0},
 
     // Phone: <ac> - <phone>:3 - <suffix>:4 (Ext: <ext>)?
-      // {REGEX_PHONE, FIELD_AREA_CODE, 0},
-      // {REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3},
-      // {REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 4},
-      // {REGEX_SEPARATOR, FIELD_NONE, 0},
+    ["tel", "tel-area-code", 0],
+    ["tel", "tel-local-prefix", 3],
+    ["tel", "tel-local-suffix", 4],
+    [null, null, 0],
 
     // Phone: <cc> - <ac> - <phone> (Ext: <ext>)?
       // {REGEX_PHONE, FIELD_COUNTRY_CODE, 0},
       // {REGEX_PREFIX_SEPARATOR, FIELD_AREA_CODE, 0},
       // {REGEX_SUFFIX_SEPARATOR, FIELD_PHONE, 0},
       // {REGEX_SEPARATOR, FIELD_NONE, 0},
 
     // Phone: <ac> - <phone> (Ext: <ext>)?