Bug 1349490 - Part 1: Implement FormAutofillUtil.extractLabelStrings function.; r?MattN draft
authorSean Lee <selee@mozilla.com>
Mon, 24 Apr 2017 22:30:37 +0800
changeset 585823 01e83900707b007c4205e477032ad65930abdff6
parent 585789 701e0ebc2b4b7ae57248e44fd06278e5309e1a05
child 585824 e44778afe229a384d9bbe15bb5c5572f6b17b7b5
child 586717 cfe695cfe92c829921490410f67a56455d2dca9e
push id61198
push userbmo:selee@mozilla.com
push dateMon, 29 May 2017 02:52:32 +0000
reviewersMattN
bugs1349490
milestone55.0a1
Bug 1349490 - Part 1: Implement FormAutofillUtil.extractLabelStrings function.; r?MattN MozReview-Commit-ID: 9kCZkgnqOfF
browser/extensions/formautofill/FormAutofillUtils.jsm
browser/extensions/formautofill/test/unit/test_extractLabelStrings.js
browser/extensions/formautofill/test/unit/xpcshell.ini
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillUtils.jsm
@@ -57,16 +57,58 @@ this.FormAutofillUtils = {
       let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
       return new ConsoleAPI({
         maxLogLevelPref: "extensions.formautofill.loglevel",
         prefix: logPrefix,
       });
     });
   },
 
+  // 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) {
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_extractLabelStrings.js
@@ -0,0 +1,66 @@
+"use strict";
+
+Cu.import("resource://formautofill/FormAutofillUtils.jsm");
+
+const TESTCASES = [
+  {
+    description: "A label element contains one input element.",
+    document: `<label id="typeA"> label type A
+                 <!-- This comment should not be extracted. -->
+                 <input type="text">
+                 <script>FOO</script>
+                 <noscript>FOO</noscript>
+                 <option>FOO</option>
+                 <style>FOO</style>
+               </label>`,
+    inputId: "typeA",
+    expectedStrings: ["label type A"],
+  },
+  {
+    description: "A label element with inner div contains one input element.",
+    document: `<label id="typeB"> label type B
+                 <!-- This comment should not be extracted. -->
+                 <script>FOO</script>
+                 <noscript>FOO</noscript>
+                 <option>FOO</option>
+                 <style>FOO</style>
+                 <div> inner div
+                   <input type="text">
+                 </div>
+               </label>`,
+    inputId: "typeB",
+    expectedStrings: ["label type B", "inner div"],
+  },
+  {
+    description: "A label element with inner prefix/postfix strings contains span elements.",
+    document: `<label id="typeC"> label type C
+                 <!-- This comment should not be extracted. -->
+                 <script>FOO</script>
+                 <noscript>FOO</noscript>
+                 <option>FOO</option>
+                 <style>FOO</style>
+                 <div> inner div prefix
+                   <span>test C-1 </span>
+                   <span> test C-2</span>
+                  inner div postfix
+                 </div>
+               </label>`,
+    inputId: "typeC",
+    expectedStrings: ["label type C", "inner div prefix", "test C-1",
+      "test C-2", "inner div postfix"],
+  },
+];
+
+TESTCASES.forEach(testcase => {
+  add_task(function* () {
+    do_print("Starting testcase: " + testcase.description);
+
+    let doc = MockDocument.createTestDocument(
+      "http://localhost:8080/test/", testcase.document);
+
+    let element = doc.getElementById(testcase.inputId);
+    let strings = FormAutofillUtils.extractLabelStrings(element);
+
+    Assert.deepEqual(strings, testcase.expectedStrings);
+  });
+});
--- a/browser/extensions/formautofill/test/unit/xpcshell.ini
+++ b/browser/extensions/formautofill/test/unit/xpcshell.ini
@@ -16,16 +16,17 @@ support-files =
 [heuristics/third_party/test_Sears.js]
 [heuristics/third_party/test_Staples.js]
 [heuristics/third_party/test_Walmart.js]
 [test_activeStatus.js]
 [test_addressRecords.js]
 [test_autofillFormFields.js]
 [test_collectFormFields.js]
 [test_creditCardRecords.js]
+[test_extractLabelStrings.js]
 [test_findLabelElements.js]
 [test_getCategoriesFromFieldNames.js]
 [test_getFormInputDetails.js]
 [test_isCJKName.js]
 [test_markAsAutofillField.js]
 [test_nameUtils.js]
 [test_onFormSubmitted.js]
 [test_profileAutocompleteResult.js]