Bug 1370429 - Part 4: Implement grammar list matching algorithm for telephone fields. r=MattN
authorSean Lee <selee@mozilla.com>
Wed, 19 Jul 2017 10:18:06 +0800
changeset 420370 8225d170ecb25c94575c081814d9c2e2a2e20c7e
parent 420369 bd882520e8a4628f4116d8e74473f73a5569ad3b
child 420371 986bb6e88919374f8447c1a645b308ab54f731e3
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1370429
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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>)?