Bug 1383058 - Always adopt the info from the autocomplete attribute for autofill. r=MattN

MozReview-Commit-ID: CdAX9XZX7R0

--HG--
extra : amend_source : 93a5873c356eb0fdf9e5f70b3775b6b1c332d63a
This commit is contained in:
Sean Lee 2017-07-21 22:17:17 +08:00
parent 7aa262a783
commit 939e51f819
4 changed files with 86 additions and 34 deletions

View File

@ -115,13 +115,16 @@ FormAutofillHandler.prototype = {
/**
* Set fieldDetails from the form about fields that can be autofilled.
*
* @param {boolean} allowDuplicates
* true to remain any duplicated field details otherwise to remove the
* duplicated ones.
* @returns {Array} The valid address and credit card details.
*/
collectFormFields() {
collectFormFields(allowDuplicates = false) {
this._cacheValue.allFieldNames = null;
this._formFieldCount = this.form.elements.length;
let fieldDetails = FormAutofillHeuristics.getFormInfo(this.form);
let fieldDetails = FormAutofillHeuristics.getFormInfo(this.form, allowDuplicates);
this.fieldDetails = fieldDetails ? fieldDetails : [];
log.debug("Collected details on", this.fieldDetails.length, "fields");

View File

@ -123,6 +123,10 @@ class FieldScanner {
elementWeakRef: Cu.getWeakReference(element),
};
if (info._reason) {
fieldInfo._reason = info._reason;
}
// Store the association between the field metadata and the element.
if (this.findSameField(info) != -1) {
// A field with the same identifier already exists.
@ -204,7 +208,7 @@ this.FormAutofillHeuristics = {
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) {
if (!detail || GRAMMARS[i][0] != detail.fieldName || detail._reason == "autocomplete") {
break;
}
let element = detail.elementWeakRef.get();
@ -288,7 +292,23 @@ this.FormAutofillHeuristics = {
return parsedFields;
},
getFormInfo(form) {
/**
* This function should provide all field details of a form. The details
* contain the autocomplete info (e.g. fieldName, section, etc).
*
* `allowDuplicates` is used for the xpcshell-test purpose currently because
* the heuristics should be verified that some duplicated elements still can
* be predicted correctly.
*
* @param {HTMLFormElement} form
* the elements in this form to be predicted the field info.
* @param {boolean} allowDuplicates
* true to remain any duplicated field details otherwise to remove the
* duplicated ones.
* @returns {Array<Object>}
* all field details in the form.
*/
getFormInfo(form, allowDuplicates = false) {
if (form.autocomplete == "off" || form.elements.length <= 0) {
return [];
}
@ -304,6 +324,10 @@ this.FormAutofillHeuristics = {
fieldScanner.parsingIndex++;
}
}
if (allowDuplicates) {
return fieldScanner.fieldDetails;
}
return fieldScanner.trimmedFieldDetail;
},
@ -316,6 +340,7 @@ this.FormAutofillHeuristics = {
// An input[autocomplete="on"] will not be early return here since it stll
// needs to find the field name.
if (info && info.fieldName && info.fieldName != "on") {
info._reason = "autocomplete";
return info;
}

View File

@ -149,6 +149,7 @@ function runHeuristicsTest(patterns, fixturePathPrefix) {
Assert.equal(formInfo.length, testPattern.expectedResult[formIndex].length, "Expected field count.");
formInfo.forEach((field, fieldIndex) => {
let expectedField = testPattern.expectedResult[formIndex][fieldIndex];
delete field._reason;
expectedField.elementWeakRef = field.elementWeakRef;
Assert.deepEqual(field, expectedField);
});

View File

@ -174,28 +174,29 @@ const TESTCASES = [
{
description: "Three sets of adjacent phone number fields",
document: `<form>
<input id="shippingAreaCode" autocomplete="shipping tel" maxlength="3">
<input id="shippingPrefix" autocomplete="shipping tel" maxlength="3">
<input id="shippingSuffix" autocomplete="shipping tel" maxlength="4">
<input id="shippingTelExt" autocomplete="shipping tel-extension">
<input id="shippingAC" name="phone" maxlength="3">
<input id="shippingPrefix" name="phone" maxlength="3">
<input id="shippingSuffix" name="phone" maxlength="4">
<input id="shippingTelExt" name="extension">
<input id="billingAreaCode" autocomplete="billing tel" maxlength="3">
<input id="billingPrefix" autocomplete="billing tel" maxlength="3">
<input id="billingSuffix" autocomplete="billing tel" maxlength="4">
<input id="billingAC" name="phone" maxlength="3">
<input id="billingPrefix" name="phone" maxlength="3">
<input id="billingSuffix" name="phone" maxlength="4">
<input id="otherCountryCode" autocomplete="tel" maxlength="3">
<input id="otherAreaCode" autocomplete="tel" maxlength="3">
<input id="otherPrefix" autocomplete="tel" maxlength="3">
<input id="otherSuffix" autocomplete="tel" maxlength="4">
<input id="otherCC" name="phone" maxlength="3">
<input id="otherAC" name="phone" maxlength="3">
<input id="otherPrefix" name="phone" maxlength="3">
<input id="otherSuffix" name="phone" maxlength="4">
</form>`,
allowDuplicates: true,
addressFieldDetails: [
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel-area-code"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel-local-prefix"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel-local-suffix"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel-extension"},
{"section": "", "addressType": "billing", "contactType": "", "fieldName": "tel-area-code"},
{"section": "", "addressType": "billing", "contactType": "", "fieldName": "tel-local-prefix"},
{"section": "", "addressType": "billing", "contactType": "", "fieldName": "tel-local-suffix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-country-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"},
@ -203,22 +204,22 @@ const TESTCASES = [
],
creditCardFieldDetails: [],
validFieldDetails: [
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel-area-code"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel-local-prefix"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel-local-suffix"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel-extension"},
{"section": "", "addressType": "billing", "contactType": "", "fieldName": "tel-area-code"},
{"section": "", "addressType": "billing", "contactType": "", "fieldName": "tel-local-prefix"},
{"section": "", "addressType": "billing", "contactType": "", "fieldName": "tel-local-suffix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-country-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"},
],
ids: [
"shippingAreaCode", "shippingPrefix", "shippingSuffix", "shippingTelExt",
"billingAreaCode", "billingPrefix", "billingSuffix",
"otherCountryCode", "otherAreaCode", "otherPrefix", "otherSuffix",
"shippingAC", "shippingPrefix", "shippingSuffix", "shippingTelExt",
"billingAC", "billingPrefix", "billingSuffix",
"otherCC", "otherAC", "otherPrefix", "otherSuffix",
],
},
{
@ -290,6 +291,28 @@ const TESTCASES = [
ids: ["i1", "i2", "i3", "i4", "singlePhone",
"shippingAreaCode", "shippingPrefix", "shippingSuffix"],
},
{
description: "Always adopt the info from autocomplete attribute.",
document: `<form>
<input id="given-name" autocomplete="shipping given-name">
<input id="family-name" autocomplete="shipping family-name">
<input id="dummyAreaCode" autocomplete="shipping tel" maxlength="3">
<input id="dummyPrefix" autocomplete="shipping tel" maxlength="3">
<input id="dummySuffix" autocomplete="shipping tel" maxlength="4">
</form>`,
addressFieldDetails: [
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel"},
],
creditCardFieldDetails: [],
validFieldDetails: [
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel"},
],
ids: ["given-name", "family-name", "dummyAreaCode"],
},
];
for (let tc of TESTCASES) {
@ -340,7 +363,7 @@ for (let tc of TESTCASES) {
].forEach(details => setElementWeakRef(details));
let handler = new FormAutofillHandler(formLike);
let validFieldDetails = handler.collectFormFields();
let validFieldDetails = handler.collectFormFields(testcase.allowDuplicates);
verifyDetails(handler.address.fieldDetails, testcase.addressFieldDetails);
verifyDetails(handler.creditCard.fieldDetails, testcase.creditCardFieldDetails);