Bug 1390757 - Introduce pref extensions.formautofill.creditCards.enabled to form autofill activation process. r=lchang
MozReview-Commit-ID: J5nyAeSoggr
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -27,16 +27,17 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://formautofill/FormAutofillHandler.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormLikeFactory",
"resource://gre/modules/FormLikeFactory.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "InsecurePasswordUtils",
"resource://gre/modules/InsecurePasswordUtils.jsm");
const formFillController = Cc["@mozilla.org/satchel/form-fill-controller;1"]
.getService(Ci.nsIFormFillController);
+const {ADDRESSES_COLLECTION_NAME, CREDITCARDS_COLLECTION_NAME} = FormAutofillUtils;
// Register/unregister a constructor as a factory.
function AutocompleteFactory() {}
AutocompleteFactory.prototype = {
register(targetConstructor) {
let proto = targetConstructor.prototype;
this._classID = proto.classID;
@@ -98,35 +99,39 @@ AutofillProfileAutoCompleteSearch.protot
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.allFieldNames;
let filledRecordGUID = isAddressField ? handler.address.filledRecordGUID : handler.creditCard.filledRecordGUID;
+ let searchPermitted = isAddressField ?
+ FormAutofillUtils.isAutofillAddressesEnabled :
+ FormAutofillUtils.isAutofillCreditCardsEnabled;
// Fallback to form-history if ...
+ // - specified autofill feature is pref off.
// - no profile can fill the currently-focused input.
// - the current form has already been populated.
// - (address only) less than 3 inputs are covered by all saved fields in the storage.
- if (!savedFieldNames.has(info.fieldName) || filledRecordGUID || (isAddressField &&
+ if (!searchPermitted || !savedFieldNames.has(info.fieldName) || filledRecordGUID || (isAddressField &&
allFieldNames.filter(field => savedFieldNames.has(field)).length < FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD)) {
let formHistory = Cc["@mozilla.org/autocomplete/search;1?name=form-history"]
.createInstance(Ci.nsIAutoCompleteSearch);
formHistory.startSearch(searchString, searchParam, previousResult, {
onSearchResult: (search, result) => {
listener.onSearchResult(this, result);
ProfileAutocomplete.setProfileAutoCompleteResult(result);
},
});
return;
}
- let collectionName = isAddressField ? "addresses" : "creditCards";
+ let collectionName = isAddressField ? ADDRESSES_COLLECTION_NAME : CREDITCARDS_COLLECTION_NAME;
this._getRecords({collectionName, info, searchString}).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);
@@ -336,22 +341,23 @@ var FormAutofillContent = {
init() {
FormAutofillUtils.defineLazyLogGetter(this, "FormAutofillContent");
Services.cpmm.addMessageListener("FormAutofill:enabledStatus", this);
Services.cpmm.addMessageListener("FormAutofill:savedFieldNames", this);
Services.obs.addObserver(this, "earlyformsubmit");
let autofillEnabled = Services.cpmm.initialProcessData.autofillEnabled;
- if (autofillEnabled ||
- // If storage hasn't be initialized yet autofillEnabled is undefined but we need to ensure
- // autocomplete is registered before the focusin so register it in this case as long as the
- // pref is true.
- (autofillEnabled === undefined &&
- Services.prefs.getBoolPref("extensions.formautofill.addresses.enabled"))) {
+ // If storage hasn't be initialized yet autofillEnabled is undefined but we need to ensure
+ // autocomplete is registered before the focusin so register it in this case as long as the
+ // pref is true.
+ let shouldEnableAutofill = autofillEnabled === undefined &&
+ (FormAutofillUtils.isAutofillAddressesEnabled ||
+ FormAutofillUtils.isAutofillCreditCardsEnabled);
+ if (autofillEnabled || shouldEnableAutofill) {
ProfileAutocomplete.ensureRegistered();
}
this.savedFieldNames =
Services.cpmm.initialProcessData.autofillSavedFieldNames;
},
/**
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -45,17 +45,17 @@ XPCOMUtils.defineLazyModuleGetter(this,
XPCOMUtils.defineLazyModuleGetter(this, "MasterPassword",
"resource://formautofill/MasterPassword.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
"resource:///modules/RecentWindow.jsm");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
-const ENABLED_PREF = "extensions.formautofill.addresses.enabled";
+const {ENABLED_AUTOFILL_ADDRESSES_PREF, ENABLED_AUTOFILL_CREDITCARDS_PREF} = FormAutofillUtils;
function FormAutofillParent() {
// Lazily load the storage JSM to avoid disk I/O until absolutely needed.
// Once storage is loaded we need to update saved field names and inform content processes.
XPCOMUtils.defineLazyGetter(this, "profileStorage", () => {
let {profileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {});
log.debug("Loading profileStorage");
@@ -86,17 +86,18 @@ FormAutofillParent.prototype = {
Services.ppmm.addMessageListener("FormAutofill:SaveAddress", this);
Services.ppmm.addMessageListener("FormAutofill:SaveCreditCard", this);
Services.ppmm.addMessageListener("FormAutofill:RemoveAddresses", this);
Services.ppmm.addMessageListener("FormAutofill:RemoveCreditCards", this);
Services.ppmm.addMessageListener("FormAutofill:OpenPreferences", this);
Services.mm.addMessageListener("FormAutofill:OnFormSubmit", this);
// Observing the pref and storage changes
- Services.prefs.addObserver(ENABLED_PREF, this);
+ Services.prefs.addObserver(ENABLED_AUTOFILL_ADDRESSES_PREF, this);
+ Services.prefs.addObserver(ENABLED_AUTOFILL_CREDITCARDS_PREF, this);
Services.obs.addObserver(this, "formautofill-storage-changed");
},
observe(subject, topic, data) {
log.debug("observe:", topic, "with data:", data);
switch (topic) {
case "advanced-pane-loaded": {
let useOldOrganization = Services.prefs.getBoolPref("browser.preferences.useOldOrganization",
@@ -149,21 +150,22 @@ FormAutofillParent.prototype = {
/**
* Query preference and storage status to determine the overall status of the
* form autofill feature.
*
* @returns {boolean} whether form autofill is active (enabled and has data)
*/
_computeStatus() {
- if (!Services.prefs.getBoolPref(ENABLED_PREF)) {
- return false;
- }
+ const savedFieldNames = Services.ppmm.initialProcessData.autofillSavedFieldNames;
- return Services.ppmm.initialProcessData.autofillSavedFieldNames.size > 0;
+ return (Services.prefs.getBoolPref(ENABLED_AUTOFILL_ADDRESSES_PREF) ||
+ Services.prefs.getBoolPref(ENABLED_AUTOFILL_CREDITCARDS_PREF)) &&
+ savedFieldNames &&
+ savedFieldNames.size > 0;
},
/**
* Update the status and trigger _onStatusChanged, if necessary.
*/
_updateStatus() {
let wasActive = this._active;
this._active = this._computeStatus();
@@ -231,17 +233,18 @@ FormAutofillParent.prototype = {
Services.ppmm.removeMessageListener("FormAutofill:InitStorage", this);
Services.ppmm.removeMessageListener("FormAutofill:GetRecords", this);
Services.ppmm.removeMessageListener("FormAutofill:SaveAddress", this);
Services.ppmm.removeMessageListener("FormAutofill:SaveCreditCard", this);
Services.ppmm.removeMessageListener("FormAutofill:RemoveAddresses", this);
Services.ppmm.removeMessageListener("FormAutofill:RemoveCreditCards", this);
Services.obs.removeObserver(this, "advanced-pane-loaded");
- Services.prefs.removeObserver(ENABLED_PREF, this);
+ Services.prefs.removeObserver(ENABLED_AUTOFILL_ADDRESSES_PREF, this);
+ Services.prefs.removeObserver(ENABLED_AUTOFILL_CREDITCARDS_PREF, this);
},
/**
* Get the records from profile store and return results back to content
* process.
*
* @private
* @param {string} data.collectionName
--- a/browser/extensions/formautofill/FormAutofillPreferences.jsm
+++ b/browser/extensions/formautofill/FormAutofillPreferences.jsm
@@ -8,48 +8,40 @@
"use strict";
this.EXPORTED_SYMBOLS = ["FormAutofillPreferences"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
// Add addresses enabled flag in telemetry environment for recording the number of
// users who disable/enable the address autofill feature.
-const PREF_AUTOFILL_ENABLED = "extensions.formautofill.addresses.enabled";
-// Add credit card enabled flag in telemetry environment for recording the number of
-// users who disable/enable the credit card autofill feature.
-// TODO: Add const PREF_CREDITCARD_ENABLED = "extensions.formautofill.creditCards.enabled";
-// when the credit card preferences UI is ready
const BUNDLE_URI = "chrome://formautofill/locale/formautofill.properties";
const MANAGE_ADDRESSES_URL = "chrome://formautofill/content/manageAddresses.xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
+const {ENABLED_AUTOFILL_ADDRESSES_PREF} = FormAutofillUtils;
+// Add credit card enabled flag in telemetry environment for recording the number of
+// users who disable/enable the credit card autofill feature.
+// TODO: Add const PREF_CREDITCARD_ENABLED = "extensions.formautofill.creditCards.enabled";
+// when the credit card preferences UI is ready
+
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
function FormAutofillPreferences({useOldOrganization}) {
this.useOldOrganization = useOldOrganization;
this.bundle = Services.strings.createBundle(BUNDLE_URI);
}
FormAutofillPreferences.prototype = {
/**
- * Check if Form Autofill feature is enabled.
- *
- * @returns {boolean}
- */
- get isAutofillEnabled() {
- return Services.prefs.getBoolPref(PREF_AUTOFILL_ENABLED);
- },
-
- /**
* Create the Form Autofill preference group.
*
* @param {XULDocument} document
* @returns {XULElement}
*/
init(document) {
this.createPreferenceGroup(document);
this.attachEventListeners();
@@ -99,17 +91,17 @@ FormAutofillPreferences.prototype = {
};
formAutofillGroup.id = "formAutofillGroup";
addressAutofill.id = "addressAutofill";
savedAddressesBtn.setAttribute("label", this.bundle.GetStringFromName("savedAddresses"));
addressAutofillCheckbox.setAttribute("label", this.bundle.GetStringFromName("enableAddressAutofill"));
// Manually set the checked state
- if (this.isAutofillEnabled) {
+ if (FormAutofillUtils.isAutofillAddressesEnabled) {
addressAutofillCheckbox.setAttribute("checked", true);
}
addressAutofillCheckbox.flex = 1;
formAutofillGroup.appendChild(addressAutofill);
addressAutofill.appendChild(addressAutofillCheckbox);
addressAutofill.appendChild(savedAddressesBtn);
@@ -122,17 +114,17 @@ FormAutofillPreferences.prototype = {
*/
handleEvent(event) {
switch (event.type) {
case "command": {
let target = event.target;
if (target == this.refs.addressAutofillCheckbox) {
// Set preference directly instead of relying on <Preference>
- Services.prefs.setBoolPref(PREF_AUTOFILL_ENABLED, target.checked);
+ Services.prefs.setBoolPref(ENABLED_AUTOFILL_ADDRESSES_PREF, target.checked);
} else if (target == this.refs.savedAddressesBtn) {
target.ownerGlobal.gSubDialog.open(MANAGE_ADDRESSES_URL);
}
break;
}
}
},
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillUtils.jsm
@@ -11,22 +11,32 @@ const {classes: Cc, interfaces: Ci, util
const ADDRESS_REFERENCES = "chrome://formautofill/content/addressReferences.js";
// TODO: We only support US in MVP. We are going to support more countries in
// bug 1370193.
const ALTERNATIVE_COUNTRY_NAMES = {
"US": ["US", "United States of America", "United States", "America", "U.S.", "USA", "U.S.A.", "U.S.A"],
};
+const ADDRESSES_COLLECTION_NAME = "addresses";
+const CREDITCARDS_COLLECTION_NAME = "creditCards";
+const ENABLED_AUTOFILL_ADDRESSES_PREF = "extensions.formautofill.addresses.enabled";
+const ENABLED_AUTOFILL_CREDITCARDS_PREF = "extensions.formautofill.creditCards.enabled";
+
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
this.FormAutofillUtils = {
get AUTOFILL_FIELDS_THRESHOLD() { return 3; },
+ ADDRESSES_COLLECTION_NAME,
+ CREDITCARDS_COLLECTION_NAME,
+ ENABLED_AUTOFILL_ADDRESSES_PREF,
+ ENABLED_AUTOFILL_CREDITCARDS_PREF,
+
_fieldNameInfo: {
"name": "name",
"given-name": "name",
"additional-name": "name",
"family-name": "name",
"organization": "organization",
"street-address": "address",
"address-line1": "address",
@@ -466,8 +476,13 @@ XPCOMUtils.defineLazyGetter(this.FormAut
});
this.log = null;
this.FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
XPCOMUtils.defineLazyGetter(FormAutofillUtils, "stringBundle", function() {
return Services.strings.createBundle("chrome://formautofill/locale/formautofill.properties");
});
+
+XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
+ "isAutofillAddressesEnabled", ENABLED_AUTOFILL_ADDRESSES_PREF);
+XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
+ "isAutofillCreditCardsEnabled", ENABLED_AUTOFILL_CREDITCARDS_PREF);
--- a/browser/extensions/formautofill/content/FormAutofillFrameScript.js
+++ b/browser/extensions/formautofill/content/FormAutofillFrameScript.js
@@ -30,17 +30,18 @@ var FormAutofillFrameScript = {
addMessageListener("FormAutoComplete:PopupOpened", this);
},
handleEvent(evt) {
if (!evt.isTrusted) {
return;
}
- if (!Services.prefs.getBoolPref("extensions.formautofill.addresses.enabled")) {
+ if (!FormAutofillUtils.isAutofillAddressesEnabled &&
+ !FormAutofillUtils.isAutofillCreditCardsEnable) {
return;
}
switch (evt.type) {
case "focusin": {
let element = evt.target;
let doc = element.ownerDocument;
@@ -57,17 +58,18 @@ var FormAutofillFrameScript = {
doIdentifyAutofillFields();
}
break;
}
}
},
receiveMessage(message) {
- if (!Services.prefs.getBoolPref("extensions.formautofill.addresses.enabled")) {
+ if (!FormAutofillUtils.isAutofillAddressesEnabled &&
+ !FormAutofillUtils.isAutofillCreditCardsEnable) {
return;
}
const doc = content.document;
const {chromeEventHandler} = doc.ownerGlobal.getInterface(Ci.nsIDocShell);
switch (message.name) {
case "FormAutofill:PreviewProfile": {
--- a/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
+++ b/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
@@ -80,16 +80,17 @@ function checkFormFilled(address) {
return Promise.all(promises);
}
async function setupAddressStorage() {
await addAddress(MOCK_STORAGE[0]);
await addAddress(MOCK_STORAGE[1]);
}
+// TODO: Add form history fallback test cases for credit card fields. (bug 1393374)
async function setupFormHistory() {
await updateFormHistory([
{op: "add", fieldname: "tel", value: "+1234567890"},
{op: "add", fieldname: "email", value: "foo@mozilla.com"},
]);
}
initPopupListener();
@@ -167,16 +168,30 @@ add_task(async function check_menu_when_
// Display history search result if no matched data in addresses.
add_task(async function check_fallback_for_mismatched_field() {
await setInput("#email", "");
doKey("down");
await expectPopup();
checkMenuEntries(["foo@mozilla.com"], false);
});
+// Display history search result if address autofill is disabled.
+add_task(async function check_search_result_for_pref_off() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.formautofill.addresses.enabled", false]],
+ });
+
+ await setInput("#tel", "");
+ doKey("down");
+ await expectPopup();
+ checkMenuEntries(["+1234567890"], false);
+
+ await SpecialPowers.popPrefEnv();
+});
+
// Autofill the address from dropdown menu.
add_task(async function check_fields_after_form_autofill() {
await setInput("#organization", "Moz");
doKey("down");
await expectPopup();
checkMenuEntries(MOCK_STORAGE.map(address =>
JSON.stringify({
primary: address.organization,
--- a/browser/extensions/formautofill/test/unit/test_activeStatus.js
+++ b/browser/extensions/formautofill/test/unit/test_activeStatus.js
@@ -34,22 +34,24 @@ add_task(async function test_activeStatu
let formAutofillParent = new FormAutofillParent();
sinon.stub(formAutofillParent, "_computeStatus");
sinon.spy(formAutofillParent, "_onStatusChanged");
// _active = _computeStatus() => No need to trigger _onStatusChanged
formAutofillParent._active = true;
formAutofillParent._computeStatus.returns(true);
formAutofillParent.observe(null, "nsPref:changed", "extensions.formautofill.addresses.enabled");
+ formAutofillParent.observe(null, "nsPref:changed", "extensions.formautofill.creditCards.enabled");
do_check_eq(formAutofillParent._onStatusChanged.called, false);
// _active != _computeStatus() => Need to trigger _onStatusChanged
formAutofillParent._computeStatus.returns(false);
formAutofillParent._onStatusChanged.reset();
formAutofillParent.observe(null, "nsPref:changed", "extensions.formautofill.addresses.enabled");
+ formAutofillParent.observe(null, "nsPref:changed", "extensions.formautofill.creditCards.enabled");
do_check_eq(formAutofillParent._onStatusChanged.called, true);
// profile changed => Need to trigger _onStatusChanged
["add", "update", "remove", "reconcile", "merge"].forEach(event => {
formAutofillParent._computeStatus.returns(!formAutofillParent._active);
formAutofillParent._onStatusChanged.reset();
formAutofillParent.observe(null, "formautofill-storage-changed", event);
do_check_eq(formAutofillParent._onStatusChanged.called, true);
@@ -61,31 +63,45 @@ add_task(async function test_activeStatu
formAutofillParent.observe(null, "formautofill-storage-changed", "notifyUsed");
do_check_eq(formAutofillParent._onStatusChanged.called, false);
});
add_task(async function test_activeStatus_computeStatus() {
let formAutofillParent = new FormAutofillParent();
do_register_cleanup(function cleanup() {
Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled");
+ Services.prefs.clearUserPref("extensions.formautofill.creditCards.enabled");
});
sinon.stub(profileStorage.addresses, "getAll");
profileStorage.addresses.getAll.returns([]);
// pref is enabled and profile is empty.
Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", true);
+ Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", true);
do_check_eq(formAutofillParent._computeStatus(), false);
// pref is disabled and profile is empty.
Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
+ Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
do_check_eq(formAutofillParent._computeStatus(), false);
profileStorage.addresses.getAll.returns([{"given-name": "John"}]);
formAutofillParent.observe(null, "formautofill-storage-changed", "add");
// pref is enabled and profile is not empty.
Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", true);
+ Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", true);
do_check_eq(formAutofillParent._computeStatus(), true);
+ // pref is partial enabled and profile is not empty.
+ Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", true);
+ Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
+ do_check_eq(formAutofillParent._computeStatus(), true);
+ Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
+ Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", true);
+ do_check_eq(formAutofillParent._computeStatus(), true);
+
+
// pref is disabled and profile is not empty.
Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
+ Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
do_check_eq(formAutofillParent._computeStatus(), false);
});