Bug 1334037 - [Form Autofill] Support name fields in profiles. r=MattN draft
authorLuke Chang <lchang@mozilla.com>
Thu, 30 Mar 2017 18:08:54 +0800
changeset 568661 301754d14f51449ce5aa0e5a074d51856ac0ec1c
parent 566789 8e969cc9aff49f845678cba5b35d9dd8aa340f16
child 625993 bb7b72776ab2f8f44e910f47a5af2340617570b2
push id55944
push userbmo:lchang@mozilla.com
push dateWed, 26 Apr 2017 12:35:46 +0000
reviewersMattN
bugs1334037
milestone55.0a1
Bug 1334037 - [Form Autofill] Support name fields in profiles. r=MattN MozReview-Commit-ID: AW2CVAY0ghi
browser/extensions/formautofill/FormAutofillHeuristics.jsm
browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
browser/extensions/formautofill/ProfileStorage.jsm
browser/extensions/formautofill/test/unit/test_collectFormFields.js
browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
browser/extensions/formautofill/test/unit/test_profileStorage.js
--- a/browser/extensions/formautofill/FormAutofillHeuristics.jsm
+++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm
@@ -12,16 +12,19 @@ this.EXPORTED_SYMBOLS = ["FormAutofillHe
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 /**
  * Returns the autocomplete information of fields according to heuristics.
  */
 this.FormAutofillHeuristics = {
   VALID_FIELDS: [
+    "given-name",
+    "additional-name",
+    "family-name",
     "organization",
     "street-address",
     "address-level2",
     "address-level1",
     "postal-code",
     "country",
     "tel",
     "email",
--- a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
+++ b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
@@ -85,42 +85,62 @@ ProfileAutoCompleteResult.prototype = {
    * @param   {Array<Object>} allFieldNames The field names in the same section
    * @param   {object} profile The profile providing the labels to show.
    * @returns {string} The secondary label
    */
   _getSecondaryLabel(focusedFieldName, allFieldNames, profile) {
     /* TODO: Since "name" is a special case here, so the secondary "name" label
        will be refined when the handling rule for "name" is ready.
     */
-    const possibleNameFields = ["given-name", "additional-name", "family-name"];
+    const possibleNameFields = [
+      "name",
+      "given-name",
+      "additional-name",
+      "family-name",
+    ];
+
     focusedFieldName = possibleNameFields.includes(focusedFieldName) ?
                        "name" : focusedFieldName;
-    if (!profile.name) {
-      profile.name = FormAutofillUtils.generateFullName(profile["given-name"],
-                                                        profile["family-name"],
-                                                        profile["additional-name"]);
+
+    // Clones the profile to avoid exposing our modification.
+    let clonedProfile = Object.assign({}, profile);
+    if (!clonedProfile.name) {
+      clonedProfile.name =
+        FormAutofillUtils.generateFullName(clonedProfile["given-name"],
+                                           clonedProfile["family-name"],
+                                           clonedProfile["additional-name"]);
     }
 
     const secondaryLabelOrder = [
       "street-address",  // Street address
-      "name",            // Full name if needed
+      "name",            // Full name
       "address-level2",  // City/Town
       "organization",    // Company or organization name
       "address-level1",  // Province/State (Standardized code if possible)
       "country",         // Country
       "postal-code",     // Postal code
       "tel",             // Phone number
       "email",           // Email address
     ];
 
     for (const currentFieldName of secondaryLabelOrder) {
-      if (focusedFieldName != currentFieldName &&
-          allFieldNames.includes(currentFieldName) &&
-          profile[currentFieldName]) {
-        return profile[currentFieldName];
+      if (focusedFieldName == currentFieldName ||
+          !clonedProfile[currentFieldName]) {
+        continue;
+      }
+
+      let matching;
+      if (currentFieldName == "name") {
+        matching = allFieldNames.some(fieldName => possibleNameFields.includes(fieldName));
+      } else {
+        matching = allFieldNames.includes(currentFieldName);
+      }
+
+      if (matching) {
+        return clonedProfile[currentFieldName];
       }
     }
 
     return ""; // Nothing matched.
   },
 
   _generateLabels(focusedFieldName, allFieldNames, profiles) {
     // Skip results without a primary label.
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -10,16 +10,19 @@
  *
  * {
  *   version: 1,
  *   profiles: [
  *     {
  *       guid,             // 12 character...
  *
  *       // profile
+ *       given-name,
+ *       additional-name,
+ *       family-name,
  *       organization,     // Company
  *       street-address,    // (Multiline)
  *       address-level2,    // City/Town
  *       address-level1,    // Province (Standardized code if possible)
  *       postal-code,
  *       country,          // ISO 3166
  *       tel,
  *       email,
@@ -58,16 +61,19 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 this.log = null;
 FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
 
 const SCHEMA_VERSION = 1;
 
 // Name-related fields will be handled in follow-up bugs due to the complexity.
 const VALID_FIELDS = [
+  "given-name",
+  "additional-name",
+  "family-name",
   "organization",
   "street-address",
   "address-level2",
   "address-level1",
   "postal-code",
   "country",
   "tel",
   "email",
--- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -20,16 +20,18 @@ const TESTCASES = [
     document: `<form><input id="given-name" autocomplete="given-name">
                <input id="family-name" autocomplete="family-name">
                <input id="street-addr" autocomplete="street-address">
                <input id="city" autocomplete="address-level2">
                <input id="country" autocomplete="country">
                <input id="email" autocomplete="email">
                <input id="tel" autocomplete="tel"></form>`,
     fieldDetails: [
+      {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
+      {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"},
     ],
   },
   {
@@ -37,16 +39,18 @@ const TESTCASES = [
     document: `<form><input id="given-name" autocomplete="shipping given-name">
                <input id="family-name" autocomplete="shipping family-name">
                <input id="street-addr" autocomplete="shipping street-address">
                <input id="city" autocomplete="shipping address-level2">
                <input id="country" autocomplete="shipping country">
                <input id='email' autocomplete="shipping email">
                <input id="tel" autocomplete="shipping tel"></form>`,
     fieldDetails: [
+      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
+      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel"},
     ],
   },
   {
@@ -54,16 +58,18 @@ const TESTCASES = [
     document: `<form><input id="given-name" autocomplete="shipping given-name">
                <input id="family-name" autocomplete="shipping family-name">
                <input id="street-addr" autocomplete="shipping street-address">
                <input id="city" autocomplete="shipping address-level2">
                <input id="country" autocomplete="shipping country">
                <input id='email' autocomplete="shipping email">
                <input id="tel" autocomplete="shipping tel"></form>`,
     fieldDetails: [
+      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
+      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel"},
     ],
   },
 ];
--- a/browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
+++ b/browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
@@ -1,25 +1,31 @@
 "use strict";
 
 Cu.import("resource://formautofill/FormAutofillContent.jsm");
 
 const TESTCASES = [
   {
-    description: "Form containing 5 fields with autocomplete attribute.",
+    description: "Form containing 8 fields with autocomplete attribute.",
     document: `<form>
+                 <input id="given-name" autocomplete="given-name">
+                 <input id="additional-name" autocomplete="additional-name">
+                 <input id="family-name" autocomplete="family-name">
                  <input id="street-addr" autocomplete="street-address">
                  <input id="city" autocomplete="address-level2">
                  <input id="country" autocomplete="country">
                  <input id="email" autocomplete="email">
                  <input id="tel" autocomplete="tel">
                  <input id="without-autocomplete-1">
                  <input id="without-autocomplete-2">
                </form>`,
     expectedResult: [
+      "given-name",
+      "additional-name",
+      "family-name",
       "street-addr",
       "city",
       "country",
       "email",
       "tel",
     ],
   },
   {
--- a/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
+++ b/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
@@ -1,30 +1,34 @@
 "use strict";
 
 Cu.import("resource://formautofill/ProfileAutoCompleteResult.jsm");
 
 let matchingProfiles = [{
   guid: "test-guid-1",
+  "given-name": "Timothy",
+  "family-name": "Berners-Lee",
   organization: "Sesame Street",
   "street-address": "123 Sesame Street.",
   tel: "1-345-345-3456.",
 }, {
   guid: "test-guid-2",
+  "given-name": "John",
+  "family-name": "Doe",
   organization: "Mozilla",
   "street-address": "331 E. Evelyn Avenue",
   tel: "1-650-903-0800",
 }, {
   guid: "test-guid-3",
   organization: "",
   "street-address": "321, No Name St.",
   tel: "1-000-000-0000",
 }];
 
-let allFieldNames = ["street-address", "organization", "tel"];
+let allFieldNames = ["given-name", "family-name", "street-address", "organization", "tel"];
 
 let testCases = [{
   description: "Focus on an `organization` field",
   options: {},
   matchingProfiles,
   allFieldNames,
   searchString: "",
   fieldName: "organization",
@@ -101,26 +105,26 @@ let testCases = [{
     searchResult: Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
     defaultIndex: 0,
     items: [{
       value: "123 Sesame Street.",
       style: "autofill-profile",
       comment: JSON.stringify(matchingProfiles[0]),
       label: JSON.stringify({
         primary: "123 Sesame Street.",
-        secondary: "Sesame Street",
+        secondary: "Timothy Berners-Lee",
       }),
       image: "",
     }, {
       value: "331 E. Evelyn Avenue",
       style: "autofill-profile",
       comment: JSON.stringify(matchingProfiles[1]),
       label: JSON.stringify({
         primary: "331 E. Evelyn Avenue",
-        secondary: "Mozilla",
+        secondary: "John Doe",
       }),
       image: "",
     }, {
       value: "321, No Name St.",
       style: "autofill-profile",
       comment: JSON.stringify(matchingProfiles[2]),
       label: JSON.stringify({
         primary: "321, No Name St.",
--- a/browser/extensions/formautofill/test/unit/test_profileStorage.js
+++ b/browser/extensions/formautofill/test/unit/test_profileStorage.js
@@ -5,16 +5,19 @@
 "use strict";
 
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://formautofill/ProfileStorage.jsm");
 
 const TEST_STORE_FILE_NAME = "test-profile.json";
 
 const TEST_PROFILE_1 = {
+  "given-name": "Timothy",
+  "additional-name": "John",
+  "family-name": "Berners-Lee",
   organization: "World Wide Web Consortium",
   "street-address": "32 Vassar Street\nMIT Room 32-G524",
   "address-level2": "Cambridge",
   "address-level1": "MA",
   "postal-code": "02139",
   country: "US",
   tel: "+1 617 253 5702",
   email: "timbl@w3.org",