rename from browser/extensions/formautofill/content/FormAutofillContent.js
rename to browser/extensions/formautofill/FormAutofillContent.jsm
--- a/browser/extensions/formautofill/content/FormAutofillContent.js
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -1,181 +1,39 @@
/* 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/. */
-/* eslint-disable no-use-before-define */
+/*
+ * Form Autofill content process module.
+ */
-/*
- * Form Autofill frame script.
- */
+/* eslint-disable no-use-before-define */
"use strict";
+this.EXPORTED_SYMBOLS = ["FormAutofillContent"];
+
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
+Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ProfileAutoCompleteResult",
"resource://formautofill/ProfileAutoCompleteResult.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillHandler",
+ "resource://formautofill/FormAutofillHandler.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormLikeFactory",
"resource://gre/modules/FormLikeFactory.jsm");
const formFillController = Cc["@mozilla.org/satchel/form-fill-controller;1"]
.getService(Ci.nsIFormFillController);
const AUTOFILL_FIELDS_THRESHOLD = 3;
-/**
- * Returns the autocomplete information of fields according to heuristics.
- */
-let FormAutofillHeuristics = {
- VALID_FIELDS: [
- "organization",
- "street-address",
- "address-level2",
- "address-level1",
- "postal-code",
- "country",
- "tel",
- "email",
- ],
-
- getInfo(element) {
- if (!(element instanceof Ci.nsIDOMHTMLInputElement)) {
- return null;
- }
-
- let info = element.getAutocompleteInfo();
- if (!info || !info.fieldName ||
- !this.VALID_FIELDS.includes(info.fieldName)) {
- return null;
- }
-
- return info;
- },
-};
-
-/**
- * Handles profile autofill for a DOM Form element.
- * @param {HTMLFormElement} 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,
-
- /**
- * Returns information from the form about fields that can be autofilled, and
- * populates the fieldDetails array on this object accordingly.
- *
- * @returns {Array<Object>} Serializable data structure that can be sent to the user
- * interface, or null if the operation failed because the constraints
- * on the allowed fields were not honored.
- */
- collectFormFields() {
- let autofillData = [];
-
- 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.
- return null;
- }
-
- let inputFormat = {
- section: info.section,
- addressType: info.addressType,
- contactType: info.contactType,
- fieldName: info.fieldName,
- };
- // Clone the inputFormat for caching the fields and elements together
- let formatWithElement = Object.assign({}, inputFormat);
-
- inputFormat.index = autofillData.length;
- autofillData.push(inputFormat);
-
- formatWithElement.element = element;
- this.fieldDetails.push(formatWithElement);
- }
-
- return autofillData;
- },
-
- /**
- * Processes form fields that can be autofilled, and populates them with the
- * data provided by backend.
- *
- * @param {Array<Object>} autofillResult
- * Data returned by the user interface.
- * [{
- * section: Value originally provided to the user interface.
- * addressType: Value originally provided to the user interface.
- * contactType: Value originally provided to the user interface.
- * fieldName: Value originally provided to the user interface.
- * value: String with which the field should be updated.
- * index: Index to match the input in fieldDetails
- * }],
- * }
- */
- autofillFormFields(autofillResult) {
- for (let field of autofillResult) {
- // Get the field details, if it was processed by the user interface.
- let fieldDetail = this.fieldDetails[field.index];
-
- // Avoid the invalid value set
- if (!fieldDetail || !field.value) {
- continue;
- }
-
- let info = FormAutofillHeuristics.getInfo(fieldDetail.element);
- if (!info ||
- field.section != info.section ||
- field.addressType != info.addressType ||
- field.contactType != info.contactType ||
- field.fieldName != info.fieldName) {
- Cu.reportError("Autocomplete tokens mismatched");
- continue;
- }
-
- fieldDetail.element.setUserInput(field.value);
- }
- },
-};
-
// Register/unregister a constructor as a factory.
function AutocompleteFactory() {}
AutocompleteFactory.prototype = {
register(targetConstructor) {
let proto = targetConstructor.prototype;
this._classID = proto.classID;
let factory = XPCOMUtils._getFactory(targetConstructor);
@@ -262,22 +120,22 @@ AutofillProfileAutoCompleteSearch.protot
* The typed string for filtering out the matched profile.
* @param {string} data.info
* The input autocomplete property's information.
* @returns {Promise}
* Promise that resolves when profiles returned from parent process.
*/
getProfiles(data) {
return new Promise((resolve) => {
- addMessageListener("FormAutofill:Profiles", function getResult(result) {
- removeMessageListener("FormAutofill:Profiles", getResult);
+ Services.cpmm.addMessageListener("FormAutofill:Profiles", function getResult(result) {
+ Services.cpmm.removeMessageListener("FormAutofill:Profiles", getResult);
resolve(result.data);
});
- sendAsyncMessage("FormAutofill:GetProfiles", data);
+ Services.cpmm.sendAsyncMessage("FormAutofill:GetProfiles", data);
});
},
/**
* Get the input's information from FormAutofillContent's cache.
*
* @returns {Object}
@@ -323,48 +181,32 @@ let ProfileAutocomplete = {
this._factory.unregister();
this._factory = null;
this._registered = false;
},
};
/**
- * Handles content's interactions.
+ * Handles content's interactions for the process.
*
* NOTE: Declares it by "var" to make it accessible in unit tests.
*/
var FormAutofillContent = {
init() {
- addEventListener("DOMContentLoaded", this);
-
- addMessageListener("FormAutofill:enabledStatus", (result) => {
+ Services.cpmm.addMessageListener("FormAutofill:enabledStatus", (result) => {
if (result.data) {
ProfileAutocomplete.ensureRegistered();
} else {
ProfileAutocomplete.ensureUnregistered();
}
});
- sendAsyncMessage("FormAutofill:getEnabledStatus");
- },
-
- handleEvent(evt) {
- if (!evt.isTrusted) {
- return;
- }
-
- switch (evt.type) {
- case "DOMContentLoaded":
- let doc = evt.target;
- if (!(doc instanceof Ci.nsIDOMHTMLDocument)) {
- return;
- }
- this._identifyAutofillFields(doc);
- break;
- }
+ Services.cpmm.sendAsyncMessage("FormAutofill:getEnabledStatus");
+ // TODO: use initialProcessData:
+ // Services.cpmm.initialProcessData.autofillEnabled
},
/**
* Get the input's information from cache which is created after page identified.
*
* @param {HTMLInputElement} element Focused input which triggered profile searching
* @returns {Object|null}
* Return target input's information that cloned from content cache
copy from browser/extensions/formautofill/content/FormAutofillContent.js
copy to browser/extensions/formautofill/FormAutofillHandler.jsm
--- a/browser/extensions/formautofill/content/FormAutofillContent.js
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -1,67 +1,30 @@
/* 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/. */
-/* eslint-disable no-use-before-define */
-
/*
- * Form Autofill frame script.
+ * Defines a handler object to represent forms that autofill can handle.
*/
"use strict";
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
+this.EXPORTED_SYMBOLS = ["FormAutofillHandler"];
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ProfileAutoCompleteResult",
- "resource://formautofill/ProfileAutoCompleteResult.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "FormLikeFactory",
- "resource://gre/modules/FormLikeFactory.jsm");
-
-const formFillController = Cc["@mozilla.org/satchel/form-fill-controller;1"]
- .getService(Ci.nsIFormFillController);
-
-const AUTOFILL_FIELDS_THRESHOLD = 3;
-
-/**
- * Returns the autocomplete information of fields according to heuristics.
- */
-let FormAutofillHeuristics = {
- VALID_FIELDS: [
- "organization",
- "street-address",
- "address-level2",
- "address-level1",
- "postal-code",
- "country",
- "tel",
- "email",
- ],
-
- getInfo(element) {
- if (!(element instanceof Ci.nsIDOMHTMLInputElement)) {
- return null;
- }
-
- let info = element.getAutocompleteInfo();
- if (!info || !info.fieldName ||
- !this.VALID_FIELDS.includes(info.fieldName)) {
- return null;
- }
-
- return info;
- },
-};
+XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillHeuristics",
+ "resource://formautofill/FormAutofillHeuristics.jsm");
/**
* Handles profile autofill for a DOM Form element.
- * @param {HTMLFormElement} form Form that need to be auto filled
+ * @param {FormLike} form Form that need to be auto filled
*/
function FormAutofillHandler(form) {
this.form = form;
this.fieldDetails = [];
}
FormAutofillHandler.prototype = {
/**
@@ -165,291 +128,8 @@ FormAutofillHandler.prototype = {
Cu.reportError("Autocomplete tokens mismatched");
continue;
}
fieldDetail.element.setUserInput(field.value);
}
},
};
-
-// Register/unregister a constructor as a factory.
-function AutocompleteFactory() {}
-AutocompleteFactory.prototype = {
- register(targetConstructor) {
- let proto = targetConstructor.prototype;
- this._classID = proto.classID;
-
- let factory = XPCOMUtils._getFactory(targetConstructor);
- this._factory = factory;
-
- let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
- registrar.registerFactory(proto.classID, proto.classDescription,
- proto.contractID, factory);
-
- if (proto.classID2) {
- this._classID2 = proto.classID2;
- registrar.registerFactory(proto.classID2, proto.classDescription,
- proto.contractID2, factory);
- }
- },
-
- unregister() {
- let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
- registrar.unregisterFactory(this._classID, this._factory);
- if (this._classID2) {
- registrar.unregisterFactory(this._classID2, this._factory);
- }
- this._factory = null;
- },
-};
-
-
-/**
- * @constructor
- *
- * @implements {nsIAutoCompleteSearch}
- */
-function AutofillProfileAutoCompleteSearch() {
-
-}
-AutofillProfileAutoCompleteSearch.prototype = {
- classID: Components.ID("4f9f1e4c-7f2c-439e-9c9e-566b68bc187d"),
- contractID: "@mozilla.org/autocomplete/search;1?name=autofill-profiles",
- classDescription: "AutofillProfileAutoCompleteSearch",
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteSearch]),
-
- // Begin nsIAutoCompleteSearch implementation
-
- /**
- * Searches for a given string and notifies a listener (either synchronously
- * or asynchronously) of the result
- *
- * @param {string} searchString the string to search for
- * @param {string} searchParam
- * @param {Object} previousResult a previous result to use for faster searchinig
- * @param {Object} listener the listener to notify when the search is complete
- */
- startSearch(searchString, searchParam, previousResult, listener) {
- this.forceStop = false;
- let info = this.getInputDetails();
-
- this.getProfiles({info, searchString}).then((profiles) => {
- if (this.forceStop) {
- return;
- }
-
- // TODO: Set formInfo for ProfileAutoCompleteResult
- // let formInfo = this.getFormDetails();
- let result = new ProfileAutoCompleteResult(searchString, info, profiles, {});
-
- listener.onSearchResult(this, result);
- });
- },
-
- /**
- * Stops an asynchronous search that is in progress
- */
- stopSearch() {
- this.forceStop = true;
- },
-
- /**
- * Get the profile data from parent process for AutoComplete result.
- *
- * @private
- * @param {Object} data
- * Parameters for querying the corresponding result.
- * @param {string} data.searchString
- * The typed string for filtering out the matched profile.
- * @param {string} data.info
- * The input autocomplete property's information.
- * @returns {Promise}
- * Promise that resolves when profiles returned from parent process.
- */
- getProfiles(data) {
- return new Promise((resolve) => {
- addMessageListener("FormAutofill:Profiles", function getResult(result) {
- removeMessageListener("FormAutofill:Profiles", getResult);
- resolve(result.data);
- });
-
- sendAsyncMessage("FormAutofill:GetProfiles", data);
- });
- },
-
-
- /**
- * Get the input's information from FormAutofillContent's cache.
- *
- * @returns {Object}
- * Target input's information that cached in FormAutofillContent.
- */
- getInputDetails() {
- // TODO: Maybe we'll need to wait for cache ready if detail is empty.
- return FormAutofillContent.getInputDetails(formFillController.focusedInput);
- },
-
- /**
- * Get the form's information from FormAutofillContent's cache.
- *
- * @returns {Array<Object>}
- * Array of the inputs' information for the target form.
- */
- getFormDetails() {
- // TODO: Maybe we'll need to wait for cache ready if details is empty.
- return FormAutofillContent.getFormDetails(formFillController.focusedInput);
- },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AutofillProfileAutoCompleteSearch]);
-
-let ProfileAutocomplete = {
- _registered: false,
- _factory: null,
-
- ensureRegistered() {
- if (this._registered) {
- return;
- }
-
- this._factory = new AutocompleteFactory();
- this._factory.register(AutofillProfileAutoCompleteSearch);
- this._registered = true;
- },
-
- ensureUnregistered() {
- if (!this._registered) {
- return;
- }
-
- this._factory.unregister();
- this._factory = null;
- this._registered = false;
- },
-};
-
-/**
- * Handles content's interactions.
- *
- * NOTE: Declares it by "var" to make it accessible in unit tests.
- */
-var FormAutofillContent = {
- init() {
- addEventListener("DOMContentLoaded", this);
-
- addMessageListener("FormAutofill:enabledStatus", (result) => {
- if (result.data) {
- ProfileAutocomplete.ensureRegistered();
- } else {
- ProfileAutocomplete.ensureUnregistered();
- }
- });
- sendAsyncMessage("FormAutofill:getEnabledStatus");
- },
-
- handleEvent(evt) {
- if (!evt.isTrusted) {
- return;
- }
-
- switch (evt.type) {
- case "DOMContentLoaded":
- let doc = evt.target;
- if (!(doc instanceof Ci.nsIDOMHTMLDocument)) {
- return;
- }
- this._identifyAutofillFields(doc);
- break;
- }
- },
-
- /**
- * Get the input's information from cache which is created after page identified.
- *
- * @param {HTMLInputElement} element Focused input which triggered profile searching
- * @returns {Object|null}
- * Return target input's information that cloned from content cache
- * (or return null if the information is not found in the cache).
- */
- getInputDetails(element) {
- for (let formDetails of this._formsDetails) {
- for (let detail of formDetails) {
- if (element == detail.element) {
- return this._serializeInfo(detail);
- }
- }
- }
- return null;
- },
-
- /**
- * Get the form's information from cache which is created after page identified.
- *
- * @param {HTMLInputElement} element Focused input which triggered profile searching
- * @returns {Array<Object>|null}
- * Return target form's information that cloned from content cache
- * (or return null if the information is not found in the cache).
- *
- */
- getFormDetails(element) {
- for (let formDetails of this._formsDetails) {
- if (formDetails.some((detail) => detail.element == element)) {
- return formDetails.map((detail) => this._serializeInfo(detail));
- }
- }
- return null;
- },
-
- /**
- * Create a clone the information object without element reference.
- *
- * @param {Object} detail Profile autofill information for specific input.
- * @returns {Object}
- * Return a copy of cached information object without element reference
- * since it's not needed for creating result.
- */
- _serializeInfo(detail) {
- let info = Object.assign({}, detail);
- delete info.element;
- return info;
- },
-
- _identifyAutofillFields(doc) {
- let forms = [];
- this._formsDetails = [];
-
- // Collects root forms from inputs.
- for (let field of doc.getElementsByTagName("input")) {
- // We only consider text-like fields for now until we support radio and
- // checkbox buttons in the future.
- if (!field.mozIsTextField(true)) {
- continue;
- }
-
- let formLike = FormLikeFactory.createFromField(field);
- if (!forms.some(form => form.rootElement === formLike.rootElement)) {
- forms.push(formLike);
- }
- }
-
- // Collects the fields that can be autofilled from each form and marks them
- // as autofill fields if the amount is above the threshold.
- forms.forEach(form => {
- let formHandler = new FormAutofillHandler(form);
- formHandler.collectFormFields();
- if (formHandler.fieldDetails.length < AUTOFILL_FIELDS_THRESHOLD) {
- return;
- }
-
- this._formsDetails.push(formHandler.fieldDetails);
- formHandler.fieldDetails.forEach(
- detail => this._markAsAutofillField(detail.element));
- });
- },
-
- _markAsAutofillField(field) {
- formFillController.markAsAutofillField(field);
- },
-};
-
-
-FormAutofillContent.init();
copy from browser/extensions/formautofill/content/FormAutofillContent.js
copy to browser/extensions/formautofill/FormAutofillHeuristics.jsm
--- a/browser/extensions/formautofill/content/FormAutofillContent.js
+++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm
@@ -1,38 +1,26 @@
/* 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/. */
-/* eslint-disable no-use-before-define */
-
/*
- * Form Autofill frame script.
+ * Form Autofill field heuristics.
*/
"use strict";
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+this.EXPORTED_SYMBOLS = ["FormAutofillHeuristics"];
-XPCOMUtils.defineLazyModuleGetter(this, "ProfileAutoCompleteResult",
- "resource://formautofill/ProfileAutoCompleteResult.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "FormLikeFactory",
- "resource://gre/modules/FormLikeFactory.jsm");
-
-const formFillController = Cc["@mozilla.org/satchel/form-fill-controller;1"]
- .getService(Ci.nsIFormFillController);
-
-const AUTOFILL_FIELDS_THRESHOLD = 3;
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
/**
* Returns the autocomplete information of fields according to heuristics.
*/
-let FormAutofillHeuristics = {
+this.FormAutofillHeuristics = {
VALID_FIELDS: [
"organization",
"street-address",
"address-level2",
"address-level1",
"postal-code",
"country",
"tel",
@@ -48,408 +36,8 @@ let FormAutofillHeuristics = {
if (!info || !info.fieldName ||
!this.VALID_FIELDS.includes(info.fieldName)) {
return null;
}
return info;
},
};
-
-/**
- * Handles profile autofill for a DOM Form element.
- * @param {HTMLFormElement} 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,
-
- /**
- * Returns information from the form about fields that can be autofilled, and
- * populates the fieldDetails array on this object accordingly.
- *
- * @returns {Array<Object>} Serializable data structure that can be sent to the user
- * interface, or null if the operation failed because the constraints
- * on the allowed fields were not honored.
- */
- collectFormFields() {
- let autofillData = [];
-
- 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.
- return null;
- }
-
- let inputFormat = {
- section: info.section,
- addressType: info.addressType,
- contactType: info.contactType,
- fieldName: info.fieldName,
- };
- // Clone the inputFormat for caching the fields and elements together
- let formatWithElement = Object.assign({}, inputFormat);
-
- inputFormat.index = autofillData.length;
- autofillData.push(inputFormat);
-
- formatWithElement.element = element;
- this.fieldDetails.push(formatWithElement);
- }
-
- return autofillData;
- },
-
- /**
- * Processes form fields that can be autofilled, and populates them with the
- * data provided by backend.
- *
- * @param {Array<Object>} autofillResult
- * Data returned by the user interface.
- * [{
- * section: Value originally provided to the user interface.
- * addressType: Value originally provided to the user interface.
- * contactType: Value originally provided to the user interface.
- * fieldName: Value originally provided to the user interface.
- * value: String with which the field should be updated.
- * index: Index to match the input in fieldDetails
- * }],
- * }
- */
- autofillFormFields(autofillResult) {
- for (let field of autofillResult) {
- // Get the field details, if it was processed by the user interface.
- let fieldDetail = this.fieldDetails[field.index];
-
- // Avoid the invalid value set
- if (!fieldDetail || !field.value) {
- continue;
- }
-
- let info = FormAutofillHeuristics.getInfo(fieldDetail.element);
- if (!info ||
- field.section != info.section ||
- field.addressType != info.addressType ||
- field.contactType != info.contactType ||
- field.fieldName != info.fieldName) {
- Cu.reportError("Autocomplete tokens mismatched");
- continue;
- }
-
- fieldDetail.element.setUserInput(field.value);
- }
- },
-};
-
-// Register/unregister a constructor as a factory.
-function AutocompleteFactory() {}
-AutocompleteFactory.prototype = {
- register(targetConstructor) {
- let proto = targetConstructor.prototype;
- this._classID = proto.classID;
-
- let factory = XPCOMUtils._getFactory(targetConstructor);
- this._factory = factory;
-
- let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
- registrar.registerFactory(proto.classID, proto.classDescription,
- proto.contractID, factory);
-
- if (proto.classID2) {
- this._classID2 = proto.classID2;
- registrar.registerFactory(proto.classID2, proto.classDescription,
- proto.contractID2, factory);
- }
- },
-
- unregister() {
- let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
- registrar.unregisterFactory(this._classID, this._factory);
- if (this._classID2) {
- registrar.unregisterFactory(this._classID2, this._factory);
- }
- this._factory = null;
- },
-};
-
-
-/**
- * @constructor
- *
- * @implements {nsIAutoCompleteSearch}
- */
-function AutofillProfileAutoCompleteSearch() {
-
-}
-AutofillProfileAutoCompleteSearch.prototype = {
- classID: Components.ID("4f9f1e4c-7f2c-439e-9c9e-566b68bc187d"),
- contractID: "@mozilla.org/autocomplete/search;1?name=autofill-profiles",
- classDescription: "AutofillProfileAutoCompleteSearch",
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteSearch]),
-
- // Begin nsIAutoCompleteSearch implementation
-
- /**
- * Searches for a given string and notifies a listener (either synchronously
- * or asynchronously) of the result
- *
- * @param {string} searchString the string to search for
- * @param {string} searchParam
- * @param {Object} previousResult a previous result to use for faster searchinig
- * @param {Object} listener the listener to notify when the search is complete
- */
- startSearch(searchString, searchParam, previousResult, listener) {
- this.forceStop = false;
- let info = this.getInputDetails();
-
- this.getProfiles({info, searchString}).then((profiles) => {
- if (this.forceStop) {
- return;
- }
-
- // TODO: Set formInfo for ProfileAutoCompleteResult
- // let formInfo = this.getFormDetails();
- let result = new ProfileAutoCompleteResult(searchString, info, profiles, {});
-
- listener.onSearchResult(this, result);
- });
- },
-
- /**
- * Stops an asynchronous search that is in progress
- */
- stopSearch() {
- this.forceStop = true;
- },
-
- /**
- * Get the profile data from parent process for AutoComplete result.
- *
- * @private
- * @param {Object} data
- * Parameters for querying the corresponding result.
- * @param {string} data.searchString
- * The typed string for filtering out the matched profile.
- * @param {string} data.info
- * The input autocomplete property's information.
- * @returns {Promise}
- * Promise that resolves when profiles returned from parent process.
- */
- getProfiles(data) {
- return new Promise((resolve) => {
- addMessageListener("FormAutofill:Profiles", function getResult(result) {
- removeMessageListener("FormAutofill:Profiles", getResult);
- resolve(result.data);
- });
-
- sendAsyncMessage("FormAutofill:GetProfiles", data);
- });
- },
-
-
- /**
- * Get the input's information from FormAutofillContent's cache.
- *
- * @returns {Object}
- * Target input's information that cached in FormAutofillContent.
- */
- getInputDetails() {
- // TODO: Maybe we'll need to wait for cache ready if detail is empty.
- return FormAutofillContent.getInputDetails(formFillController.focusedInput);
- },
-
- /**
- * Get the form's information from FormAutofillContent's cache.
- *
- * @returns {Array<Object>}
- * Array of the inputs' information for the target form.
- */
- getFormDetails() {
- // TODO: Maybe we'll need to wait for cache ready if details is empty.
- return FormAutofillContent.getFormDetails(formFillController.focusedInput);
- },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AutofillProfileAutoCompleteSearch]);
-
-let ProfileAutocomplete = {
- _registered: false,
- _factory: null,
-
- ensureRegistered() {
- if (this._registered) {
- return;
- }
-
- this._factory = new AutocompleteFactory();
- this._factory.register(AutofillProfileAutoCompleteSearch);
- this._registered = true;
- },
-
- ensureUnregistered() {
- if (!this._registered) {
- return;
- }
-
- this._factory.unregister();
- this._factory = null;
- this._registered = false;
- },
-};
-
-/**
- * Handles content's interactions.
- *
- * NOTE: Declares it by "var" to make it accessible in unit tests.
- */
-var FormAutofillContent = {
- init() {
- addEventListener("DOMContentLoaded", this);
-
- addMessageListener("FormAutofill:enabledStatus", (result) => {
- if (result.data) {
- ProfileAutocomplete.ensureRegistered();
- } else {
- ProfileAutocomplete.ensureUnregistered();
- }
- });
- sendAsyncMessage("FormAutofill:getEnabledStatus");
- },
-
- handleEvent(evt) {
- if (!evt.isTrusted) {
- return;
- }
-
- switch (evt.type) {
- case "DOMContentLoaded":
- let doc = evt.target;
- if (!(doc instanceof Ci.nsIDOMHTMLDocument)) {
- return;
- }
- this._identifyAutofillFields(doc);
- break;
- }
- },
-
- /**
- * Get the input's information from cache which is created after page identified.
- *
- * @param {HTMLInputElement} element Focused input which triggered profile searching
- * @returns {Object|null}
- * Return target input's information that cloned from content cache
- * (or return null if the information is not found in the cache).
- */
- getInputDetails(element) {
- for (let formDetails of this._formsDetails) {
- for (let detail of formDetails) {
- if (element == detail.element) {
- return this._serializeInfo(detail);
- }
- }
- }
- return null;
- },
-
- /**
- * Get the form's information from cache which is created after page identified.
- *
- * @param {HTMLInputElement} element Focused input which triggered profile searching
- * @returns {Array<Object>|null}
- * Return target form's information that cloned from content cache
- * (or return null if the information is not found in the cache).
- *
- */
- getFormDetails(element) {
- for (let formDetails of this._formsDetails) {
- if (formDetails.some((detail) => detail.element == element)) {
- return formDetails.map((detail) => this._serializeInfo(detail));
- }
- }
- return null;
- },
-
- /**
- * Create a clone the information object without element reference.
- *
- * @param {Object} detail Profile autofill information for specific input.
- * @returns {Object}
- * Return a copy of cached information object without element reference
- * since it's not needed for creating result.
- */
- _serializeInfo(detail) {
- let info = Object.assign({}, detail);
- delete info.element;
- return info;
- },
-
- _identifyAutofillFields(doc) {
- let forms = [];
- this._formsDetails = [];
-
- // Collects root forms from inputs.
- for (let field of doc.getElementsByTagName("input")) {
- // We only consider text-like fields for now until we support radio and
- // checkbox buttons in the future.
- if (!field.mozIsTextField(true)) {
- continue;
- }
-
- let formLike = FormLikeFactory.createFromField(field);
- if (!forms.some(form => form.rootElement === formLike.rootElement)) {
- forms.push(formLike);
- }
- }
-
- // Collects the fields that can be autofilled from each form and marks them
- // as autofill fields if the amount is above the threshold.
- forms.forEach(form => {
- let formHandler = new FormAutofillHandler(form);
- formHandler.collectFormFields();
- if (formHandler.fieldDetails.length < AUTOFILL_FIELDS_THRESHOLD) {
- return;
- }
-
- this._formsDetails.push(formHandler.fieldDetails);
- formHandler.fieldDetails.forEach(
- detail => this._markAsAutofillField(detail.element));
- });
- },
-
- _markAsAutofillField(field) {
- formFillController.markAsAutofillField(field);
- },
-};
-
-
-FormAutofillContent.init();
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -70,17 +70,17 @@ FormAutofillParent.prototype = {
Services.obs.addObserver(this, "advanced-pane-loaded", false);
// Observing the pref (and storage) changes
Services.prefs.addObserver(ENABLED_PREF, this, false);
this._enabled = this._getStatus();
// Force to trigger the onStatusChanged function for setting listeners properly
// while initizlization
this._onStatusChanged();
- Services.mm.addMessageListener("FormAutofill:getEnabledStatus", this);
+ Services.ppmm.addMessageListener("FormAutofill:getEnabledStatus", this);
},
observe(subject, topic, data) {
switch (topic) {
case "advanced-pane-loaded": {
let formAutofillPreferences = new FormAutofillPreferences();
let document = subject.document;
let prefGroup = formAutofillPreferences.init(document);
@@ -107,21 +107,21 @@ FormAutofillParent.prototype = {
},
/**
* Add/remove message listener and broadcast the status to frames while the
* form autofill status changed.
*/
_onStatusChanged() {
if (this._enabled) {
- Services.mm.addMessageListener("FormAutofill:PopulateFieldValues", this);
- Services.mm.addMessageListener("FormAutofill:GetProfiles", this);
+ Services.ppmm.addMessageListener("FormAutofill:PopulateFieldValues", this);
+ Services.ppmm.addMessageListener("FormAutofill:GetProfiles", this);
} else {
- Services.mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
- Services.mm.removeMessageListener("FormAutofill:GetProfiles", this);
+ Services.ppmm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
+ Services.ppmm.removeMessageListener("FormAutofill:GetProfiles", this);
}
Services.mm.broadcastAsyncMessage("FormAutofill:enabledStatus", this._enabled);
},
/**
* Query pref (and storage) status to determine the overall status for
* form autofill feature.
@@ -143,18 +143,18 @@ FormAutofillParent.prototype = {
switch (name) {
case "FormAutofill:PopulateFieldValues":
this._populateFieldValues(data, target);
break;
case "FormAutofill:GetProfiles":
this._getProfiles(data, target);
break;
case "FormAutofill:getEnabledStatus":
- target.messageManager.sendAsyncMessage("FormAutofill:enabledStatus",
- this._enabled);
+ Services.ppmm.broadcastAsyncMessage("FormAutofill:enabledStatus",
+ this._enabled);
break;
}
},
/**
* Returns the instance of ProfileStorage. To avoid syncing issues, anyone
* who needs to access the profile should request the instance by this instead
* of creating a new one.
@@ -171,18 +171,18 @@ FormAutofillParent.prototype = {
* @private
*/
_uninit() {
if (this._profileStore) {
this._profileStore._saveImmediately();
this._profileStore = null;
}
- Services.mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
- Services.mm.removeMessageListener("FormAutofill:GetProfiles", this);
+ Services.ppmm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
+ Services.ppmm.removeMessageListener("FormAutofill:GetProfiles", this);
Services.obs.removeObserver(this, "advanced-pane-loaded");
Services.prefs.removeObserver(ENABLED_PREF, this);
},
/**
* Populates the field values and notifies content to fill in. Exception will
* be thrown if there's no matching profile.
*
@@ -215,17 +215,17 @@ FormAutofillParent.prototype = {
let profiles = [];
if (info && info.fieldName) {
profiles = this._profileStore.getByFilter({searchString, info});
} else {
profiles = this._profileStore.getAll();
}
- target.messageManager.sendAsyncMessage("FormAutofill:Profiles", profiles);
+ target.sendAsyncMessage("FormAutofill:Profiles", profiles);
},
/**
* Get the corresponding value from the specified profile according to a valid
* @autocomplete field name.
*
* Note that the field name doesn't need to match the property name defined in
* Profile object. This method can transform the raw data to fulfill it. (e.g.
--- a/browser/extensions/formautofill/bootstrap.js
+++ b/browser/extensions/formautofill/bootstrap.js
@@ -18,14 +18,14 @@ function startup() {
// Besides this pref, we'll need dom.forms.autocomplete.experimental enabled
// as well to make sure form autocomplete works correctly.
if (!Services.prefs.getBoolPref("browser.formautofill.experimental")) {
return;
}
let parent = new FormAutofillParent();
parent.init();
- Services.mm.loadFrameScript("chrome://formautofill/content/FormAutofillContent.js", true);
+ Services.mm.loadFrameScript("chrome://formautofill/content/FormAutofillFrameScript.js", true);
}
function shutdown() {}
function install() {}
function uninstall() {}
copy from browser/extensions/formautofill/content/FormAutofillContent.js
copy to browser/extensions/formautofill/content/FormAutofillFrameScript.js
--- a/browser/extensions/formautofill/content/FormAutofillContent.js
+++ b/browser/extensions/formautofill/content/FormAutofillFrameScript.js
@@ -1,455 +1,51 @@
/* 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/. */
-/* eslint-disable no-use-before-define */
-
/*
* Form Autofill frame script.
*/
"use strict";
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "ProfileAutoCompleteResult",
- "resource://formautofill/ProfileAutoCompleteResult.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "FormLikeFactory",
- "resource://gre/modules/FormLikeFactory.jsm");
-
-const formFillController = Cc["@mozilla.org/satchel/form-fill-controller;1"]
- .getService(Ci.nsIFormFillController);
-
-const AUTOFILL_FIELDS_THRESHOLD = 3;
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-/**
- * Returns the autocomplete information of fields according to heuristics.
- */
-let FormAutofillHeuristics = {
- VALID_FIELDS: [
- "organization",
- "street-address",
- "address-level2",
- "address-level1",
- "postal-code",
- "country",
- "tel",
- "email",
- ],
-
- getInfo(element) {
- if (!(element instanceof Ci.nsIDOMHTMLInputElement)) {
- return null;
- }
-
- let info = element.getAutocompleteInfo();
- if (!info || !info.fieldName ||
- !this.VALID_FIELDS.includes(info.fieldName)) {
- return null;
- }
-
- return info;
- },
-};
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/**
- * Handles profile autofill for a DOM Form element.
- * @param {HTMLFormElement} 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,
-
- /**
- * Returns information from the form about fields that can be autofilled, and
- * populates the fieldDetails array on this object accordingly.
- *
- * @returns {Array<Object>} Serializable data structure that can be sent to the user
- * interface, or null if the operation failed because the constraints
- * on the allowed fields were not honored.
- */
- collectFormFields() {
- let autofillData = [];
-
- 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.
- return null;
- }
-
- let inputFormat = {
- section: info.section,
- addressType: info.addressType,
- contactType: info.contactType,
- fieldName: info.fieldName,
- };
- // Clone the inputFormat for caching the fields and elements together
- let formatWithElement = Object.assign({}, inputFormat);
-
- inputFormat.index = autofillData.length;
- autofillData.push(inputFormat);
-
- formatWithElement.element = element;
- this.fieldDetails.push(formatWithElement);
- }
-
- return autofillData;
- },
-
- /**
- * Processes form fields that can be autofilled, and populates them with the
- * data provided by backend.
- *
- * @param {Array<Object>} autofillResult
- * Data returned by the user interface.
- * [{
- * section: Value originally provided to the user interface.
- * addressType: Value originally provided to the user interface.
- * contactType: Value originally provided to the user interface.
- * fieldName: Value originally provided to the user interface.
- * value: String with which the field should be updated.
- * index: Index to match the input in fieldDetails
- * }],
- * }
- */
- autofillFormFields(autofillResult) {
- for (let field of autofillResult) {
- // Get the field details, if it was processed by the user interface.
- let fieldDetail = this.fieldDetails[field.index];
-
- // Avoid the invalid value set
- if (!fieldDetail || !field.value) {
- continue;
- }
-
- let info = FormAutofillHeuristics.getInfo(fieldDetail.element);
- if (!info ||
- field.section != info.section ||
- field.addressType != info.addressType ||
- field.contactType != info.contactType ||
- field.fieldName != info.fieldName) {
- Cu.reportError("Autocomplete tokens mismatched");
- continue;
- }
-
- fieldDetail.element.setUserInput(field.value);
- }
- },
-};
-
-// Register/unregister a constructor as a factory.
-function AutocompleteFactory() {}
-AutocompleteFactory.prototype = {
- register(targetConstructor) {
- let proto = targetConstructor.prototype;
- this._classID = proto.classID;
-
- let factory = XPCOMUtils._getFactory(targetConstructor);
- this._factory = factory;
-
- let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
- registrar.registerFactory(proto.classID, proto.classDescription,
- proto.contractID, factory);
-
- if (proto.classID2) {
- this._classID2 = proto.classID2;
- registrar.registerFactory(proto.classID2, proto.classDescription,
- proto.contractID2, factory);
- }
- },
-
- unregister() {
- let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
- registrar.unregisterFactory(this._classID, this._factory);
- if (this._classID2) {
- registrar.unregisterFactory(this._classID2, this._factory);
- }
- this._factory = null;
- },
-};
-
-
-/**
- * @constructor
- *
- * @implements {nsIAutoCompleteSearch}
- */
-function AutofillProfileAutoCompleteSearch() {
-
-}
-AutofillProfileAutoCompleteSearch.prototype = {
- classID: Components.ID("4f9f1e4c-7f2c-439e-9c9e-566b68bc187d"),
- contractID: "@mozilla.org/autocomplete/search;1?name=autofill-profiles",
- classDescription: "AutofillProfileAutoCompleteSearch",
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteSearch]),
-
- // Begin nsIAutoCompleteSearch implementation
-
- /**
- * Searches for a given string and notifies a listener (either synchronously
- * or asynchronously) of the result
- *
- * @param {string} searchString the string to search for
- * @param {string} searchParam
- * @param {Object} previousResult a previous result to use for faster searchinig
- * @param {Object} listener the listener to notify when the search is complete
- */
- startSearch(searchString, searchParam, previousResult, listener) {
- this.forceStop = false;
- let info = this.getInputDetails();
-
- this.getProfiles({info, searchString}).then((profiles) => {
- if (this.forceStop) {
- return;
- }
-
- // TODO: Set formInfo for ProfileAutoCompleteResult
- // let formInfo = this.getFormDetails();
- let result = new ProfileAutoCompleteResult(searchString, info, profiles, {});
-
- listener.onSearchResult(this, result);
- });
- },
-
- /**
- * Stops an asynchronous search that is in progress
- */
- stopSearch() {
- this.forceStop = true;
- },
-
- /**
- * Get the profile data from parent process for AutoComplete result.
- *
- * @private
- * @param {Object} data
- * Parameters for querying the corresponding result.
- * @param {string} data.searchString
- * The typed string for filtering out the matched profile.
- * @param {string} data.info
- * The input autocomplete property's information.
- * @returns {Promise}
- * Promise that resolves when profiles returned from parent process.
- */
- getProfiles(data) {
- return new Promise((resolve) => {
- addMessageListener("FormAutofill:Profiles", function getResult(result) {
- removeMessageListener("FormAutofill:Profiles", getResult);
- resolve(result.data);
- });
-
- sendAsyncMessage("FormAutofill:GetProfiles", data);
- });
- },
-
-
- /**
- * Get the input's information from FormAutofillContent's cache.
- *
- * @returns {Object}
- * Target input's information that cached in FormAutofillContent.
- */
- getInputDetails() {
- // TODO: Maybe we'll need to wait for cache ready if detail is empty.
- return FormAutofillContent.getInputDetails(formFillController.focusedInput);
- },
-
- /**
- * Get the form's information from FormAutofillContent's cache.
- *
- * @returns {Array<Object>}
- * Array of the inputs' information for the target form.
- */
- getFormDetails() {
- // TODO: Maybe we'll need to wait for cache ready if details is empty.
- return FormAutofillContent.getFormDetails(formFillController.focusedInput);
- },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AutofillProfileAutoCompleteSearch]);
-
-let ProfileAutocomplete = {
- _registered: false,
- _factory: null,
-
- ensureRegistered() {
- if (this._registered) {
- return;
- }
-
- this._factory = new AutocompleteFactory();
- this._factory.register(AutofillProfileAutoCompleteSearch);
- this._registered = true;
- },
-
- ensureUnregistered() {
- if (!this._registered) {
- return;
- }
-
- this._factory.unregister();
- this._factory = null;
- this._registered = false;
- },
-};
-
-/**
- * Handles content's interactions.
+ * Handles content's interactions for the frame.
*
* NOTE: Declares it by "var" to make it accessible in unit tests.
*/
-var FormAutofillContent = {
+var FormAutofillFrameScript = {
init() {
addEventListener("DOMContentLoaded", this);
-
- addMessageListener("FormAutofill:enabledStatus", (result) => {
- if (result.data) {
- ProfileAutocomplete.ensureRegistered();
- } else {
- ProfileAutocomplete.ensureUnregistered();
- }
- });
- sendAsyncMessage("FormAutofill:getEnabledStatus");
},
handleEvent(evt) {
if (!evt.isTrusted) {
return;
}
+ if (!Services.prefs.getBoolPref("browser.formautofill.enabled")) {
+ return;
+ }
+
switch (evt.type) {
- case "DOMContentLoaded":
+ case "DOMContentLoaded": {
let doc = evt.target;
if (!(doc instanceof Ci.nsIDOMHTMLDocument)) {
return;
}
- this._identifyAutofillFields(doc);
+ this.FormAutofillContent._identifyAutofillFields(doc);
break;
- }
- },
-
- /**
- * Get the input's information from cache which is created after page identified.
- *
- * @param {HTMLInputElement} element Focused input which triggered profile searching
- * @returns {Object|null}
- * Return target input's information that cloned from content cache
- * (or return null if the information is not found in the cache).
- */
- getInputDetails(element) {
- for (let formDetails of this._formsDetails) {
- for (let detail of formDetails) {
- if (element == detail.element) {
- return this._serializeInfo(detail);
- }
- }
- }
- return null;
- },
-
- /**
- * Get the form's information from cache which is created after page identified.
- *
- * @param {HTMLInputElement} element Focused input which triggered profile searching
- * @returns {Array<Object>|null}
- * Return target form's information that cloned from content cache
- * (or return null if the information is not found in the cache).
- *
- */
- getFormDetails(element) {
- for (let formDetails of this._formsDetails) {
- if (formDetails.some((detail) => detail.element == element)) {
- return formDetails.map((detail) => this._serializeInfo(detail));
}
}
- return null;
- },
-
- /**
- * Create a clone the information object without element reference.
- *
- * @param {Object} detail Profile autofill information for specific input.
- * @returns {Object}
- * Return a copy of cached information object without element reference
- * since it's not needed for creating result.
- */
- _serializeInfo(detail) {
- let info = Object.assign({}, detail);
- delete info.element;
- return info;
- },
-
- _identifyAutofillFields(doc) {
- let forms = [];
- this._formsDetails = [];
-
- // Collects root forms from inputs.
- for (let field of doc.getElementsByTagName("input")) {
- // We only consider text-like fields for now until we support radio and
- // checkbox buttons in the future.
- if (!field.mozIsTextField(true)) {
- continue;
- }
-
- let formLike = FormLikeFactory.createFromField(field);
- if (!forms.some(form => form.rootElement === formLike.rootElement)) {
- forms.push(formLike);
- }
- }
-
- // Collects the fields that can be autofilled from each form and marks them
- // as autofill fields if the amount is above the threshold.
- forms.forEach(form => {
- let formHandler = new FormAutofillHandler(form);
- formHandler.collectFormFields();
- if (formHandler.fieldDetails.length < AUTOFILL_FIELDS_THRESHOLD) {
- return;
- }
-
- this._formsDetails.push(formHandler.fieldDetails);
- formHandler.fieldDetails.forEach(
- detail => this._markAsAutofillField(detail.element));
- });
- },
-
- _markAsAutofillField(field) {
- formFillController.markAsAutofillField(field);
},
};
+XPCOMUtils.defineLazyModuleGetter(FormAutofillFrameScript, "FormAutofillContent",
+ "resource://formautofill/FormAutofillContent.jsm");
-FormAutofillContent.init();
+FormAutofillFrameScript.init();
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -40,31 +40,16 @@ if (!extensionDir.exists()) {
Components.manager.addBootstrappedManifestLocation(extensionDir);
// While the previous test file should have deleted all the temporary files it
// used, on Windows these might still be pending deletion on the physical file
// system. Thus, start from a new base number every time, to make a collision
// with a file that is still pending deletion highly unlikely.
let gFileCounter = Math.floor(Math.random() * 1000000);
-function loadFormAutofillContent() {
- let facGlobal = {
- addEventListener() {},
- addMessageListener() {},
- sendAsyncMessage() {},
- };
- let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
- .getService(Ci.mozIJSSubScriptLoader);
- loader.loadSubScriptWithOptions("chrome://formautofill/content/FormAutofillContent.js", {
- target: facGlobal,
- });
-
- return facGlobal;
-}
-
/**
* Returns a reference to a temporary file, that is guaranteed not to exist, and
* to have never been created before.
*
* @param {string} leafName
* Suggested leaf name for the file to be created.
*
* @returns {nsIFile} pointing to a non-existent file in a temporary directory.
--- a/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
@@ -1,15 +1,15 @@
/*
* Test for form auto fill content helper fill all inputs function.
*/
"use strict";
-let {FormAutofillHandler} = loadFormAutofillContent();
+Cu.import("resource://formautofill/FormAutofillHandler.jsm");
const TESTCASES = [
{
description: "Form without autocomplete property",
document: `<form><input id="given-name"><input id="family-name">
<input id="street-addr"><input id="city"><input id="country">
<input id='email'><input id="tel"></form>`,
fieldDetails: [],
--- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -1,15 +1,15 @@
/*
* Test for form auto fill content helper collectFormFields functions.
*/
"use strict";
-let {FormAutofillHandler} = loadFormAutofillContent();
+Cu.import("resource://formautofill/FormAutofillHandler.jsm");
const TESTCASES = [
{
description: "Form without autocomplete property",
document: `<form><input id="given-name"><input id="family-name">
<input id="street-addr"><input id="city"><input id="country">
<input id='email'><input id="tel"></form>`,
returnedFormat: [],
--- a/browser/extensions/formautofill/test/unit/test_getFormInputDetails.js
+++ b/browser/extensions/formautofill/test/unit/test_getFormInputDetails.js
@@ -1,11 +1,11 @@
"use strict";
-let {FormAutofillContent} = loadFormAutofillContent();
+Cu.import("resource://formautofill/FormAutofillContent.jsm");
const TESTCASES = [
{
description: "Form containing 5 fields with autocomplete attribute.",
document: `<form>
<input id="street-addr" autocomplete="street-address">
<input id="city" autocomplete="address-level2">
<input id="country" autocomplete="country">
--- a/browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
+++ b/browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
@@ -1,11 +1,11 @@
"use strict";
-let {FormAutofillContent} = loadFormAutofillContent();
+Cu.import("resource://formautofill/FormAutofillContent.jsm");
const TESTCASES = [
{
description: "Form containing 5 fields with autocomplete attribute.",
document: `<form>
<input id="street-addr" autocomplete="street-address">
<input id="city" autocomplete="address-level2">
<input id="country" autocomplete="country">
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -74,17 +74,16 @@
"extension-storage.js": ["ExtensionStorageEngine", "EncryptionRemoteTransformer", "KeyRingEncryptionRemoteTransformer"],
"ExtensionXPCShellUtils.jsm": ["ExtensionTestUtils"],
"fakeservices.js": ["FakeCryptoService", "FakeFilesystemService", "FakeGUIDService", "fakeSHA256HMAC"],
"file_expandosharing.jsm": ["checkFromJSM"],
"file_stringencoding.jsm": ["checkFromJSM"],
"file_url.jsm": ["checkFromJSM"],
"file_worker_url.jsm": ["checkFromJSM"],
"Finder.jsm": ["Finder", "GetClipboardSearchString"],
- "FormAutofillContent.jsm": ["FormAutofillHandler"],
"forms.js": ["FormEngine", "FormRec", "FormValidator"],
"forms.jsm": ["FormData"],
"frame.js": ["Collector", "Runner", "events", "runTestFile", "log", "timers", "persisted", "shutdownApplication"],
"FrameScriptManager.jsm": ["getNewLoaderID"],
"fxa_utils.js": ["initializeIdentityWithTokenServerResponse"],
"fxaccounts.jsm": ["Authentication"],
"FxAccounts.jsm": ["fxAccounts", "FxAccounts"],
"FxAccountsCommon.js": ["log", "logPII", "FXACCOUNTS_PERMISSION", "DATA_FORMAT_VERSION", "DEFAULT_STORAGE_FILENAME", "ASSERTION_LIFETIME", "ASSERTION_USE_PERIOD", "CERT_LIFETIME", "KEY_LIFETIME", "POLL_SESSION", "ONLOGIN_NOTIFICATION", "ONVERIFIED_NOTIFICATION", "ONLOGOUT_NOTIFICATION", "ON_FXA_UPDATE_NOTIFICATION", "ON_DEVICE_CONNECTED_NOTIFICATION", "ON_DEVICE_DISCONNECTED_NOTIFICATION", "ON_PASSWORD_CHANGED_NOTIFICATION", "ON_PASSWORD_RESET_NOTIFICATION", "ON_COLLECTION_CHANGED_NOTIFICATION", "FXA_PUSH_SCOPE_ACCOUNT_UPDATE", "ON_PROFILE_CHANGE_NOTIFICATION", "ON_ACCOUNT_STATE_CHANGE_NOTIFICATION", "UI_REQUEST_SIGN_IN_FLOW", "UI_REQUEST_REFRESH_AUTH", "FX_OAUTH_CLIENT_ID", "WEBCHANNEL_ID", "ERRNO_ACCOUNT_ALREADY_EXISTS", "ERRNO_ACCOUNT_DOES_NOT_EXIST", "ERRNO_INCORRECT_PASSWORD", "ERRNO_UNVERIFIED_ACCOUNT", "ERRNO_INVALID_VERIFICATION_CODE", "ERRNO_NOT_VALID_JSON_BODY", "ERRNO_INVALID_BODY_PARAMETERS", "ERRNO_MISSING_BODY_PARAMETERS", "ERRNO_INVALID_REQUEST_SIGNATURE", "ERRNO_INVALID_AUTH_TOKEN", "ERRNO_INVALID_AUTH_TIMESTAMP", "ERRNO_MISSING_CONTENT_LENGTH", "ERRNO_REQUEST_BODY_TOO_LARGE", "ERRNO_TOO_MANY_CLIENT_REQUESTS", "ERRNO_INVALID_AUTH_NONCE", "ERRNO_ENDPOINT_NO_LONGER_SUPPORTED", "ERRNO_INCORRECT_LOGIN_METHOD", "ERRNO_INCORRECT_KEY_RETRIEVAL_METHOD", "ERRNO_INCORRECT_API_VERSION", "ERRNO_INCORRECT_EMAIL_CASE", "ERRNO_ACCOUNT_LOCKED", "ERRNO_ACCOUNT_UNLOCKED", "ERRNO_UNKNOWN_DEVICE", "ERRNO_DEVICE_SESSION_CONFLICT", "ERRNO_SERVICE_TEMP_UNAVAILABLE", "ERRNO_PARSE", "ERRNO_NETWORK", "ERRNO_UNKNOWN_ERROR", "OAUTH_SERVER_ERRNO_OFFSET", "ERRNO_UNKNOWN_CLIENT_ID", "ERRNO_INCORRECT_CLIENT_SECRET", "ERRNO_INCORRECT_REDIRECT_URI", "ERRNO_INVALID_FXA_ASSERTION", "ERRNO_UNKNOWN_CODE", "ERRNO_INCORRECT_CODE", "ERRNO_EXPIRED_CODE", "ERRNO_OAUTH_INVALID_TOKEN", "ERRNO_INVALID_REQUEST_PARAM", "ERRNO_INVALID_RESPONSE_TYPE", "ERRNO_UNAUTHORIZED", "ERRNO_FORBIDDEN", "ERRNO_INVALID_CONTENT_TYPE", "ERROR_ACCOUNT_ALREADY_EXISTS", "ERROR_ACCOUNT_DOES_NOT_EXIST", "ERROR_ACCOUNT_LOCKED", "ERROR_ACCOUNT_UNLOCKED", "ERROR_ALREADY_SIGNED_IN_USER", "ERROR_DEVICE_SESSION_CONFLICT", "ERROR_ENDPOINT_NO_LONGER_SUPPORTED", "ERROR_INCORRECT_API_VERSION", "ERROR_INCORRECT_EMAIL_CASE", "ERROR_INCORRECT_KEY_RETRIEVAL_METHOD", "ERROR_INCORRECT_LOGIN_METHOD", "ERROR_INVALID_EMAIL", "ERROR_INVALID_AUDIENCE", "ERROR_INVALID_AUTH_TOKEN", "ERROR_INVALID_AUTH_TIMESTAMP", "ERROR_INVALID_AUTH_NONCE", "ERROR_INVALID_BODY_PARAMETERS", "ERROR_INVALID_PASSWORD", "ERROR_INVALID_VERIFICATION_CODE", "ERROR_INVALID_REFRESH_AUTH_VALUE", "ERROR_INVALID_REQUEST_SIGNATURE", "ERROR_INTERNAL_INVALID_USER", "ERROR_MISSING_BODY_PARAMETERS", "ERROR_MISSING_CONTENT_LENGTH", "ERROR_NO_TOKEN_SESSION", "ERROR_NO_SILENT_REFRESH_AUTH", "ERROR_NOT_VALID_JSON_BODY", "ERROR_OFFLINE", "ERROR_PERMISSION_DENIED", "ERROR_REQUEST_BODY_TOO_LARGE", "ERROR_SERVER_ERROR", "ERROR_SYNC_DISABLED", "ERROR_TOO_MANY_CLIENT_REQUESTS", "ERROR_SERVICE_TEMP_UNAVAILABLE", "ERROR_UI_ERROR", "ERROR_UI_REQUEST", "ERROR_PARSE", "ERROR_NETWORK", "ERROR_UNKNOWN", "ERROR_UNKNOWN_DEVICE", "ERROR_UNVERIFIED_ACCOUNT", "ERROR_UNKNOWN_CLIENT_ID", "ERROR_INCORRECT_CLIENT_SECRET", "ERROR_INCORRECT_REDIRECT_URI", "ERROR_INVALID_FXA_ASSERTION", "ERROR_UNKNOWN_CODE", "ERROR_INCORRECT_CODE", "ERROR_EXPIRED_CODE", "ERROR_OAUTH_INVALID_TOKEN", "ERROR_INVALID_REQUEST_PARAM", "ERROR_INVALID_RESPONSE_TYPE", "ERROR_UNAUTHORIZED", "ERROR_FORBIDDEN", "ERROR_INVALID_CONTENT_TYPE", "ERROR_NO_ACCOUNT", "ERROR_AUTH_ERROR", "ERROR_INVALID_PARAMETER", "ERROR_CODE_METHOD_NOT_ALLOWED", "ERROR_MSG_METHOD_NOT_ALLOWED", "FXA_PWDMGR_PLAINTEXT_FIELDS", "FXA_PWDMGR_SECURE_FIELDS", "FXA_PWDMGR_MEMORY_FIELDS", "FXA_PWDMGR_REAUTH_WHITELIST", "FXA_PWDMGR_HOST", "FXA_PWDMGR_REALM", "SERVER_ERRNO_TO_ERROR", "ERROR_TO_GENERAL_ERROR_CLASS"],