Skip to content

Commit

Permalink
Bug 1387988 - [Form Autofill] Move "findLabelElements" function to Fo…
Browse files Browse the repository at this point in the history
…rmAutofillHeuristics.jsm. r=MattN

MozReview-Commit-ID: 93c9R7JaCLA

--HG--
extra : rebase_source : 3621757255f250e3e95281cc401e039e37891e44
  • Loading branch information
Luke Chang committed Aug 7, 2017
1 parent c24c7ab commit 0def08b
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 83 deletions.
84 changes: 81 additions & 3 deletions browser/extensions/formautofill/FormAutofillHeuristics.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

"use strict";

this.EXPORTED_SYMBOLS = ["FormAutofillHeuristics"];
this.EXPORTED_SYMBOLS = ["FormAutofillHeuristics", "LabelUtils"];

const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;

Expand Down Expand Up @@ -182,6 +182,84 @@ class FieldScanner {
}
}

this.LabelUtils = {
// The tag name list is from Chromium except for "STYLE":
// eslint-disable-next-line max-len
// https://cs.chromium.org/chromium/src/components/autofill/content/renderer/form_autofill_util.cc?l=216&rcl=d33a171b7c308a64dc3372fac3da2179c63b419e
EXCLUDED_TAGS: ["SCRIPT", "NOSCRIPT", "OPTION", "STYLE"],
/**
* Extract all strings of an element's children to an array.
* "element.textContent" is a string which is merged of all children nodes,
* and this function provides an array of the strings contains in an element.
*
* @param {Object} element
* A DOM element to be extracted.
* @returns {Array}
* All strings in an element.
*/
extractLabelStrings(element) {
let strings = [];
let _extractLabelStrings = (el) => {
if (this.EXCLUDED_TAGS.includes(el.tagName)) {
return;
}

if (el.nodeType == Ci.nsIDOMNode.TEXT_NODE ||
el.childNodes.length == 0) {
let trimmedText = el.textContent.trim();
if (trimmedText) {
strings.push(trimmedText);
}
return;
}

for (let node of el.childNodes) {
if (node.nodeType != Ci.nsIDOMNode.ELEMENT_NODE &&
node.nodeType != Ci.nsIDOMNode.TEXT_NODE) {
continue;
}
_extractLabelStrings(node);
}
};
_extractLabelStrings(element);
return strings;
},

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) {
labels.push(label);
}
}

if (labels.length > 0) {
log.debug("Label found by ID", element.id);
return labels;
}

let parent = element.parentNode;
if (!parent) {
return [];
}
do {
if (parent.tagName == "LABEL" &&
parent.control == element &&
!parent.hasAttribute("for")) {
log.debug("Label found in input's parent or ancestor.");
return [parent];
}
parent = parent.parentNode;
} while (parent);

return [];
},
};

/**
* Returns the autocomplete information of fields according to heuristics.
*/
Expand Down Expand Up @@ -370,9 +448,9 @@ this.FormAutofillHeuristics = {
yield element.name;
if (!labelStrings) {
labelStrings = [];
let labels = FormAutofillUtils.findLabelElements(element);
let labels = LabelUtils.findLabelElements(element);
for (let label of labels) {
labelStrings.push(...FormAutofillUtils.extractLabelStrings(label));
labelStrings.push(...LabelUtils.extractLabelStrings(label));
}
}
yield *labelStrings;
Expand Down
76 changes: 0 additions & 76 deletions browser/extensions/formautofill/FormAutofillUtils.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -128,82 +128,6 @@ this.FormAutofillUtils = {
return true;
},

// The tag name list is from Chromium except for "STYLE":
// eslint-disable-next-line max-len
// https://cs.chromium.org/chromium/src/components/autofill/content/renderer/form_autofill_util.cc?l=216&rcl=d33a171b7c308a64dc3372fac3da2179c63b419e
EXCLUDED_TAGS: ["SCRIPT", "NOSCRIPT", "OPTION", "STYLE"],
/**
* Extract all strings of an element's children to an array.
* "element.textContent" is a string which is merged of all children nodes,
* and this function provides an array of the strings contains in an element.
*
* @param {Object} element
* A DOM element to be extracted.
* @returns {Array}
* All strings in an element.
*/
extractLabelStrings(element) {
let strings = [];
let _extractLabelStrings = (el) => {
if (this.EXCLUDED_TAGS.includes(el.tagName)) {
return;
}

if (el.nodeType == Ci.nsIDOMNode.TEXT_NODE ||
el.childNodes.length == 0) {
let trimmedText = el.textContent.trim();
if (trimmedText) {
strings.push(trimmedText);
}
return;
}

for (let node of el.childNodes) {
if (node.nodeType != Ci.nsIDOMNode.ELEMENT_NODE &&
node.nodeType != Ci.nsIDOMNode.TEXT_NODE) {
continue;
}
_extractLabelStrings(node);
}
};
_extractLabelStrings(element);
return strings;
},

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) {
labels.push(label);
}
}

if (labels.length > 0) {
log.debug("Label found by ID", element.id);
return labels;
}

let parent = element.parentNode;
if (!parent) {
return [];
}
do {
if (parent.tagName == "LABEL" &&
parent.control == element &&
!parent.hasAttribute("for")) {
log.debug("Label found in input's parent or ancestor.");
return [parent];
}
parent = parent.parentNode;
} while (parent);

return [];
},

loadDataFromScript(url, sandbox = {}) {
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";

Cu.import("resource://formautofill/FormAutofillUtils.jsm");
Cu.import("resource://formautofill/FormAutofillHeuristics.jsm");

const TESTCASES = [
{
Expand Down Expand Up @@ -59,7 +59,7 @@ TESTCASES.forEach(testcase => {
"http://localhost:8080/test/", testcase.document);

let element = doc.getElementById(testcase.inputId);
let strings = FormAutofillUtils.extractLabelStrings(element);
let strings = LabelUtils.extractLabelStrings(element);

Assert.deepEqual(strings, testcase.expectedStrings);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";

Cu.import("resource://formautofill/FormAutofillUtils.jsm");
Cu.import("resource://formautofill/FormAutofillHeuristics.jsm");

const TESTCASES = [
{
Expand Down Expand Up @@ -83,7 +83,7 @@ TESTCASES.forEach(testcase => {
"http://localhost:8080/test/", testcase.document);

let input = doc.getElementById(testcase.inputId);
let labels = FormAutofillUtils.findLabelElements(input);
let labels = LabelUtils.findLabelElements(input);

Assert.deepEqual(labels.map(l => l.id), testcase.expectedLabelIds);
});
Expand Down
1 change: 1 addition & 0 deletions tools/lint/eslint/modules.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"Finder.jsm": ["Finder", "GetClipboardSearchString"],
"forms.js": ["FormEngine", "FormRec", "FormValidator"],
"forms.jsm": ["FormData"],
"FormAutofillHeuristics.jsm": ["FormAutofillHeuristics", "LabelUtils"],
"FormAutofillSync.jsm": ["AddressesEngine", "CreditCardsEngine"],
"frame.js": ["Collector", "Runner", "events", "runTestFile", "log", "timers", "persisted", "shutdownApplication"],
"FrameScriptManager.jsm": ["getNewLoaderID"],
Expand Down

0 comments on commit 0def08b

Please sign in to comment.