Bug 1330111 - Add FormLikeFactory.findRootForField API. r=johannh draft
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Tue, 31 Jan 2017 23:54:45 -0800
changeset 478920 25c0e4cd01e288e4b97829f2ab2c6f6eae42a36a
parent 478919 2a06c526c41349c6147f31b7fab0b61efcf39021
child 478921 46e234139b5fd969fa2fca4966afea02bf0f3b5d
push id44090
push usermozilla@noorenberghe.ca
push dateSat, 04 Feb 2017 01:25:53 +0000
reviewersjohannh
bugs1330111
milestone54.0a1
Bug 1330111 - Add FormLikeFactory.findRootForField API. r=johannh MozReview-Commit-ID: 8xgtfs9iADG
toolkit/modules/FormLikeFactory.jsm
--- a/toolkit/modules/FormLikeFactory.jsm
+++ b/toolkit/modules/FormLikeFactory.jsm
@@ -63,42 +63,61 @@ let FormLikeFactory = {
    * @throws Error if aField isn't a password or username field in a document
    */
   createFromField(aField) {
     if (!(aField instanceof Ci.nsIDOMHTMLInputElement) ||
         !aField.ownerDocument) {
       throw new Error("createFromField requires a field in a document");
     }
 
-    if (aField.form) {
-      return this.createFromForm(aField.form);
+    let rootElement = this.findRootForField(aField);
+    if (rootElement instanceof Ci.nsIDOMHTMLFormElement) {
+      return this.createFromForm(rootElement);
     }
 
     let doc = aField.ownerDocument;
     let elements = [];
-    for (let el of doc.documentElement.querySelectorAll("input")) {
+    for (let el of rootElement.querySelectorAll("input")) {
+      // Exclude elements inside the rootElement that are already in a <form> as
+      // they will be handled by their own FormLike.
       if (!el.form) {
         elements.push(el);
       }
     }
     let formLike = {
       action: doc.baseURI,
       autocomplete: "on",
-      // Exclude elements inside the rootElement that are already in a <form> as
-      // they will be handled by their own FormLike.
       elements,
       ownerDocument: doc,
-      rootElement: doc.documentElement,
+      rootElement,
     };
 
     this._addToJSONProperty(formLike);
     return formLike;
   },
 
   /**
+   * Determine the Element that encapsulates the related fields. For example, if
+   * a page contains a login form and a checkout form which are "submitted"
+   * separately, and the username field is passed in, ideally this would return
+   * an ancestor Element of the username and password fields which doesn't
+   * include any of the checkout fields.
+   *
+   * @param {HTMLInputElement} aField - a field in a document
+   * @return {HTMLElement} - the root element surrounding related fields
+   */
+  findRootForField(aField) {
+    if (aField.form) {
+      return aField.form;
+    }
+
+    return aField.ownerDocument.documentElement;
+  },
+
+  /**
    * Add a `toJSON` property to a FormLike so logging which ends up going
    * through dump doesn't include usless garbage from DOM objects.
    */
   _addToJSONProperty(aFormLike) {
     function prettyElementOutput(aElement) {
       let idText = aElement.id ? "#" + aElement.id : "";
       let classText = "";
       for (let className of aElement.classList) {