Bug 1364477 - Delay ProfileStorage initialization until focusin. r=lchang,seanlee,steveck
MozReview-Commit-ID: CNlVNOI1mWw
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -300,22 +300,28 @@ var FormAutofillContent = {
init() {
FormAutofillUtils.defineLazyLogGetter(this, "FormAutofillContent");
Services.cpmm.addMessageListener("FormAutofill:enabledStatus", this);
Services.cpmm.addMessageListener("FormAutofill:savedFieldNames", this);
Services.obs.addObserver(this, "earlyformsubmit");
- if (Services.cpmm.initialProcessData.autofillEnabled) {
+ 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"))) {
ProfileAutocomplete.ensureRegistered();
}
this.savedFieldNames =
- Services.cpmm.initialProcessData.autofillSavedFieldNames || new Set();
+ Services.cpmm.initialProcessData.autofillSavedFieldNames;
},
_onFormSubmit(handler) {
// TODO: Handle form submit event for profile saving(bug 990219) and metrics(bug 1341569).
},
notify(formElement) {
this.log.debug("notified for form early submission");
@@ -395,16 +401,22 @@ var FormAutofillContent = {
getAllFieldNames(element) {
let formDetails = this.getFormDetails(element);
return formDetails.map(record => record.fieldName);
},
identifyAutofillFields(doc) {
this.log.debug("identifyAutofillFields:", "" + doc.location);
+
+ if (!this.savedFieldNames) {
+ this.log.debug("identifyAutofillFields: savedFieldNames are not known yet");
+ Services.cpmm.sendAsyncMessage("FormAutofill:InitStorage");
+ }
+
let forms = [];
// 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;
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -34,55 +34,63 @@ this.EXPORTED_SYMBOLS = ["FormAutofillPa
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://gre/modules/Services.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "profileStorage",
- "resource://formautofill/ProfileStorage.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillPreferences",
"resource://formautofill/FormAutofillPreferences.jsm");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
const ENABLED_PREF = "extensions.formautofill.addresses.enabled";
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");
+
+ profileStorage.initialize().then(function onStorageInitialized() {
+ // Update the saved field names to compute the status and update child processes.
+ this._updateSavedFieldNames();
+ }.bind(this));
+
+ return profileStorage;
+ });
}
FormAutofillParent.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
/**
* Cache of the Form Autofill status (considering preferences and storage).
*/
_active: null,
/**
* Initializes ProfileStorage and registers the message handler.
*/
async init() {
log.debug("init");
- await profileStorage.initialize();
Services.obs.addObserver(this, "advanced-pane-loaded");
+ Services.ppmm.addMessageListener("FormAutofill:InitStorage", this);
Services.ppmm.addMessageListener("FormAutofill:GetAddresses", this);
Services.ppmm.addMessageListener("FormAutofill:SaveAddress", this);
Services.ppmm.addMessageListener("FormAutofill:RemoveAddresses", this);
// Observing the pref and storage changes
Services.prefs.addObserver(ENABLED_PREF, this);
Services.obs.addObserver(this, "formautofill-storage-changed");
-
- // Update the saved field names to compute the status and update child processes.
- this._updateSavedFieldNames();
},
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",
false);
@@ -161,43 +169,48 @@ FormAutofillParent.prototype = {
* Handles the message coming from FormAutofillContent.
*
* @param {string} message.name The name of the message.
* @param {object} message.data The data of the message.
* @param {nsIFrameMessageManager} message.target Caller's message manager.
*/
receiveMessage({name, data, target}) {
switch (name) {
+ case "FormAutofill:InitStorage": {
+ this.profileStorage.initialize();
+ break;
+ }
case "FormAutofill:GetAddresses": {
this._getAddresses(data, target);
break;
}
case "FormAutofill:SaveAddress": {
if (data.guid) {
- profileStorage.addresses.update(data.guid, data.address);
+ this.profileStorage.addresses.update(data.guid, data.address);
} else {
- profileStorage.addresses.add(data.address);
+ this.profileStorage.addresses.add(data.address);
}
break;
}
case "FormAutofill:RemoveAddresses": {
- data.guids.forEach(guid => profileStorage.addresses.remove(guid));
+ data.guids.forEach(guid => this.profileStorage.addresses.remove(guid));
break;
}
}
},
/**
* Uninitializes FormAutofillParent. This is for testing only.
*
* @private
*/
_uninit() {
- profileStorage._saveImmediately();
+ this.profileStorage._saveImmediately();
+ Services.ppmm.removeMessageListener("FormAutofill:InitStorage", this);
Services.ppmm.removeMessageListener("FormAutofill:GetAddresses", this);
Services.ppmm.removeMessageListener("FormAutofill:SaveAddress", this);
Services.ppmm.removeMessageListener("FormAutofill:RemoveAddresses", this);
Services.obs.removeObserver(this, "advanced-pane-loaded");
Services.prefs.removeObserver(ENABLED_PREF, this);
},
/**
@@ -211,43 +224,43 @@ FormAutofillParent.prototype = {
* The input autocomplete property's information.
* @param {nsIFrameMessageManager} target
* Content's message manager.
*/
_getAddresses({searchString, info}, target) {
let addresses = [];
if (info && info.fieldName) {
- addresses = profileStorage.addresses.getByFilter({searchString, info});
+ addresses = this.profileStorage.addresses.getByFilter({searchString, info});
} else {
- addresses = profileStorage.addresses.getAll();
+ addresses = this.profileStorage.addresses.getAll();
}
target.sendAsyncMessage("FormAutofill:Addresses", addresses);
},
_updateSavedFieldNames() {
log.debug("_updateSavedFieldNames");
if (!Services.ppmm.initialProcessData.autofillSavedFieldNames) {
Services.ppmm.initialProcessData.autofillSavedFieldNames = new Set();
} else {
Services.ppmm.initialProcessData.autofillSavedFieldNames.clear();
}
- profileStorage.addresses.getAll().forEach((address) => {
+ this.profileStorage.addresses.getAll().forEach((address) => {
Object.keys(address).forEach((fieldName) => {
if (!address[fieldName]) {
return;
}
Services.ppmm.initialProcessData.autofillSavedFieldNames.add(fieldName);
});
});
// Remove the internal guid and metadata fields.
- profileStorage.INTERNAL_FIELDS.forEach((fieldName) => {
+ this.profileStorage.INTERNAL_FIELDS.forEach((fieldName) => {
Services.ppmm.initialProcessData.autofillSavedFieldNames.delete(fieldName);
});
Services.ppmm.broadcastAsyncMessage("FormAutofill:savedFieldNames",
Services.ppmm.initialProcessData.autofillSavedFieldNames);
this._updateStatus();
},
};
--- a/browser/extensions/formautofill/test/unit/test_activeStatus.js
+++ b/browser/extensions/formautofill/test/unit/test_activeStatus.js
@@ -11,16 +11,24 @@ add_task(async function test_activeStatu
let formAutofillParent = new FormAutofillParent();
sinon.spy(formAutofillParent, "_updateStatus");
// Default status is null before initialization
do_check_eq(formAutofillParent._active, null);
do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, undefined);
await formAutofillParent.init();
+ // init shouldn't call updateStatus since that requires storage which will
+ // lead to startup time regressions.
+ do_check_eq(formAutofillParent._updateStatus.called, false);
+ do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, undefined);
+
+ // Initialize profile storage
+ await formAutofillParent.profileStorage.initialize();
+ // Upon first initializing profile storage, status should be computed.
do_check_eq(formAutofillParent._updateStatus.called, true);
do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, false);
formAutofillParent._uninit();
});
add_task(async function test_activeStatus_observe() {
let formAutofillParent = new FormAutofillParent();
--- a/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
+++ b/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
@@ -7,16 +7,17 @@
Cu.import("resource://formautofill/FormAutofillParent.jsm");
Cu.import("resource://formautofill/ProfileStorage.jsm");
add_task(async function test_profileSavedFieldNames_init() {
let formAutofillParent = new FormAutofillParent();
sinon.stub(formAutofillParent, "_updateSavedFieldNames");
await formAutofillParent.init();
+ await formAutofillParent.profileStorage.initialize();
do_check_eq(formAutofillParent._updateSavedFieldNames.called, true);
formAutofillParent._uninit();
});
add_task(async function test_profileSavedFieldNames_observe() {
let formAutofillParent = new FormAutofillParent();
sinon.stub(formAutofillParent, "_updateSavedFieldNames");