Bug 1416664 - Identify the sections for the fields without the section part of autocomplete attr. r=lchang,ralin

MozReview-Commit-ID: 7La8Bn0TF1y

--HG--
extra : rebase_source : 4864178cdfbb644912bcfb9dc1a45806f1cae46d
This commit is contained in:
Sean Lee 2017-11-22 15:57:33 +08:00
Родитель 9ee1f72e41
Коммит a7c09638b3
3 изменённых файлов: 153 добавлений и 44 удалений

Просмотреть файл

@ -21,6 +21,7 @@ FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
const PREF_HEURISTICS_ENABLED = "extensions.formautofill.heuristics.enabled"; const PREF_HEURISTICS_ENABLED = "extensions.formautofill.heuristics.enabled";
const PREF_SECTION_ENABLED = "extensions.formautofill.section.enabled"; const PREF_SECTION_ENABLED = "extensions.formautofill.section.enabled";
const DEFAULT_SECTION_NAME = "-moz-section-default";
/** /**
* A scanner for traversing all elements in a form and retrieving the field * A scanner for traversing all elements in a form and retrieving the field
@ -35,11 +36,13 @@ class FieldScanner {
* @param {Array.DOMElement} elements * @param {Array.DOMElement} elements
* The elements from a form for each parser. * The elements from a form for each parser.
*/ */
constructor(elements) { constructor(elements, {allowDuplicates = false, sectionEnabled = true}) {
this._elementsWeakRef = Cu.getWeakReference(elements); this._elementsWeakRef = Cu.getWeakReference(elements);
this.fieldDetails = []; this.fieldDetails = [];
this._parsingIndex = 0; this._parsingIndex = 0;
this._sections = []; this._sections = [];
this._allowDuplicates = allowDuplicates;
this._sectionEnabled = sectionEnabled;
} }
get _elements() { get _elements() {
@ -112,22 +115,63 @@ class FieldScanner {
}); });
} }
getSectionFieldDetails(allowDuplicates) { _classifySections() {
// TODO: [Bug 1416664] If there is only one section which is not defined by let fieldDetails = this._sections[0].fieldDetails;
// `autocomplete` attribute, the sections should be classified by the this._sections = [];
// heuristics. let seenTypes = new Set();
return this._sections.map(section => { let previousType;
if (allowDuplicates) { let sectionCount = 0;
return section.fieldDetails;
for (let fieldDetail of fieldDetails) {
if (!fieldDetail.fieldName) {
continue;
} }
return this._trimFieldDetails(section.fieldDetails); if (seenTypes.has(fieldDetail.fieldName) &&
}); previousType != fieldDetail.fieldName) {
seenTypes.clear();
sectionCount++;
}
previousType = fieldDetail.fieldName;
seenTypes.add(fieldDetail.fieldName);
delete fieldDetail._duplicated;
this._pushToSection(DEFAULT_SECTION_NAME + "-" + sectionCount, fieldDetail);
}
}
/**
* The result is an array contains the sections with its belonging field
* details. If `this._sections` contains one section only with the default
* section name (DEFAULT_SECTION_NAME), `this._classifySections` should be
* able to identify all sections in the heuristic way.
*
* @returns {Array<Object>}
* The array with the sections, and the belonging fieldDetails are in
* each section.
*/
getSectionFieldDetails() {
// When the section feature is disabled, `getSectionFieldDetails` should
// provide a single section result.
if (!this._sectionEnabled) {
return [this._getFinalDetails(this.fieldDetails)];
}
if (this._sections.length == 0) {
return [];
}
if (this._sections.length == 1 && this._sections[0].name == DEFAULT_SECTION_NAME) {
this._classifySections();
}
return this._sections.map(section =>
this._getFinalDetails(section.fieldDetails)
);
} }
/** /**
* This function will prepare an autocomplete info object with getInfo * This function will prepare an autocomplete info object with getInfo
* function and push the detail to fieldDetails property. Any duplicated * function and push the detail to fieldDetails property. Any duplicated
* detail will be marked as _duplicated = true for the parser. * detail will be marked as _duplicated = true for the parser.
* Any field will be pushed into `this._sections` based on the section name
* in `autocomplete` attribute.
* *
* Any element without the related detail will be used for adding the detail * Any element without the related detail will be used for adding the detail
* to the end of field details. * to the end of field details.
@ -173,7 +217,7 @@ class FieldScanner {
if (info.addressType) { if (info.addressType) {
names.push(info.addressType); names.push(info.addressType);
} }
return names.length ? names.join(" ") : "-moz-section-default"; return names.length ? names.join(" ") : DEFAULT_SECTION_NAME;
} }
/** /**
@ -205,7 +249,10 @@ class FieldScanner {
} }
/** /**
* Provide the field details without invalid field name and duplicated fields. * Provide the final field details without invalid field name, and the
* duplicated fields will be removed as well. For the debugging purpose,
* the final `fieldDetails` will include the duplicated fields if
* `_allowDuplicates` is true.
* *
* @param {Array<Object>} fieldDetails * @param {Array<Object>} fieldDetails
* The field details for trimming. * The field details for trimming.
@ -213,14 +260,13 @@ class FieldScanner {
* The array with the field details without invalid field name and * The array with the field details without invalid field name and
* duplicated fields. * duplicated fields.
*/ */
_trimFieldDetails(fieldDetails) { _getFinalDetails(fieldDetails) {
if (this._allowDuplicates) {
return fieldDetails.filter(f => f.fieldName);
}
return fieldDetails.filter(f => f.fieldName && !f._duplicated); return fieldDetails.filter(f => f.fieldName && !f._duplicated);
} }
getFieldDetails(allowDuplicates) {
return allowDuplicates ? this.fieldDetails : this._trimFieldDetails(this.fieldDetails);
}
elementExisting(index) { elementExisting(index) {
return index < this._elements.length; return index < this._elements.length;
} }
@ -651,7 +697,8 @@ this.FormAutofillHeuristics = {
return []; return [];
} }
let fieldScanner = new FieldScanner(eligibleFields); let fieldScanner = new FieldScanner(eligibleFields,
{allowDuplicates, sectionEnabled: this._sectionEnabled});
while (!fieldScanner.parsingFinished) { while (!fieldScanner.parsingFinished) {
let parsedPhoneFields = this._parsePhoneFields(fieldScanner); let parsedPhoneFields = this._parsePhoneFields(fieldScanner);
let parsedAddressFields = this._parseAddressFields(fieldScanner); let parsedAddressFields = this._parseAddressFields(fieldScanner);
@ -666,13 +713,7 @@ this.FormAutofillHeuristics = {
LabelUtils.clearLabelMap(); LabelUtils.clearLabelMap();
if (!this._sectionEnabled) { return fieldScanner.getSectionFieldDetails();
// When the section feature is disabled, `getFormInfo` should provide a
// single section result.
return [fieldScanner.getFieldDetails(allowDuplicates)];
}
return fieldScanner.getSectionFieldDetails(allowDuplicates);
}, },
_regExpTableHashValue(...signBits) { _regExpTableHashValue(...signBits) {

Просмотреть файл

@ -11,32 +11,69 @@
<label>Organization: <input id="organization" autocomplete="organization"></label><br/> <label>Organization: <input id="organization" autocomplete="organization"></label><br/>
<br/> <br/>
<label>Street Address: <input id="street-address" autocomplete="shipping street-address"></label><br/> <label>Street Address: <input id="street-address-a" autocomplete="shipping street-address"></label><br/>
<label>Address Level 2: <input id="address-level2" autocomplete="shipping address-level2"></label><br/> <label>Address Level 2: <input id="address-level2-a" autocomplete="shipping address-level2"></label><br/>
<label>Address Level 1: <input id="address-level1" autocomplete="shipping address-level1"></label><br/> <label>Address Level 1: <input id="address-level1-a" autocomplete="shipping address-level1"></label><br/>
<label>Postal Code: <input id="postal-code" autocomplete="shipping postal-code"></label><br/> <label>Postal Code: <input id="postal-code-a" autocomplete="shipping postal-code"></label><br/>
<label>Country: <input id="country" autocomplete="shipping country"></label><br/> <label>Country: <input id="country-a" autocomplete="shipping country"></label><br/>
<br/> <br/>
<label>Street Address: <input id="street-address" autocomplete="billing street-address"></label><br/> <label>Street Address: <input id="street-address-b" autocomplete="billing street-address"></label><br/>
<label>Address Level 2: <input id="address-level2" autocomplete="billing address-level2"></label><br/> <label>Address Level 2: <input id="address-level2-b" autocomplete="billing address-level2"></label><br/>
<label>Address Level 1: <input id="address-level1" autocomplete="billing address-level1"></label><br/> <label>Address Level 1: <input id="address-level1-b" autocomplete="billing address-level1"></label><br/>
<label>Postal Code: <input id="postal-code" autocomplete="billing postal-code"></label><br/> <label>Postal Code: <input id="postal-code-b" autocomplete="billing postal-code"></label><br/>
<label>Country: <input id="country" autocomplete="billing country"></label><br/> <label>Country: <input id="country-b" autocomplete="billing country"></label><br/>
<br/> <br/>
<label>Street Address: <input id="street-address" autocomplete="section-my street-address"></label><br/> <label>Street Address: <input id="street-address-c" autocomplete="section-my street-address"></label><br/>
<label>Address Level 2: <input id="address-level2" autocomplete="section-my address-level2"></label><br/> <label>Address Level 2: <input id="address-level2-c" autocomplete="section-my address-level2"></label><br/>
<label>Address Level 1: <input id="address-level1" autocomplete="section-my address-level1"></label><br/> <label>Address Level 1: <input id="address-level1-c" autocomplete="section-my address-level1"></label><br/>
<label>Postal Code: <input id="postal-code" autocomplete="section-my postal-code"></label><br/> <label>Postal Code: <input id="postal-code-c" autocomplete="section-my postal-code"></label><br/>
<label>Country: <input id="country" autocomplete="section-my country"></label><br/> <label>Country: <input id="country-c" autocomplete="section-my country"></label><br/>
<br/> <br/>
<label>Telephone: <input id="tel" autocomplete="work tel"></label><br/> <label>Telephone: <input id="tel-a" autocomplete="work tel"></label><br/>
<label>Email: <input id="email" autocomplete="work email"></label><br/> <label>Email: <input id="email-a" autocomplete="work email"></label><br/>
<br/> <br/>
<label>Telephone: <input id="tel" autocomplete="home tel"></label><br/> <label>Telephone: <input id="tel-b" autocomplete="home tel"></label><br/>
<label>Email: <input id="email" autocomplete="home email"></label><br/> <label>Email: <input id="email-b" autocomplete="home email"></label><br/>
<p>
<input type="submit" value="Submit">
<button type="reset">Reset</button>
</p>
</form>
<form>
<label>Name: <input autocomplete="name"></label><br/>
<label>Organization: <input autocomplete="organization"></label><br/>
<br/>
<label>Street Address: <input autocomplete="street-address"></label><br/>
<label>Address Level 2: <input autocomplete="address-level2"></label><br/>
<label>Address Level 1: <input autocomplete="address-level1"></label><br/>
<label>Postal Code: <input autocomplete="postal-code"></label><br/>
<label>Country: <input autocomplete="country"></label><br/>
<br/>
<label>Street Address: <input autocomplete="street-address"></label><br/>
<label>Address Level 2: <input autocomplete="address-level2"></label><br/>
<label>Address Level 1: <input autocomplete="address-level1"></label><br/>
<label>Postal Code: <input autocomplete="postal-code"></label><br/>
<label>Country: <input autocomplete="country"></label><br/>
<br/>
<label>Street Address: <input autocomplete="street-address"></label><br/>
<label>Address Level 2: <input autocomplete="address-level2"></label><br/>
<label>Address Level 1: <input autocomplete="address-level1"></label><br/>
<label>Postal Code: <input autocomplete="postal-code"></label><br/>
<label>Country: <input autocomplete="country"></label><br/>
<br/>
<label>Telephone: <input autocomplete="work tel"></label><br/>
<label>Email: <input autocomplete="work email"></label><br/>
<br/>
<label>Telephone: <input autocomplete="home tel"></label><br/>
<label>Email: <input autocomplete="home email"></label><br/>
<p> <p>
<input type="submit" value="Submit"> <input type="submit" value="Submit">
<button type="reset">Reset</button> <button type="reset">Reset</button>

Просмотреть файл

@ -41,6 +41,37 @@ runHeuristicsTest([
{"section": "section-my", "addressType": "", "contactType": "", "fieldName": "country"}, {"section": "section-my", "addressType": "", "contactType": "", "fieldName": "country"},
], ],
], ],
[
[
{"section": "", "addressType": "", "contactType": "", "fieldName": "name"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "organization"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
],
[
{"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
],
[
{"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
{"section": "", "addressType": "", "contactType": "work", "fieldName": "tel"},
{"section": "", "addressType": "", "contactType": "work", "fieldName": "email"},
],
[
{"section": "", "addressType": "", "contactType": "home", "fieldName": "tel"},
{"section": "", "addressType": "", "contactType": "home", "fieldName": "email"},
],
],
], ],
}, },
], "../../fixtures/"); ], "../../fixtures/");