gecko-dev/browser/extensions/formautofill/FormAutofillHandler.jsm
steveck-chung e5e4a87c51 Bug 1348757 - Apply weakref for the element in cache. r=MattN
MozReview-Commit-ID: Ezo5kLzf4UK

--HG--
extra : rebase_source : 14b70b5c023b7cc1b6aacd21af10aacefde45de8
2017-03-08 14:38:08 +08:00

164 lines
4.9 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* 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"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillHeuristics",
"resource://formautofill/FormAutofillHeuristics.jsm");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
/**
* Handles profile autofill for a DOM Form element.
* @param {FormLike} form Form that need to be auto filled
*/
function FormAutofillHandler(form) {
this.form = form;
this.fieldDetails = [];
}
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,
/**
* String of the filled profile's guid.
*/
filledProfileGUID: null,
/**
* Set fieldDetails from the form about fields that can be autofilled.
*/
collectFormFields() {
this.fieldDetails = [];
for (let element of this.form.elements) {
// Exclude elements to which no autocomplete field has been assigned.
let info = FormAutofillHeuristics.getInfo(element);
if (!info) {
continue;
}
// Store the association between the field metadata and the element.
if (this.fieldDetails.some(f => f.section == info.section &&
f.addressType == info.addressType &&
f.contactType == info.contactType &&
f.fieldName == info.fieldName)) {
// A field with the same identifier already exists.
log.debug("Not collecting a field matching another with the same info:", info);
continue;
}
let formatWithElement = {
section: info.section,
addressType: info.addressType,
contactType: info.contactType,
fieldName: info.fieldName,
elementWeakRef: Cu.getWeakReference(element),
};
this.fieldDetails.push(formatWithElement);
}
log.debug("Collected details on", this.fieldDetails.length, "fields");
},
/**
* 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 {Object} focusedInput
* A focused input element which is skipped for filling.
*/
autofillFormFields(profile, focusedInput) {
log.debug("profile in autofillFormFields:", profile);
this.filledProfileGUID = profile.guid;
for (let fieldDetail of this.fieldDetails) {
// Avoid filling field value in the following cases:
// 1. the focused input which is filled in FormFillController.
// 2. a non-empty input field
// 3. the invalid value set
let element = fieldDetail.elementWeakRef.get();
if (!element || element === focusedInput || element.value) {
continue;
}
let value = profile[fieldDetail.fieldName];
if (value) {
element.setUserInput(value);
}
}
},
/**
* Populates result to the preview layers with given profile.
*
* @param {Object} profile
* A profile to be previewed with
*/
previewFormFields(profile) {
log.debug("preview profile in autofillFormFields:", profile);
/*
for (let fieldDetail of this.fieldDetails) {
let value = profile[fieldDetail.fieldName] || "";
// Skip the fields that already has text entered
if (fieldDetail.element.value) {
continue;
}
// TODO: Set highlight style and preview text.
}
*/
},
clearPreviewedFormFields() {
log.debug("clear previewed fields in:", this.form);
/*
for (let fieldDetail of this.fieldDetails) {
// TODO: Clear preview text
// We keep the highlight of all fields if this form has
// already been auto-filled with a profile.
if (this.filledProfileGUID == null) {
// TODO: Remove highlight style
}
}
*/
},
};