Backed out 2 changesets (bug 1339731) for failing browser/extensions/formautofill/test/mochitest/test_form_changes.html r=backout on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Fri, 17 Nov 2017 17:56:44 +0200
changeset 446681 2492b082f42f9366fb99329ab967cfb71cc6a5f8
parent 446680 988c9c017c7041c5843ca396dce68f5f00af7aed
child 446682 10df6e7390f6e380de796950a77cbcdd91a6342c
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1339731
milestone59.0a1
backs out9faf8dcf5c6e22cd7de1f7e111417d84d30a8ec1
8492412ff6f645b06aa707279d32583db3d15422
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 2 changesets (bug 1339731) for failing browser/extensions/formautofill/test/mochitest/test_form_changes.html r=backout on a CLOSED TREE Backed out changeset 9faf8dcf5c6e (bug 1339731) Backed out changeset 8492412ff6f6 (bug 1339731)
browser/app/profile/firefox.js
browser/extensions/formautofill/FormAutofillContent.jsm
browser/extensions/formautofill/FormAutofillHandler.jsm
browser/extensions/formautofill/FormAutofillHeuristics.jsm
browser/extensions/formautofill/test/unit/head.js
browser/extensions/formautofill/test/unit/test_autofillFormFields.js
browser/extensions/formautofill/test/unit/test_collectFormFields.js
browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1704,17 +1704,16 @@ pref("extensions.formautofill.creditCard
 // 0: none
 // 1: submitted a manually-filled credit card form (but didn't see the doorhanger
 //    because of a duplicate profile in the storage)
 // 2: saw the doorhanger
 // 3: submitted an autofill'ed credit card form
 pref("extensions.formautofill.creditCards.used", 0);
 pref("extensions.formautofill.firstTimeUse", true);
 pref("extensions.formautofill.heuristics.enabled", true);
-pref("extensions.formautofill.section.enabled", true);
 pref("extensions.formautofill.loglevel", "Warn");
 
 // Whether or not to restore a session with lazy-browser tabs.
 pref("browser.sessionstore.restore_tabs_lazily", true);
 
 pref("browser.suppress_first_window_animation", true);
 
 // Preferences for Photon onboarding system extension
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -97,18 +97,18 @@ AutofillProfileAutoCompleteSearch.protot
     this.forceStop = false;
 
     let savedFieldNames = FormAutofillContent.savedFieldNames;
 
     let focusedInput = formFillController.focusedInput;
     let info = FormAutofillContent.getInputDetails(focusedInput);
     let isAddressField = FormAutofillUtils.isAddressField(info.fieldName);
     let handler = FormAutofillContent.getFormHandler(focusedInput);
-    let allFieldNames = handler.getAllFieldNames(focusedInput);
-    let filledRecordGUID = handler.getFilledRecordGUID(focusedInput);
+    let allFieldNames = handler.allFieldNames;
+    let filledRecordGUID = isAddressField ? handler.address.filledRecordGUID : handler.creditCard.filledRecordGUID;
     let searchPermitted = isAddressField ?
                           FormAutofillUtils.isAutofillAddressesEnabled :
                           FormAutofillUtils.isAutofillCreditCardsEnabled;
 
     ProfileAutocomplete.lastProfileAutoCompleteFocusedInput = focusedInput;
     // Fallback to form-history if ...
     //   - specified autofill feature is pref off.
     //   - no profile can fill the currently-focused input.
@@ -144,17 +144,17 @@ AutofillProfileAutoCompleteSearch.protot
 
     this._getRecords(data).then((records) => {
       if (this.forceStop) {
         return;
       }
       // Sort addresses by timeLastUsed for showing the lastest used address at top.
       records.sort((a, b) => b.timeLastUsed - a.timeLastUsed);
 
-      let adaptedRecords = handler.getAdaptedProfiles(records, focusedInput);
+      let adaptedRecords = handler.getAdaptedProfiles(records);
       let result = null;
       if (isAddressField) {
         result = new AddressResult(searchString,
                                    info.fieldName,
                                    allFieldNames,
                                    adaptedRecords,
                                    {});
       } else {
@@ -476,17 +476,17 @@ var FormAutofillContent = {
    */
   getFormDetails(element) {
     let formHandler = this.getFormHandler(element);
     return formHandler ? formHandler.fieldDetails : null;
   },
 
   getAllFieldNames(element) {
     let formHandler = this.getFormHandler(element);
-    return formHandler ? formHandler.getAllFieldNames(element) : null;
+    return formHandler ? formHandler.allFieldNames : null;
   },
 
   identifyAutofillFields(element) {
     this.log.debug("identifyAutofillFields:", "" + element.ownerDocument.location);
 
     if (!this.savedFieldNames) {
       this.log.debug("identifyAutofillFields: savedFieldNames are not known yet");
       Services.cpmm.sendAsyncMessage("FormAutofill:InitStorage");
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -3,108 +3,229 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Defines a handler object to represent forms that autofill can handle.
  */
 
 "use strict";
 
-this.EXPORTED_SYMBOLS = ["FormAutofillHandler"]; /* exported FormAutofillHandler */
+this.EXPORTED_SYMBOLS = ["FormAutofillHandler"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillHeuristics",
                                   "resource://formautofill/FormAutofillHeuristics.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FormLikeFactory",
                                   "resource://gre/modules/FormLikeFactory.jsm");
 
 this.log = null;
 FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
 
-class FormAutofillSection {
-  constructor(fieldDetails, winUtils) {
-    this.address = {
-      /**
-       * Similar to the `_validDetails` but contains address fields only.
-       */
-      fieldDetails: [],
-      /**
-       * String of the filled address' guid.
-       */
-      filledRecordGUID: null,
-    };
-    this.creditCard = {
-      /**
-       * Similar to the `_validDetails` but contains credit card fields only.
-       */
-      fieldDetails: [],
-      /**
-       * String of the filled creditCard's' guid.
-       */
-      filledRecordGUID: null,
+/**
+ * Handles profile autofill for a DOM Form element.
+ * @param {FormLike} form Form that need to be auto filled
+ */
+function FormAutofillHandler(form) {
+  this._updateForm(form);
+  this.winUtils = this.form.rootElement.ownerGlobal.QueryInterface(Ci.nsIInterfaceRequestor)
+    .getInterface(Ci.nsIDOMWindowUtils);
+
+  this.address = {
+    /**
+     * Similar to the `fieldDetails` above but contains address fields only.
+     */
+    fieldDetails: [],
+    /**
+     * String of the filled address' guid.
+     */
+    filledRecordGUID: null,
+  };
+
+  this.creditCard = {
+    /**
+     * Similar to the `fieldDetails` above but contains credit card fields only.
+     */
+    fieldDetails: [],
+    /**
+     * String of the filled creditCard's guid.
+     */
+    filledRecordGUID: null,
+  };
+
+  this._cacheValue = {
+    allFieldNames: null,
+    oneLineStreetAddress: null,
+    matchingSelectOption: null,
+  };
+}
+
+FormAutofillHandler.prototype = {
+  /**
+   * DOM Form element to which this object is attached.
+   */
+  form: null,
+
+  /**
+   * Array of collected data about relevant form fields.  Each item is an object
+   * storing the identifying details of the field and a reference to the
+   * originally associated element from the form.
+   *
+   * The "section", "addressType", "contactType", and "fieldName" values are
+   * used to identify the exact field when the serializable data is received
+   * from the backend.  There cannot be multiple fields which have
+   * the same exact combination of these values.
+   *
+   * A direct reference to the associated element cannot be sent to the user
+   * interface because processing may be done in the parent process.
+   */
+  fieldDetails: null,
+
+  /**
+   * Subcategory of handler that contains address related data.
+   */
+  address: null,
+
+  /**
+   * Subcategory of handler that contains credit card related data.
+   */
+  creditCard: null,
+
+  /**
+   * A WindowUtils reference of which Window the form belongs
+   */
+  winUtils: null,
+
+  /**
+   * Enum for form autofill MANUALLY_MANAGED_STATES values
+   */
+  fieldStateEnum: {
+    // not themed
+    NORMAL: null,
+    // highlighted
+    AUTO_FILLED: "-moz-autofill",
+    // highlighted && grey color text
+    PREVIEW: "-moz-autofill-preview",
+  },
+
+  /**
+   * Time in milliseconds since epoch when a user started filling in the form.
+   */
+  timeStartedFillingMS: null,
+
+  /**
+  * Check the form is necessary to be updated. This function should be able to
+  * detect any changes including all control elements in the form.
+  * @param {HTMLElement} element The element supposed to be in the form.
+  * @returns {boolean} FormAutofillHandler.form is updated or not.
+  */
+  updateFormIfNeeded(element) {
+    // When the following condition happens, FormAutofillHandler.form should be
+    // updated:
+    // * The count of form controls is changed.
+    // * When the element can not be found in the current form.
+    //
+    // However, we should improve the function to detect the element changes.
+    // e.g. a tel field is changed from type="hidden" to type="tel".
+
+    let _formLike;
+    let getFormLike = () => {
+      if (!_formLike) {
+        _formLike = FormLikeFactory.createFromField(element);
+      }
+      return _formLike;
     };
 
-    /**
-     * Enum for form autofill MANUALLY_MANAGED_STATES values
-     */
-    this._FIELD_STATE_ENUM = {
-      // not themed
-      NORMAL: null,
-      // highlighted
-      AUTO_FILLED: "-moz-autofill",
-      // highlighted && grey color text
-      PREVIEW: "-moz-autofill-preview",
-    };
+    let currentForm = element.form;
+    if (!currentForm) {
+      currentForm = getFormLike();
+    }
+
+    if (currentForm.elements.length != this.form.elements.length) {
+      log.debug("The count of form elements is changed.");
+      this._updateForm(getFormLike());
+      return true;
+    }
+
+    if (this.form.elements.indexOf(element) === -1) {
+      log.debug("The element can not be found in the current form.");
+      this._updateForm(getFormLike());
+      return true;
+    }
+
+    return false;
+  },
 
-    this.winUtils = winUtils;
+  /**
+  * Update the form with a new FormLike, and the related fields should be
+  * updated or clear to ensure the data consistency.
+  * @param {FormLike} form a new FormLike to replace the original one.
+  */
+  _updateForm(form) {
+    this.form = form;
+    this.fieldDetails = [];
 
-    this.address.fieldDetails = fieldDetails.filter(
+    if (this.address) {
+      this.address.fieldDetails = [];
+    }
+    if (this.creditCard) {
+      this.creditCard.fieldDetails = [];
+    }
+  },
+
+  /**
+   * Set fieldDetails from the form about fields that can be autofilled.
+   *
+   * @param {boolean} allowDuplicates
+   *        true to remain any duplicated field details otherwise to remove the
+   *        duplicated ones.
+   * @returns {Array} The valid address and credit card details.
+   */
+  collectFormFields(allowDuplicates = false) {
+    this._cacheValue.allFieldNames = null;
+    let fieldDetails = FormAutofillHeuristics.getFormInfo(this.form, allowDuplicates);
+    this.fieldDetails = fieldDetails ? fieldDetails : [];
+    log.debug("Collected details on", this.fieldDetails.length, "fields");
+
+    this.address.fieldDetails = this.fieldDetails.filter(
       detail => FormAutofillUtils.isAddressField(detail.fieldName)
     );
+    this.creditCard.fieldDetails = this.fieldDetails.filter(
+      detail => FormAutofillUtils.isCreditCardField(detail.fieldName)
+    );
+
     if (this.address.fieldDetails.length < FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD) {
-      log.debug("Ignoring address related fields since the section has only",
+      log.debug("Ignoring address related fields since it has only",
                 this.address.fieldDetails.length,
                 "field(s)");
       this.address.fieldDetails = [];
     }
 
-    this.creditCard.fieldDetails = fieldDetails.filter(
-      detail => FormAutofillUtils.isCreditCardField(detail.fieldName)
-    );
     if (!this._isValidCreditCardForm(this.creditCard.fieldDetails)) {
-      log.debug("Invalid credit card section.");
+      log.debug("Invalid credit card form");
       this.creditCard.fieldDetails = [];
     }
 
-    this._cacheValue = {
-      allFieldNames: null,
-      oneLineStreetAddress: null,
-      matchingSelectOption: null,
-    };
+    let validDetails = Array.of(...(this.address.fieldDetails),
+                                ...(this.creditCard.fieldDetails));
+    for (let detail of validDetails) {
+      let input = detail.elementWeakRef.get();
+      if (!input) {
+        continue;
+      }
+      input.addEventListener("input", this);
+    }
 
-    this._validDetails = Array.of(...(this.address.fieldDetails),
-                                  ...(this.creditCard.fieldDetails));
-    log.debug(this._validDetails.length, "valid fields in the section is collected.");
-  }
-
-  get validDetails() {
-    return this._validDetails;
-  }
-
-  getFieldDetailByElement(element) {
-    return this._validDetails.find(
-      detail => detail.elementWeakRef.get() == element
-    );
-  }
+    return validDetails;
+  },
 
   _isValidCreditCardForm(fieldDetails) {
     let ccNumberReason = "";
     let hasCCNumber = false;
     let hasExpiryDate = false;
 
     for (let detail of fieldDetails) {
       switch (detail.fieldName) {
@@ -116,62 +237,58 @@ class FormAutofillSection {
         case "cc-exp-month":
         case "cc-exp-year":
           hasExpiryDate = true;
           break;
       }
     }
 
     return hasCCNumber && (ccNumberReason == "autocomplete" || hasExpiryDate);
-  }
+  },
+
+  getFieldDetailByName(fieldName) {
+    return this.fieldDetails.find(detail => detail.fieldName == fieldName);
+  },
+
+  getFieldDetailByElement(element) {
+    return this.fieldDetails.find(
+      detail => detail.elementWeakRef.get() == element
+    );
+  },
+
+  getFieldDetailsByElement(element) {
+    let fieldDetail = this.getFieldDetailByElement(element);
+    if (!fieldDetail) {
+      return [];
+    }
+    if (FormAutofillUtils.isAddressField(fieldDetail.fieldName)) {
+      return this.address.fieldDetails;
+    }
+    if (FormAutofillUtils.isCreditCardField(fieldDetail.fieldName)) {
+      return this.creditCard.fieldDetails;
+    }
+    return [];
+  },
 
   get allFieldNames() {
     if (!this._cacheValue.allFieldNames) {
-      this._cacheValue.allFieldNames = this._validDetails.map(record => record.fieldName);
+      this._cacheValue.allFieldNames = this.fieldDetails.map(record => record.fieldName);
     }
     return this._cacheValue.allFieldNames;
-  }
-
-  getFieldDetailByName(fieldName) {
-    return this._validDetails.find(detail => detail.fieldName == fieldName);
-  }
-
-  _getTargetSet(element) {
-    let fieldDetail = this.getFieldDetailByElement(element);
-    if (!fieldDetail) {
-      return null;
-    }
-    if (FormAutofillUtils.isAddressField(fieldDetail.fieldName)) {
-      return this.address;
-    }
-    if (FormAutofillUtils.isCreditCardField(fieldDetail.fieldName)) {
-      return this.creditCard;
-    }
-    return null;
-  }
-
-  getFieldDetailsByElement(element) {
-    let targetSet = this._getTargetSet(element);
-    return targetSet ? targetSet.fieldDetails : [];
-  }
-
-  getFilledRecordGUID(element) {
-    let targetSet = this._getTargetSet(element);
-    return targetSet ? targetSet.filledRecordGUID : null;
-  }
+  },
 
   _getOneLineStreetAddress(address) {
     if (!this._cacheValue.oneLineStreetAddress) {
       this._cacheValue.oneLineStreetAddress = {};
     }
     if (!this._cacheValue.oneLineStreetAddress[address]) {
       this._cacheValue.oneLineStreetAddress[address] = FormAutofillUtils.toOneLineAddress(address);
     }
     return this._cacheValue.oneLineStreetAddress[address];
-  }
+  },
 
   _addressTransformer(profile) {
     if (profile["street-address"]) {
       // "-moz-street-address-one-line" is used by the labels in
       // ProfileAutoCompleteResult.
       profile["-moz-street-address-one-line"] = this._getOneLineStreetAddress(profile["street-address"]);
       let streetAddressDetail = this.getFieldDetailByName("street-address");
       if (streetAddressDetail &&
@@ -185,17 +302,17 @@ class FormAutofillSection {
         if (this.getFieldDetailByName(f)) {
           if (waitForConcat.length > 1) {
             profile[f] = FormAutofillUtils.toOneLineAddress(waitForConcat);
           }
           waitForConcat = [];
         }
       }
     }
-  }
+  },
 
   /**
    * Replace tel with tel-national if tel violates the input element's
    * restriction.
    * @param {Object} profile
    *        A profile to be converted.
    */
   _telTransformer(profile) {
@@ -239,17 +356,17 @@ class FormAutofillSection {
       if (testPattern(profile["tel-national"])) {
         profile.tel = profile["tel-national"];
       }
     } else if (element.maxLength) {
       if (profile["tel-national"].length <= element.maxLength) {
         profile.tel = profile["tel-national"];
       }
     }
-  }
+  },
 
   _matchSelectOptions(profile) {
     if (!this._cacheValue.matchingSelectOption) {
       this._cacheValue.matchingSelectOption = new WeakMap();
     }
 
     for (let fieldName in profile) {
       let fieldDetail = this.getFieldDetailByName(fieldName);
@@ -277,17 +394,17 @@ class FormAutofillSection {
           delete cache[value];
           this._cacheValue.matchingSelectOption.set(element, cache);
         }
         // Delete the field so the phishing hint won't treat it as a "also fill"
         // field.
         delete profile[fieldName];
       }
     }
-  }
+  },
 
   _creditCardExpDateTransformer(profile) {
     if (!profile["cc-exp"]) {
       return;
     }
 
     let detail = this.getFieldDetailByName("cc-exp");
     if (!detail) {
@@ -313,62 +430,67 @@ class FormAutofillSection {
     }
 
     result = /(?:[^y]|\b)(y{2,4})\s*([-/\\]*)\s*(m{1,2})(?!m)/i.exec(placeholder);
     if (result) {
       profile["cc-exp"] = String(ccExpYear).substr(-1 * result[1].length) +
                           result[2] +
                           String(ccExpMonth).padStart(result[3].length, "0");
     }
-  }
+  },
 
   getAdaptedProfiles(originalProfiles) {
     for (let profile of originalProfiles) {
       this._addressTransformer(profile);
       this._telTransformer(profile);
       this._matchSelectOptions(profile);
       this._creditCardExpDateTransformer(profile);
     }
     return originalProfiles;
-  }
+  },
 
   /**
    * Processes form fields that can be autofilled, and populates them with the
    * profile provided by backend.
    *
    * @param {Object} profile
    *        A profile to be filled in.
    * @param {HTMLElement} focusedInput
    *        A focused input element needed to determine the address or credit
    *        card field.
    */
-  async autofillFields(profile, focusedInput) {
+  async autofillFormFields(profile, focusedInput) {
     let focusedDetail = this.getFieldDetailByElement(focusedInput);
     if (!focusedDetail) {
       throw new Error("No fieldDetail for the focused input.");
     }
-    let targetSet = this._getTargetSet(focusedInput);
+    let targetSet;
     if (FormAutofillUtils.isCreditCardField(focusedDetail.fieldName)) {
       // When Master Password is enabled by users, the decryption process
       // should prompt Master Password dialog to get the decrypted credit
       // card number. Otherwise, the number can be decrypted with the default
       // password.
       if (profile["cc-number-encrypted"]) {
         let decrypted = await this._decrypt(profile["cc-number-encrypted"], true);
 
         if (!decrypted) {
           // Early return if the decrypted is empty or undefined
           return;
         }
 
         profile["cc-number"] = decrypted;
       }
+      targetSet = this.creditCard;
+    } else if (FormAutofillUtils.isAddressField(focusedDetail.fieldName)) {
+      targetSet = this.address;
+    } else {
+      throw new Error("Unknown form fields");
     }
 
-    log.debug("profile in autofillFields:", profile);
+    log.debug("profile in autofillFormFields:", profile);
 
     targetSet.filledRecordGUID = profile.guid;
     for (let fieldDetail of targetSet.fieldDetails) {
       // Avoid filling field value in the following cases:
       // 1. a non-empty input field for an unfocused input
       // 2. the invalid value set
       // 3. value already chosen in select element
 
@@ -405,28 +527,62 @@ class FormAutofillSection {
           option.selected = true;
           element.dispatchEvent(new element.ownerGlobal.UIEvent("input", {bubbles: true}));
           element.dispatchEvent(new element.ownerGlobal.Event("change", {bubbles: true}));
         }
         // Autofill highlight appears regardless if value is changed or not
         this.changeFieldState(fieldDetail, "AUTO_FILLED");
       }
     }
-  }
+
+    // Handle the highlight style resetting caused by user's correction afterward.
+    log.debug("register change handler for filled form:", this.form);
+    const onChangeHandler = e => {
+      let hasFilledFields;
+
+      if (!e.isTrusted) {
+        return;
+      }
+
+      for (let fieldDetail of targetSet.fieldDetails) {
+        let element = fieldDetail.elementWeakRef.get();
+
+        if (!element) {
+          return;
+        }
+
+        if (e.target == element || (e.target == element.form && e.type == "reset")) {
+          this.changeFieldState(fieldDetail, "NORMAL");
+        }
+
+        hasFilledFields |= (fieldDetail.state == "AUTO_FILLED");
+      }
+
+      // Unregister listeners and clear guid once no field is in AUTO_FILLED state.
+      if (!hasFilledFields) {
+        this.form.rootElement.removeEventListener("input", onChangeHandler);
+        this.form.rootElement.removeEventListener("reset", onChangeHandler);
+        targetSet.filledRecordGUID = null;
+      }
+    };
+
+    this.form.rootElement.addEventListener("input", onChangeHandler);
+    this.form.rootElement.addEventListener("reset", onChangeHandler);
+  },
 
   /**
    * Populates result to the preview layers with given profile.
    *
    * @param {Object} profile
    *        A profile to be previewed with
    * @param {HTMLElement} focusedInput
    *        A focused input element for determining credit card or address fields.
    */
   previewFormFields(profile, focusedInput) {
-    log.debug("preview profile: ", profile);
+    log.debug("preview profile in autofillFormFields:", profile);
 
     // Always show the decrypted credit card number when Master Password is
     // disabled.
     if (profile["cc-number-decrypted"]) {
       profile["cc-number"] = profile["cc-number-decrypted"];
     }
 
     let fieldDetails = this.getFieldDetailsByElement(focusedInput);
@@ -453,17 +609,17 @@ class FormAutofillSection {
         }
       } else if (element.value) {
         // Skip the field if it already has text entered.
         continue;
       }
       element.previewValue = value;
       this.changeFieldState(fieldDetail, value ? "PREVIEW" : "NORMAL");
     }
-  }
+  },
 
   /**
    * Clear preview text and background highlight of all fields.
    *
    * @param {HTMLElement} focusedInput
    *        A focused input element for determining credit card or address fields.
    */
   clearPreviewedFormFields(focusedInput) {
@@ -482,96 +638,74 @@ class FormAutofillSection {
       // We keep the state if this field has
       // already been auto-filled.
       if (fieldDetail.state === "AUTO_FILLED") {
         continue;
       }
 
       this.changeFieldState(fieldDetail, "NORMAL");
     }
-  }
+  },
 
   /**
    * Change the state of a field to correspond with different presentations.
    *
    * @param {Object} fieldDetail
    *        A fieldDetail of which its element is about to update the state.
    * @param {string} nextState
    *        Used to determine the next state
    */
   changeFieldState(fieldDetail, nextState) {
     let element = fieldDetail.elementWeakRef.get();
 
     if (!element) {
       log.warn(fieldDetail.fieldName, "is unreachable while changing state");
       return;
     }
-    if (!(nextState in this._FIELD_STATE_ENUM)) {
+    if (!(nextState in this.fieldStateEnum)) {
       log.warn(fieldDetail.fieldName, "is trying to change to an invalid state");
       return;
     }
 
-    for (let [state, mmStateValue] of Object.entries(this._FIELD_STATE_ENUM)) {
+    for (let [state, mmStateValue] of Object.entries(this.fieldStateEnum)) {
       // The NORMAL state is simply the absence of other manually
       // managed states so we never need to add or remove it.
       if (!mmStateValue) {
         continue;
       }
 
       if (state == nextState) {
         this.winUtils.addManuallyManagedState(element, mmStateValue);
       } else {
         this.winUtils.removeManuallyManagedState(element, mmStateValue);
       }
     }
 
     fieldDetail.state = nextState;
-  }
-
-  clearFieldState(focusedInput) {
-    let fieldDetail = this.getFieldDetailByElement(focusedInput);
-    this.changeFieldState(fieldDetail, "NORMAL");
-    let targetSet = this._getTargetSet(focusedInput);
-
-    if (!targetSet.fieldDetails.some(detail => detail.state == "AUTO_FILLED")) {
-      targetSet.filledRecordGUID = null;
-    }
-  }
-
-  resetFieldStates() {
-    for (let fieldDetail of this._validDetails) {
-      this.changeFieldState(fieldDetail, "NORMAL");
-    }
-    this.address.filledRecordGUID = null;
-    this.creditCard.filledRecordGUID = null;
-  }
-
-  isFilled() {
-    return !!(this.address.filledRecordGUID || this.creditCard.filledRecordGUID);
-  }
+  },
 
   _isAddressRecordCreatable(record) {
     let hasName = 0;
     let length = 0;
     for (let key of Object.keys(record)) {
       if (!record[key]) {
         continue;
       }
       if (FormAutofillUtils.getCategoryFromFieldName(key) == "name") {
         hasName = 1;
         continue;
       }
       length++;
     }
     return (length + hasName) >= FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD;
-  }
+  },
 
   _isCreditCardRecordCreatable(record) {
     return record["cc-number"] && FormAutofillUtils.isCCNumber(record["cc-number"]);
-  }
+  },
 
   /**
    * Return the records that is converted from address/creditCard fieldDetails and
    * only valid form records are included.
    *
    * @returns {Object}
    *          Consists of two record objects: address, creditCard. Each one can
    *          be omitted if there's no valid fields. A record object consists of
@@ -655,17 +789,17 @@ class FormAutofillSection {
 
     // If both address and credit card exists, skip this metrics because it not a
     // general case and each specific histogram might contains insufficient data set.
     if (data.address && data.creditCard) {
       this.timeStartedFillingMS = null;
     }
 
     return data;
-  }
+  },
 
   _normalizeAddress(address) {
     if (!address) {
       return;
     }
 
     // Normalize Country
     if (address.record.country) {
@@ -698,244 +832,28 @@ class FormAutofillSection {
         // number part isn't between 5 and 15.
         // (The maximum length of a valid number in E.164 format is 15 digits
         //  according to https://en.wikipedia.org/wiki/E.164 )
         if (!/^(\+?)[\da-zA-Z]{5,15}$/.test(strippedNumber)) {
           address.record.tel = "";
         }
       }
     }
-  }
+  },
 
   async _decrypt(cipherText, reauth) {
     return new Promise((resolve) => {
       Services.cpmm.addMessageListener("FormAutofill:DecryptedString", function getResult(result) {
         Services.cpmm.removeMessageListener("FormAutofill:DecryptedString", getResult);
         resolve(result.data);
       });
 
       Services.cpmm.sendAsyncMessage("FormAutofill:GetDecryptedString", {cipherText, reauth});
     });
-  }
-}
-
-/**
- * Handles profile autofill for a DOM Form element.
- */
-class FormAutofillHandler {
-  /**
-   * Initialize the form from `FormLike` object to handle the section or form
-   * operations.
-   * @param {FormLike} form Form that need to be auto filled
-   */
-  constructor(form) {
-    /**
-     * DOM Form element to which this object is attached.
-     */
-    this.form = null;
-
-    this._updateForm(form);
-
-    /**
-     * A WindowUtils reference of which Window the form belongs
-     */
-    this.winUtils = this.form.rootElement.ownerGlobal.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIDOMWindowUtils);
-
-    this.sections = [];
-
-    this._sectionCache = new WeakMap();
-
-    /**
-     * Array of collected data about relevant form fields.  Each item is an object
-     * storing the identifying details of the field and a reference to the
-     * originally associated element from the form.
-     *
-     * The "section", "addressType", "contactType", and "fieldName" values are
-     * used to identify the exact field when the serializable data is received
-     * from the backend.  There cannot be multiple fields which have
-     * the same exact combination of these values.
-     *
-     * A direct reference to the associated element cannot be sent to the user
-     * interface because processing may be done in the parent process.
-     */
-    this.fieldDetails = null;
-
-    /**
-     * Time in milliseconds since epoch when a user started filling in the form.
-     */
-    this.timeStartedFillingMS = null;
-  }
-
-  /**
-   * Check the form is necessary to be updated. This function should be able to
-   * detect any changes including all control elements in the form.
-   * @param {HTMLElement} element The element supposed to be in the form.
-   * @returns {boolean} FormAutofillHandler.form is updated or not.
-   */
-  updateFormIfNeeded(element) {
-    // When the following condition happens, FormAutofillHandler.form should be
-    // updated:
-    // * The count of form controls is changed.
-    // * When the element can not be found in the current form.
-    //
-    // However, we should improve the function to detect the element changes.
-    // e.g. a tel field is changed from type="hidden" to type="tel".
-
-    let _formLike;
-    let getFormLike = () => {
-      if (!_formLike) {
-        _formLike = FormLikeFactory.createFromField(element);
-      }
-      return _formLike;
-    };
-
-    let currentForm = element.form;
-    if (!currentForm) {
-      currentForm = getFormLike();
-    }
-
-    if (currentForm.elements.length != this.form.elements.length) {
-      log.debug("The count of form elements is changed.");
-      this._updateForm(getFormLike());
-      return true;
-    }
-
-    if (this.form.elements.indexOf(element) === -1) {
-      log.debug("The element can not be found in the current form.");
-      this._updateForm(getFormLike());
-      return true;
-    }
-
-    return false;
-  }
-
-  /**
-   * Update the form with a new FormLike, and the related fields should be
-   * updated or clear to ensure the data consistency.
-   * @param {FormLike} form a new FormLike to replace the original one.
-   */
-  _updateForm(form) {
-    this.form = form;
-    this.fieldDetails = [];
-    this.sections = [];
-  }
-
-  /**
-   * Set fieldDetails from the form about fields that can be autofilled.
-   *
-   * @param {boolean} allowDuplicates
-   *        true to remain any duplicated field details otherwise to remove the
-   *        duplicated ones.
-   * @returns {Array} The valid address and credit card details.
-   */
-  collectFormFields(allowDuplicates = false) {
-    let sections = FormAutofillHeuristics.getFormInfo(this.form, allowDuplicates);
-    let allValidDetails = [];
-    for (let fieldDetails of sections) {
-      let section = new FormAutofillSection(fieldDetails, this.winUtils);
-      this.sections.push(section);
-      allValidDetails.push(...section.validDetails);
-    }
-
-    for (let detail of allValidDetails) {
-      let input = detail.elementWeakRef.get();
-      if (!input) {
-        continue;
-      }
-      input.addEventListener("input", this);
-    }
-
-    this.fieldDetails = allValidDetails;
-    return allValidDetails;
-  }
-
-  getSectionByElement(element) {
-    let section = this._sectionCache.get(element);
-    if (!section) {
-      section = this.sections.find(
-        s => s.getFieldDetailByElement(element)
-      );
-      this._sectionCache.set(element, section);
-    }
-    return section;
-  }
-
-  getAllFieldNames(focusedInput) {
-    let section = this.getSectionByElement(focusedInput);
-    return section.allFieldNames;
-  }
-
-  previewFormFields(profile, focusedInput) {
-    let section = this.getSectionByElement(focusedInput);
-    section.previewFormFields(profile, focusedInput);
-  }
-
-  clearPreviewedFormFields(focusedInput) {
-    let section = this.getSectionByElement(focusedInput);
-    section.clearPreviewedFormFields(focusedInput);
-  }
-
-  getFilledRecordGUID(focusedInput) {
-    let section = this.getSectionByElement(focusedInput);
-    return section.getFilledRecordGUID(focusedInput);
-  }
-
-  getAdaptedProfiles(originalProfiles, focusedInput) {
-    let section = this.getSectionByElement(focusedInput);
-    section.getAdaptedProfiles(originalProfiles);
-    return originalProfiles;
-  }
-
-  hasFilledSection() {
-    return this.sections.some(section => section.isFilled());
-  }
-
-  /**
-   * Processes form fields that can be autofilled, and populates them with the
-   * profile provided by backend.
-   *
-   * @param {Object} profile
-   *        A profile to be filled in.
-   * @param {HTMLElement} focusedInput
-   *        A focused input element needed to determine the address or credit
-   *        card field.
-   */
-  async autofillFormFields(profile, focusedInput) {
-    let noFilledSections = !this.hasFilledSection();
-    await this.getSectionByElement(focusedInput).autofillFields(profile, focusedInput);
-
-    // Handle the highlight style resetting caused by user's correction afterward.
-    log.debug("register change handler for filled form:", this.form);
-    const onChangeHandler = e => {
-      if (!e.isTrusted) {
-        return;
-      }
-
-      if (e.type == "input") {
-        let section = this.getSectionByElement(e.target);
-        section.clearFieldState(e.target);
-      } else if (e.type == "reset") {
-        for (let section of this.sections) {
-          section.resetFieldStates();
-        }
-      }
-
-      // Unregister listeners once no field is in AUTO_FILLED state.
-      if (!this.hasFilledSection()) {
-        this.form.rootElement.removeEventListener("input", onChangeHandler);
-        this.form.rootElement.removeEventListener("reset", onChangeHandler);
-      }
-    };
-
-    if (noFilledSections) {
-      this.form.rootElement.addEventListener("input", onChangeHandler);
-      this.form.rootElement.addEventListener("reset", onChangeHandler);
-    }
-  }
+  },
 
   handleEvent(event) {
     switch (event.type) {
       case "input":
         if (!event.isTrusted) {
           return;
         }
 
@@ -944,20 +862,10 @@ class FormAutofillHandler {
           if (!input) {
             continue;
           }
           input.removeEventListener("input", this);
         }
         this.timeStartedFillingMS = Date.now();
         break;
     }
-  }
-
-  createRecords() {
-    // TODO [Bug 1415073] `FormAutofillHandler.createRecords` should traverse
-    // all sections and aggregate the records into one result.
-    if (this.sections.length > 0) {
-      return this.sections[0].createRecords();
-    }
-    return null;
-  }
-}
-
+  },
+};
--- a/browser/extensions/formautofill/FormAutofillHeuristics.jsm
+++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm
@@ -15,17 +15,16 @@ const {classes: Cc, interfaces: Ci, util
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
 this.log = null;
 FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
 
 const PREF_HEURISTICS_ENABLED = "extensions.formautofill.heuristics.enabled";
-const PREF_SECTION_ENABLED = "extensions.formautofill.section.enabled";
 
 /**
  * A scanner for traversing all elements in a form and retrieving the field
  * detail with FormAutofillHeuristics.getInfo function. It also provides a
  * cursor (parsingIndex) to indicate which element is waiting for parsing.
  */
 class FieldScanner {
   /**
@@ -540,31 +539,30 @@ this.FormAutofillHeuristics = {
     // that preceding the actual expiration fields.
     fieldScanner.updateFieldName(fieldScanner.parsingIndex, "cc-exp");
     fieldScanner.parsingIndex++;
 
     return true;
   },
 
   /**
-   * This function should provide all field details of a form which are placed
-   * in the belonging section. The details contain the autocomplete info
-   * (e.g. fieldName, section, etc).
+   * This function should provide all field details of a form. The details
+   * contain the autocomplete info (e.g. fieldName, section, etc).
    *
    * `allowDuplicates` is used for the xpcshell-test purpose currently because
    * the heuristics should be verified that some duplicated elements still can
    * be predicted correctly.
    *
    * @param {HTMLFormElement} form
    *        the elements in this form to be predicted the field info.
    * @param {boolean} allowDuplicates
    *        true to remain any duplicated field details otherwise to remove the
    *        duplicated ones.
-   * @returns {Array<Array<Object>>}
-   *        all sections within its field details in the form.
+   * @returns {Array<Object>}
+   *        all field details in the form.
    */
   getFormInfo(form, allowDuplicates = false) {
     const eligibleFields = Array.from(form.elements)
       .filter(elem => FormAutofillUtils.isFieldEligibleForAutofill(elem));
 
     if (eligibleFields.length <= 0) {
       return [];
     }
@@ -579,29 +577,21 @@ this.FormAutofillHeuristics = {
       // forward to the next one.
       if (!parsedPhoneFields && !parsedAddressFields && !parsedExpirationDateFields) {
         fieldScanner.parsingIndex++;
       }
     }
 
     LabelUtils.clearLabelMap();
 
-    if (!this._sectionEnabled) {
-      // When the section feature is disabled, `getFormInfo` should provide a
-      // single section result.
-      return [allowDuplicates ? fieldScanner.fieldDetails : fieldScanner.trimmedFieldDetail];
+    if (allowDuplicates) {
+      return fieldScanner.fieldDetails;
     }
 
-    return this._groupingFields(fieldScanner, allowDuplicates);
-  },
-
-  _groupingFields(fieldScanner, allowDuplicates) {
-    // TODO [Bug 1415077] This function should be able to handle the section
-    // part of autocomplete attr.
-    return [allowDuplicates ? fieldScanner.fieldDetails : fieldScanner.trimmedFieldDetail];
+    return fieldScanner.trimmedFieldDetail;
   },
 
   _regExpTableHashValue(...signBits) {
     return signBits.reduce((p, c, i) => p | !!c << i, 0);
   },
 
   _setRegExpListCache(regexps, b0, b1, b2) {
     if (!this._regexpList) {
@@ -900,16 +890,8 @@ XPCOMUtils.defineLazyGetter(this.FormAut
 XPCOMUtils.defineLazyGetter(this.FormAutofillHeuristics, "_prefEnabled", () => {
   return Services.prefs.getBoolPref(PREF_HEURISTICS_ENABLED);
 });
 
 Services.prefs.addObserver(PREF_HEURISTICS_ENABLED, () => {
   this.FormAutofillHeuristics._prefEnabled = Services.prefs.getBoolPref(PREF_HEURISTICS_ENABLED);
 });
 
-XPCOMUtils.defineLazyGetter(this.FormAutofillHeuristics, "_sectionEnabled", () => {
-  return Services.prefs.getBoolPref(PREF_SECTION_ENABLED);
-});
-
-Services.prefs.addObserver(PREF_SECTION_ENABLED, () => {
-  this.FormAutofillHeuristics._sectionEnabled = Services.prefs.getBoolPref(PREF_SECTION_ENABLED);
-});
-
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -95,23 +95,17 @@ function runHeuristicsTest(patterns, fix
         if (!forms.some(form => form.rootElement === formLike.rootElement)) {
           forms.push(formLike);
         }
       }
 
       Assert.equal(forms.length, testPattern.expectedResult.length, "Expected form count.");
 
       forms.forEach((form, formIndex) => {
-        let sections = FormAutofillHeuristics.getFormInfo(form);
-        if (testPattern.expectedResult[formIndex].length == 0) {
-          return;
-        }
-        // TODO [Bug 1415077] the test should be able to support traversing all
-        // sections.
-        let formInfo = sections[0];
+        let formInfo = FormAutofillHeuristics.getFormInfo(form);
         do_print("FieldName Prediction Results: " + formInfo.map(i => i.fieldName));
         do_print("FieldName Expected Results:   " + testPattern.expectedResult[formIndex].map(i => i.fieldName));
         Assert.equal(formInfo.length, testPattern.expectedResult[formIndex].length, "Expected field count.");
         formInfo.forEach((field, fieldIndex) => {
           let expectedField = testPattern.expectedResult[formIndex][fieldIndex];
           delete field._reason;
           expectedField.elementWeakRef = field.elementWeakRef;
           Assert.deepEqual(field, expectedField);
@@ -168,20 +162,18 @@ function objectMatches(object, fields) {
   }
   return ObjectUtils.deepEqual(actual, fields);
 }
 
 add_task(async function head_initialize() {
   Services.prefs.setStringPref("extensions.formautofill.available", "on");
   Services.prefs.setBoolPref("extensions.formautofill.creditCards.available", true);
   Services.prefs.setBoolPref("extensions.formautofill.heuristics.enabled", true);
-  Services.prefs.setBoolPref("extensions.formautofill.section.enabled", false);
   Services.prefs.setBoolPref("dom.forms.autocomplete.formautofill", true);
 
   // Clean up after every test.
   do_register_cleanup(function head_cleanup() {
     Services.prefs.clearUserPref("extensions.formautofill.available");
     Services.prefs.clearUserPref("extensions.formautofill.creditCards.available");
     Services.prefs.clearUserPref("extensions.formautofill.heuristics.enabled");
-    Services.prefs.clearUserPref("extensions.formautofill.section.enabled");
     Services.prefs.clearUserPref("dom.forms.autocomplete.formautofill");
   });
 });
--- a/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
@@ -499,51 +499,44 @@ function do_test(testcases, testFn) {
 
         let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
                                                   testcase.document);
         let form = doc.querySelector("form");
         let formLike = FormLikeFactory.createFromForm(form);
         let handler = new FormAutofillHandler(formLike);
         let promises = [];
         // Replace the interal decrypt method with MasterPassword API
-        let decryptHelper = async (cipherText, reauth) => {
+        handler._decrypt = async (cipherText, reauth) => {
           let string;
           try {
             string = await MasterPassword.decrypt(cipherText, reauth);
           } catch (e) {
             if (e.result != Cr.NS_ERROR_ABORT) {
               throw e;
             }
             do_print("User canceled master password entry");
           }
           return string;
         };
 
         handler.collectFormFields();
-        for (let section of handler.sections) {
-          section._decrypt = decryptHelper;
-        }
-
-        // TODO [Bug 1415077] We can assume all test cases with only one section
-        // should be filled. Eventually, the test needs to verify the filling
-        // feature in a multiple section case.
-        let handlerInfo = handler.sections[0][testcase.expectedFillingForm];
+        let handlerInfo = handler[testcase.expectedFillingForm];
         handlerInfo.fieldDetails.forEach(field => {
           let element = field.elementWeakRef.get();
           if (!testcase.profileData[field.fieldName]) {
             // Avoid waiting for `change` event of a input with a blank value to
             // be filled.
             return;
           }
           promises.push(...testFn(testcase, element));
         });
 
-        let focusedInput = doc.getElementById(testcase.focusedInputId);
-        let [adaptedProfile] = handler.getAdaptedProfiles([testcase.profileData], focusedInput);
-        await handler.autofillFormFields(adaptedProfile, focusedInput);
+        let [adaptedProfile] = handler.getAdaptedProfiles([testcase.profileData]);
+        let focuedInput = doc.getElementById(testcase.focusedInputId);
+        await handler.autofillFormFields(adaptedProfile, focuedInput);
         Assert.equal(handlerInfo.filledRecordGUID, testcase.profileData.guid,
                      "Check if filledRecordGUID is set correctly");
         await Promise.all(promises);
       });
     })();
   }
 }
 
--- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -435,17 +435,14 @@ for (let tc of TESTCASES) {
         testcase.addressFieldDetails,
         testcase.creditCardFieldDetails,
         testcase.validFieldDetails,
       ].forEach(details => setElementWeakRef(details));
 
       let handler = new FormAutofillHandler(formLike);
       let validFieldDetails = handler.collectFormFields(testcase.allowDuplicates);
 
-      // TODO [Bug 1415077] We can assume all test cases with only one section
-      // should be filled. Eventually, the test needs to verify the filling
-      // feature in a multiple section case.
-      verifyDetails(handler.sections[0].address.fieldDetails, testcase.addressFieldDetails);
-      verifyDetails(handler.sections[0].creditCard.fieldDetails, testcase.creditCardFieldDetails);
+      verifyDetails(handler.address.fieldDetails, testcase.addressFieldDetails);
+      verifyDetails(handler.creditCard.fieldDetails, testcase.creditCardFieldDetails);
       verifyDetails(validFieldDetails, testcase.validFieldDetails);
     });
   })();
 }
