Bug 1359892 - [Form Autofill] Support full-name fields. r=MattN
MozReview-Commit-ID: 3G3d5nv6j7v
--- a/browser/extensions/formautofill/FormAutofillHeuristics.jsm
+++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm
@@ -12,16 +12,17 @@ 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: [
+ "name",
"given-name",
"additional-name",
"family-name",
"organization",
"street-address",
"address-line1",
"address-line2",
"address-line3",
--- a/browser/extensions/formautofill/FormAutofillNameUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillNameUtils.jsm
@@ -121,16 +121,20 @@ var FormAutofillNameUtils = {
// no more than 2 words.
//
// Chinese and Japanese names are usually spelled out using the Han
// characters (logographs), which constitute the "CJK Unified Ideographs"
// block in Unicode, also referred to as Unihan. Korean names are usually
// spelled out in the Korean alphabet (Hangul), although they do have a Han
// equivalent as well.
+ if (!name) {
+ return false;
+ }
+
let previousWasCJK = false;
let wordCount = 0;
for (let c of name) {
let isMiddleDot = this.MIDDLE_DOT.includes(c);
let isCJK = !isMiddleDot && this.reCJK.test(c);
if (!isCJK && !isMiddleDot && !this.WHITESPACE.includes(c)) {
return false;
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillUtils.jsm
@@ -16,29 +16,16 @@ this.FormAutofillUtils = {
let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
return new ConsoleAPI({
maxLogLevelPref: "browser.formautofill.loglevel",
prefix: logPrefix,
});
});
},
- generateFullName(firstName, lastName, middleName) {
- // TODO: The implementation should depend on the L10N spec, but a simplified
- // rule is used here.
- let fullName = firstName;
- if (middleName) {
- fullName += " " + middleName;
- }
- if (lastName) {
- fullName += " " + lastName;
- }
- return fullName;
- },
-
findLabelElements(element) {
let document = element.ownerDocument;
let labels = [];
// TODO: querySelectorAll is inefficient here. However, bug 1339726 is for
// a more efficient implementation from DOM API perspective. This function
// should be refined after input.labels API landed.
for (let label of document.querySelectorAll("label[for]")) {
if (element.id == label.htmlFor) {
--- a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
+++ b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
@@ -95,52 +95,43 @@ ProfileAutoCompleteResult.prototype = {
"given-name",
"additional-name",
"family-name",
];
focusedFieldName = possibleNameFields.includes(focusedFieldName) ?
"name" : focusedFieldName;
- // 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
"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 ||
- !clonedProfile[currentFieldName]) {
+ !profile[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 profile[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
@@ -295,16 +295,24 @@ ProfileStorage.prototype = {
return Object.assign({}, profile);
},
_findByGUID(guid) {
return this._store.data.profiles.find(profile => profile.guid == guid);
},
_computeFields(profile) {
+ // Compute name
+ profile.name = FormAutofillNameUtils.joinNameParts({
+ given: profile["given-name"],
+ middle: profile["additional-name"],
+ family: profile["family-name"],
+ });
+
+ // Compute address
if (profile["street-address"]) {
let streetAddress = profile["street-address"].split("\n");
// TODO: we should prevent the dataloss by concatenating the rest of lines
// with a locale-specific character in the future (bug 1360114).
for (let i = 0; i < 3; i++) {
if (streetAddress[i]) {
profile["address-line" + (i + 1)] = streetAddress[i];
}
@@ -332,17 +340,36 @@ ProfileStorage.prototype = {
// Concatenate "address-line*" if "street-address" is omitted.
if (!profile["street-address"]) {
profile["street-address"] = addressLines.join("\n");
}
}
},
+ _normalizeName(profile) {
+ if (!profile.name) {
+ return;
+ }
+
+ let nameParts = FormAutofillNameUtils.splitName(profile.name);
+ if (!profile["given-name"] && nameParts.given) {
+ profile["given-name"] = nameParts.given;
+ }
+ if (!profile["additional-name"] && nameParts.middle) {
+ profile["additional-name"] = nameParts.middle;
+ }
+ if (!profile["family-name"] && nameParts.family) {
+ profile["family-name"] = nameParts.family;
+ }
+ delete profile.name;
+ },
+
_normalizeProfile(profile) {
+ this._normalizeName(profile);
this._normalizeAddress(profile);
for (let key in profile) {
if (!VALID_FIELDS.includes(key)) {
throw new Error(`"${key}" is not a valid field.`);
}
if (typeof profile[key] !== "string" &&
typeof profile[key] !== "number") {
--- a/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
+++ b/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
@@ -1,23 +1,25 @@
"use strict";
Cu.import("resource://formautofill/ProfileAutoCompleteResult.jsm");
let matchingProfiles = [{
guid: "test-guid-1",
"given-name": "Timothy",
"family-name": "Berners-Lee",
+ name: "Timothy 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",
+ name: "John 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",
--- a/browser/extensions/formautofill/test/unit/test_transformFields.js
+++ b/browser/extensions/formautofill/test/unit/test_transformFields.js
@@ -14,16 +14,32 @@ const COMPUTE_TESTCASES = [
{
description: "Empty profile",
profile: {
},
expectedResult: {
},
},
+ // Name
+ {
+ description: "Has split names",
+ profile: {
+ "given-name": "Timothy",
+ "additional-name": "John",
+ "family-name": "Berners-Lee",
+ },
+ expectedResult: {
+ "given-name": "Timothy",
+ "additional-name": "John",
+ "family-name": "Berners-Lee",
+ "name": "Timothy John Berners-Lee",
+ },
+ },
+
// Address
{
description: "\"street-address\" with single line",
profile: {
"street-address": "single line",
},
expectedResult: {
"street-address": "single line",
@@ -73,16 +89,55 @@ const NORMALIZE_TESTCASES = [
{
description: "Empty profile",
profile: {
},
expectedResult: {
},
},
+ // Name
+ {
+ description: "Has \"name\", and the split names are omitted",
+ profile: {
+ "name": "Timothy John Berners-Lee",
+ },
+ expectedResult: {
+ "given-name": "Timothy",
+ "additional-name": "John",
+ "family-name": "Berners-Lee",
+ },
+ },
+ {
+ description: "Has both \"name\" and split names",
+ profile: {
+ "name": "John Doe",
+ "given-name": "Timothy",
+ "additional-name": "John",
+ "family-name": "Berners-Lee",
+ },
+ expectedResult: {
+ "given-name": "Timothy",
+ "additional-name": "John",
+ "family-name": "Berners-Lee",
+ },
+ },
+ {
+ description: "Has \"name\", and some of split names are omitted",
+ profile: {
+ "name": "John Doe",
+ "given-name": "Timothy",
+ },
+ expectedResult: {
+ "given-name": "Timothy",
+ "family-name": "Doe",
+ },
+ },
+
+
// Address
{
description: "Has \"address-line1~3\" and \"street-address\" is omitted",
profile: {
"address-line1": "line1",
"address-line2": "line2",
"address-line3": "line3",
},