Bug 1330567 - Part 1: Fallback to form history if form autofill pref is disabled, r?MattN draft
authorsteveck-chung <schung@mozilla.com>
Wed, 08 Feb 2017 14:13:59 +0800
changeset 481171 2e1d7607afb367d483b9cca8b4701f9a792dc890
parent 479651 af8a2573d0f1e9cc6f2ba0ab67d7a702a197f177
child 481172 9d168214423532a338317705607bcf1aada9ee4d
child 481630 130a5d0b3795674ab1adba5b31c1c034b561ca9b
push id44734
push userbmo:schung@mozilla.com
push dateThu, 09 Feb 2017 10:15:08 +0000
reviewersMattN
bugs1330567
milestone54.0a1
Bug 1330567 - Part 1: Fallback to form history if form autofill pref is disabled, r?MattN MozReview-Commit-ID: Aq8NhSkxNId
browser/app/profile/firefox.js
browser/extensions/formautofill/FormAutofillParent.jsm
browser/extensions/formautofill/bootstrap.js
browser/extensions/formautofill/content/FormAutofillContent.js
browser/extensions/formautofill/test/unit/head.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1574,14 +1574,15 @@ pref("browser.crashReports.unsubmittedCh
 #ifdef NIGHTLY_BUILD
 // Enable the (fairly costly) client/server validation on nightly only. The other prefs
 // controlling validation are located in /services/sync/services-sync.js
 pref("services.sync.validation.enabled", true);
 #endif
 
 // Preferences for the form autofill system extension
 pref("browser.formautofill.experimental", false);
+pref("browser.formautofill.enabled", false);
 
 // Enable safebrowsing v4 tables (suffixed by "-proto") update.
 #ifdef NIGHTLY_BUILD
 pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,goog-malware-proto,goog-unwanted-proto,test-malware-simple,test-unwanted-simple");
 pref("urlclassifier.phishTable", "goog-phish-shavar,goog-phish-proto,test-phish-simple");
 #endif
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -26,88 +26,140 @@
  */
 
 /* exported FormAutofillParent */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ProfileStorage",
                                   "resource://formautofill/ProfileStorage.jsm");
 
 const PROFILE_JSON_FILE_NAME = "autofill-profiles.json";
+const ENABLED_PREF = "browser.formautofill.enabled";
 
-let FormAutofillParent = {
+function FormAutofillParent() {
+}
+
+FormAutofillParent.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
+
   _profileStore: null,
 
+  _enabled: false,
+
   /**
    * Initializes ProfileStorage and registers the message handler.
    */
-  init: function() {
+  init() {
     let storePath =
       OS.Path.join(OS.Constants.Path.profileDir, PROFILE_JSON_FILE_NAME);
 
     this._profileStore = new ProfileStorage(storePath);
     this._profileStore.initialize();
 
-    let mm = Cc["@mozilla.org/globalmessagemanager;1"]
-               .getService(Ci.nsIMessageListenerManager);
-    mm.addMessageListener("FormAutofill:PopulateFieldValues", this);
-    mm.addMessageListener("FormAutofill:GetProfiles", this);
+    // 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);
+  },
+
+  /**
+   * Observe the pref changes and update _enabled cache if status is changed.
+   */
+  observe() {
+    let currentStatus = this._getStatus();
+
+    if (currentStatus !== this._enabled) {
+      this._enabled = currentStatus;
+      this._onStatusChanged();
+    }
+  },
+
+  /**
+   * 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);
+    } else {
+      Services.mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
+      Services.mm.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.
+   *
+   * @returns {boolean} status of form autofill feature
+   */
+  _getStatus() {
+    return Services.prefs.getBoolPref(ENABLED_PREF);
   },
 
   /**
    * 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: function({name, data, target}) {
+  receiveMessage({name, data, target}) {
     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);
+        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.
    *
    * @returns {ProfileStorage}
    */
-  getProfileStore: function() {
+  getProfileStore() {
     return this._profileStore;
   },
 
   /**
    * Uninitializes FormAutofillParent. This is for testing only.
    *
    * @private
    */
-  _uninit: function() {
+  _uninit() {
     if (this._profileStore) {
       this._profileStore._saveImmediately();
       this._profileStore = null;
     }
 
-    let mm = Cc["@mozilla.org/globalmessagemanager;1"]
-               .getService(Ci.nsIMessageListenerManager);
-    mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
-    mm.removeMessageListener("FormAutofill:GetProfiles", this);
+    Services.mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
+    Services.mm.removeMessageListener("FormAutofill:GetProfiles", this);
   },
 
   /**
    * Populates the field values and notifies content to fill in. Exception will
    * be thrown if there's no matching profile.
    *
    * @private
    * @param  {string} data.guid
--- a/browser/extensions/formautofill/bootstrap.js
+++ b/browser/extensions/formautofill/bootstrap.js
@@ -16,15 +16,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 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;
   }
 
-  FormAutofillParent.init();
+  let parent = new FormAutofillParent();
+  parent.init();
   Services.mm.loadFrameScript("chrome://formautofill/content/FormAutofillContent.js", true);
 }
 
 function shutdown() {}
 function install() {}
 function uninstall() {}
--- a/browser/extensions/formautofill/content/FormAutofillContent.js
+++ b/browser/extensions/formautofill/content/FormAutofillContent.js
@@ -335,19 +335,26 @@ let ProfileAutocomplete = {
 
 /**
  * Handles content's interactions.
  *
  * NOTE: Declares it by "var" to make it accessible in unit tests.
  */
 var FormAutofillContent = {
   init() {
-    ProfileAutocomplete.ensureRegistered();
+    addEventListener("DOMContentLoaded", this);
 
-    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) {
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -35,16 +35,18 @@ Components.manager.addBootstrappedManife
 // 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: function() {},
+    addMessageListener: function() {},
+    sendAsyncMessage: function() {},
   };
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                .getService(Ci.mozIJSSubScriptLoader);
   loader.loadSubScriptWithOptions("chrome://formautofill/content/FormAutofillContent.js", {
     target: facGlobal,
   });
 
   return facGlobal;