diff --git a/browser/extensions/formautofill/FormAutofillHeuristics.jsm b/browser/extensions/formautofill/FormAutofillHeuristics.jsm index 52ce7f639a46..f35750130fac 100644 --- a/browser/extensions/formautofill/FormAutofillHeuristics.jsm +++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm @@ -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; @@ -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. */ @@ -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; diff --git a/browser/extensions/formautofill/FormAutofillUtils.jsm b/browser/extensions/formautofill/FormAutofillUtils.jsm index d596b683ad7e..399ad9d3445c 100644 --- a/browser/extensions/formautofill/FormAutofillUtils.jsm +++ b/browser/extensions/formautofill/FormAutofillUtils.jsm @@ -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); diff --git a/browser/extensions/formautofill/test/unit/test_extractLabelStrings.js b/browser/extensions/formautofill/test/unit/test_extractLabelStrings.js index cb159c92e5d0..5c0a59f95e9e 100644 --- a/browser/extensions/formautofill/test/unit/test_extractLabelStrings.js +++ b/browser/extensions/formautofill/test/unit/test_extractLabelStrings.js @@ -1,6 +1,6 @@ "use strict"; -Cu.import("resource://formautofill/FormAutofillUtils.jsm"); +Cu.import("resource://formautofill/FormAutofillHeuristics.jsm"); const TESTCASES = [ { @@ -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); }); diff --git a/browser/extensions/formautofill/test/unit/test_findLabelElements.js b/browser/extensions/formautofill/test/unit/test_findLabelElements.js index 8aca54d7b0a9..dcdf3670d67a 100644 --- a/browser/extensions/formautofill/test/unit/test_findLabelElements.js +++ b/browser/extensions/formautofill/test/unit/test_findLabelElements.js @@ -1,6 +1,6 @@ "use strict"; -Cu.import("resource://formautofill/FormAutofillUtils.jsm"); +Cu.import("resource://formautofill/FormAutofillHeuristics.jsm"); const TESTCASES = [ { @@ -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); }); diff --git a/tools/lint/eslint/modules.json b/tools/lint/eslint/modules.json index 84f15739acac..f2d15cae99c9 100644 --- a/tools/lint/eslint/modules.json +++ b/tools/lint/eslint/modules.json @@ -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"],