--- a/browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
+++ b/browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
@@ -24,18 +24,16 @@ const DEFAULT_CREDITCARD_RECORD = {
   "cc-exp-year": 2025,
   "cc-exp": "2025-01",
 };
 
 const TESTCASES = [
   {
     description: "Address form with street-address",
     document: `<form>
-               <input autocomplete="given-name">
-               <input autocomplete="family-name">
                <input id="street-addr" autocomplete="street-address">
                </form>`,
     profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
     expectedResult: [{
       "guid": "123",
       "street-address": "2 Harrison St line2 line3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St",
@@ -67,17 +65,16 @@ const TESTCASES = [
       "country": "US",
       "tel": "+19876543210",
       "tel-national": "9876543210",
     }],
   },
   {
     description: "Address form with street-address, address-line1",
     document: `<form>
-               <input autocomplete="given-name">
                <input id="street-addr" autocomplete="street-address">
                <input id="line1" autocomplete="address-line1">
                </form>`,
     profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
     expectedResult: [{
       "guid": "123",
       "street-address": "2 Harrison St line2 line3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
@@ -130,17 +127,16 @@ const TESTCASES = [
       "country": "US",
       "tel": "+19876543210",
       "tel-national": "9876543210",
     }],
   },
   {
     description: "Address form with exact matching options in select",
     document: `<form>
-               <input autocomplete="given-name">
                <select autocomplete="address-level1">
                  <option id="option-address-level1-XX" value="XX">Dummy</option>
                  <option id="option-address-level1-CA" value="CA">California</option>
                </select>
                <select autocomplete="country">
                  <option id="option-country-XX" value="XX">Dummy</option>
                  <option id="option-country-US" value="US">United States</option>
                </select>
@@ -161,17 +157,16 @@ const TESTCASES = [
     expectedOptionElements: [{
       "address-level1": "option-address-level1-CA",
       "country": "option-country-US",
     }],
   },
   {
     description: "Address form with inexact matching options in select",
     document: `<form>
-               <input autocomplete="given-name">
                <select autocomplete="address-level1">
                  <option id="option-address-level1-XX" value="XX">Dummy</option>
                  <option id="option-address-level1-OO" value="OO">California</option>
                </select>
                <select autocomplete="country">
                  <option id="option-country-XX" value="XX">Dummy</option>
                  <option id="option-country-OO" value="OO">United States</option>
                </select>
@@ -192,17 +187,16 @@ const TESTCASES = [
     expectedOptionElements: [{
       "address-level1": "option-address-level1-OO",
       "country": "option-country-OO",
     }],
   },
   {
     description: "Address form with value-omitted options in select",
     document: `<form>
-               <input autocomplete="given-name">
                <select autocomplete="address-level1">
                  <option id="option-address-level1-1" value="">Dummy</option>
                  <option id="option-address-level1-2" value="">California</option>
                </select>
                <select autocomplete="country">
                  <option id="option-country-1" value="">Dummy</option>
                  <option id="option-country-2" value="">United States</option>
                </select>
@@ -223,17 +217,16 @@ const TESTCASES = [
     expectedOptionElements: [{
       "address-level1": "option-address-level1-2",
       "country": "option-country-2",
     }],
   },
   {
     description: "Address form with options with the same value in select ",
     document: `<form>
-               <input autocomplete="given-name">
                <select autocomplete="address-level1">
                  <option id="option-address-level1-same1" value="same">Dummy</option>
                  <option id="option-address-level1-same2" value="same">California</option>
                </select>
                <select autocomplete="country">
                  <option id="option-country-same1" value="sametoo">Dummy</option>
                  <option id="option-country-same2" value="sametoo">United States</option>
                </select>
@@ -254,17 +247,16 @@ const TESTCASES = [
     expectedOptionElements: [{
       "address-level1": "option-address-level1-same2",
       "country": "option-country-same2",
     }],
   },
   {
     description: "Address form without matching options in select for address-level1 and country",
     document: `<form>
-               <input autocomplete="given-name">
                <select autocomplete="address-level1">
                  <option id="option-address-level1-dummy1" value="">Dummy</option>
                  <option id="option-address-level1-dummy2" value="">Dummy 2</option>
                </select>
                <select autocomplete="country">
                  <option id="option-country-dummy1" value="">Dummy</option>
                  <option id="option-country-dummy2" value="">Dummy 2</option>
                </select>
@@ -468,17 +460,16 @@ const TESTCASES = [
       "country": "US",
       "tel": "9876543210",
       "tel-national": "9876543210",
     }],
   },
   {
     description: "Credit Card form with matching options of cc-exp-year and cc-exp-month",
     document: `<form>
-               <input autocomplete="cc-number">
                <select autocomplete="cc-exp-month">
                  <option id="option-cc-exp-month-01" value="1">01</option>
                  <option id="option-cc-exp-month-02" value="2">02</option>
                  <option id="option-cc-exp-month-03" value="3">03</option>
                  <option id="option-cc-exp-month-04" value="4">04</option>
                  <option id="option-cc-exp-month-05" value="5">05</option>
                  <option id="option-cc-exp-month-06" value="6">06</option>
                  <option id="option-cc-exp-month-07" value="7">07</option>
@@ -508,17 +499,16 @@ const TESTCASES = [
     expectedOptionElements: [{
       "cc-exp-month": "option-cc-exp-month-01",
       "cc-exp-year": "option-cc-exp-year-25",
     }],
   },
   {
     description: "Credit Card form with matching options which contain labels",
     document: `<form>
-               <input autocomplete="cc-number">
                <select autocomplete="cc-exp-month">
                  <option value="" selected="selected">Month</option>
                  <option label="01 - January" id="option-cc-exp-month-01" value="object:17">dummy</option>
                  <option label="02 - February" id="option-cc-exp-month-02" value="object:18">dummy</option>
                  <option label="03 - March" id="option-cc-exp-month-03" value="object:19">dummy</option>
                  <option label="04 - April" id="option-cc-exp-month-04" value="object:20">dummy</option>
                  <option label="05 - May" id="option-cc-exp-month-05" value="object:21">dummy</option>
                  <option label="06 - June" id="option-cc-exp-month-06" value="object:22">dummy</option>
@@ -556,210 +546,179 @@ const TESTCASES = [
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{
       "cc-exp-month": "option-cc-exp-month-01",
       "cc-exp-year": "option-cc-exp-year-25",
     }],
   },
   {
     description: "Compound cc-exp: {MON1}/{YEAR2}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="3/17">3/17</option>
                  <option value="1/25" id="selected-cc-exp">1/25</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {MON1}/{YEAR4}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="3/2017">3/2017</option>
                  <option value="1/2025" id="selected-cc-exp">1/2025</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {MON2}/{YEAR2}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="03/17">03/17</option>
                  <option value="01/25" id="selected-cc-exp">01/25</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {MON2}/{YEAR4}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="03/2017">03/2017</option>
                  <option value="01/2025" id="selected-cc-exp">01/2025</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {MON1}-{YEAR2}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="3-17">3-17</option>
                  <option value="1-25" id="selected-cc-exp">1-25</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {MON1}-{YEAR4}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="3-2017">3-2017</option>
                  <option value="1-2025" id="selected-cc-exp">1-2025</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {MON2}-{YEAR2}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="03-17">03-17</option>
                  <option value="01-25" id="selected-cc-exp">01-25</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {MON2}-{YEAR4}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="03-2017">03-2017</option>
                  <option value="01-2025" id="selected-cc-exp">01-2025</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {YEAR2}-{MON2}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="17-03">17-03</option>
                  <option value="25-01" id="selected-cc-exp">25-01</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {YEAR4}-{MON2}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="2017-03">2017-03</option>
                  <option value="2025-01" id="selected-cc-exp">2025-01</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {YEAR4}/{MON2}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="2017/3">2017/3</option>
                  <option value="2025/1" id="selected-cc-exp">2025/1</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {MON2}{YEAR2}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="0317">0317</option>
                  <option value="0125" id="selected-cc-exp">0125</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Compound cc-exp: {YEAR2}{MON2}",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="1703">1703</option>
                  <option value="2501" id="selected-cc-exp">2501</option>
                </select></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [DEFAULT_CREDITCARD_RECORD],
     expectedOptionElements: [{"cc-exp": "selected-cc-exp"}],
   },
   {
     description: "Fill a cc-exp without cc-exp-month value in the profile",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="03/17">03/17</option>
                  <option value="01/25">01/25</option>
                </select></form>`,
     profileData: [Object.assign({}, {
       "guid": "123",
       "cc-exp-year": 2025,
     })],
     expectedResult: [{
       "guid": "123",
       "cc-exp-year": 2025,
     }],
     expectedOptionElements: [],
   },
   {
     description: "Fill a cc-exp without cc-exp-year value in the profile",
-    document: `<form>
-               <input autocomplete="cc-number">
-               <select autocomplete="cc-exp">
+    document: `<form><select autocomplete="cc-exp">
                  <option value="03/17">03/17</option>
                  <option value="01/25">01/25</option>
                </select></form>`,
     profileData: [Object.assign({}, {
       "guid": "123",
       "cc-exp-month": 1,
     })],
     expectedResult: [{
       "guid": "123",
       "cc-exp-month": 1,
     }],
     expectedOptionElements: [],
   },
   {
     description: "Fill a cc-exp* without cc-exp-month value in the profile",
     document: `<form>
-               <input autocomplete="cc-number">
                <select autocomplete="cc-exp-month">
                  <option value="03">03</option>
                  <option value="01">01</option>
                </select>
                <select autocomplete="cc-exp-year">
                  <option value="17">2017</option>
                  <option value="25">2025</option>
                </select>
@@ -772,17 +731,16 @@ const TESTCASES = [
       "guid": "123",
       "cc-exp-year": 2025,
     }],
     expectedOptionElements: [],
   },
   {
     description: "Fill a cc-exp* without cc-exp-year value in the profile",
     document: `<form>
-               <input autocomplete="cc-number">
                <select autocomplete="cc-exp-month">
                  <option value="03">03</option>
                  <option value="01">01</option>
                </select>
                <select autocomplete="cc-exp-year">
                  <option value="17">2017</option>
                  <option value="25">2025</option>
                </select>
@@ -794,125 +752,113 @@ const TESTCASES = [
     expectedResult: [{
       "guid": "123",
       "cc-exp-month": 1,
     }],
     expectedOptionElements: [],
   },
   {
     description: "Use placeholder to adjust cc-exp format [mm/yy].",
-    document: `<form><input autocomplete="cc-number">
-               <input placeholder="mm/yy" autocomplete="cc-exp"></form>`,
+    document: `<form><input placeholder="mm/yy" autocomplete="cc-exp"></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
       "cc-exp": "01/25",
     })],
   },
   {
     description: "Use placeholder to adjust cc-exp format [mm / yy].",
-    document: `<form><input autocomplete="cc-number">
-               <input placeholder="mm / yy" autocomplete="cc-exp"></form>`,
+    document: `<form><input placeholder="mm / yy" autocomplete="cc-exp"></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
       "cc-exp": "01/25",
     })],
   },
   {
     description: "Use placeholder to adjust cc-exp format [MM / YY].",
-    document: `<form><input autocomplete="cc-number">
-               <input placeholder="MM / YY" autocomplete="cc-exp"></form>`,
+    document: `<form><input placeholder="MM / YY" autocomplete="cc-exp"></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
       "cc-exp": "01/25",
     })],
   },
   {
     description: "Use placeholder to adjust cc-exp format [mm / yyyy].",
-    document: `<form><input autocomplete="cc-number">
-               <input placeholder="mm / yyyy" autocomplete="cc-exp"></form>`,
+    document: `<form><input placeholder="mm / yyyy" autocomplete="cc-exp"></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
       "cc-exp": "01/2025",
     })],
   },
   {
     description: "Use placeholder to adjust cc-exp format [mm - yyyy].",
-    document: `<form><input autocomplete="cc-number">
-               <input placeholder="mm - yyyy" autocomplete="cc-exp"></form>`,
+    document: `<form><input placeholder="mm - yyyy" autocomplete="cc-exp"></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
       "cc-exp": "01-2025",
     })],
   },
   {
     description: "Use placeholder to adjust cc-exp format [yyyy-mm].",
-    document: `<form><input autocomplete="cc-number">
-               <input placeholder="yyyy-mm" autocomplete="cc-exp"></form>`,
+    document: `<form><input placeholder="yyyy-mm" autocomplete="cc-exp"></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
       "cc-exp": "2025-01",
     })],
   },
   {
     description: "Use placeholder to adjust cc-exp format [yyy-mm].",
-    document: `<form><input autocomplete="cc-number">
-               <input placeholder="yyy-mm" autocomplete="cc-exp"></form>`,
+    document: `<form><input placeholder="yyy-mm" autocomplete="cc-exp"></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
       "cc-exp": "025-01",
     })],
   },
   {
     description: "Use placeholder to adjust cc-exp format [mmm yyyy].",
-    document: `<form><input autocomplete="cc-number">
-               <input placeholder="mmm yyyy" autocomplete="cc-exp"></form>`,
+    document: `<form><input placeholder="mmm yyyy" autocomplete="cc-exp"></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
   },
   {
     description: "Use placeholder to adjust cc-exp format [mm foo yyyy].",
-    document: `<form><input autocomplete="cc-number">
-               <input placeholder="mm foo yyyy" autocomplete="cc-exp"></form>`,
+    document: `<form><input placeholder="mm foo yyyy" autocomplete="cc-exp"></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
   },
   {
     description: "Use placeholder to adjust cc-exp format [mm - - yyyy].",
-    document: `<form><input autocomplete="cc-number">
-               <input placeholder="mm - - yyyy" autocomplete="cc-exp"></form>`,
+    document: `<form><input placeholder="mm - - yyyy" autocomplete="cc-exp"></form>`,
     profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
     expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
   },
 ];
 
 for (let testcase of TESTCASES) {
   add_task(async function() {
     do_print("Starting testcase: " + testcase.description);
 
     let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
                                               testcase.document);
     let form = doc.querySelector("form");
     let formLike = FormLikeFactory.createFromForm(form);
     let handler = new FormAutofillHandler(formLike);
 
     handler.collectFormFields();
-    let focusedInput = form.elements[0];
-    let adaptedRecords = handler.getAdaptedProfiles(testcase.profileData, focusedInput);
+    let adaptedRecords = handler.getAdaptedProfiles(testcase.profileData);
     Assert.deepEqual(adaptedRecords, testcase.expectedResult);
 
     if (testcase.expectedOptionElements) {
       testcase.expectedOptionElements.forEach((expectedOptionElement, i) => {
         for (let field in expectedOptionElement) {
           let select = form.querySelector(`[autocomplete=${field}]`);
           let expectedOption = doc.getElementById(expectedOptionElement[field]);
           Assert.notEqual(expectedOption, null);
 
           let value = testcase.profileData[i][field];
-          let section = handler.getSectionByElement(select);
-          let cache = section._cacheValue.matchingSelectOption.get(select);
+          let cache = handler._cacheValue.matchingSelectOption.get(select);
           let targetOption = cache[value] && cache[value].get();
           Assert.notEqual(targetOption, null);
 
           Assert.equal(targetOption, expectedOption);
         }
       });
     }
   });