Bug 1304306 - Add Form Autofill preference checkbox and button draft
authorScott Wu <scottcwwu@gmail.com>
Thu, 29 Dec 2016 15:54:50 +0800
changeset 462361 3b43d74d5ae13f17ae64423f0aaa0c38526ceeff
parent 462285 88030580b14bb253a55bc174c987a9fa43c3fb55
child 542359 c53ae66f407560a3912f088cd933ea64733aefd1
push id41715
push userbmo:scwwu@mozilla.com
push dateTue, 17 Jan 2017 05:19:00 +0000
bugs1304306
milestone53.0a1
Bug 1304306 - Add Form Autofill preference checkbox and button MozReview-Commit-ID: I7O4l9aQ5JC
browser/app/profile/firefox.js
browser/extensions/formautofill/FormAutofillParent.jsm
browser/extensions/formautofill/content/FormAutofillContent.js
browser/extensions/formautofill/locale/en-US/formautofill.properties
browser/extensions/formautofill/locale/jar.mn
browser/extensions/formautofill/locale/moz.build
browser/extensions/formautofill/moz.build
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1572,14 +1572,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
\ No newline at end of file
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -37,30 +37,35 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ProfileStorage",
                                   "resource://formautofill/ProfileStorage.jsm");
 
 const PROFILE_JSON_FILE_NAME = "autofill-profiles.json";
 
 let FormAutofillParent = {
   _profileStore: null,
+  _formAutofillPref: null,
 
   /**
    * Initializes ProfileStorage and registers the message handler.
    */
   init: function() {
     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);
+
+    let os = Cc["@mozilla.org/observer-service;1"]
+               .getService(Ci.nsIObserverService);
+    os.addObserver(this, "advanced-pane-loaded", false);
   },
 
   /**
    * 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.
@@ -69,16 +74,33 @@ let FormAutofillParent = {
     switch (name) {
       case "FormAutofill:PopulateFieldValues":
         this._populateFieldValues(data, target);
         break;
     }
   },
 
   /**
+   * nsIObserver interface methods.
+   */
+  observe: function(subject, topic, data) {
+    switch (topic) {
+      case "advanced-pane-loaded": {
+        this._formAutofillPref = new FormAutofillPref();
+        let document = subject.document;
+        let prefGroup = this._formAutofillPref.init(document);
+        let parentNode = document.getElementById("mainPrefPane");
+        let insertBeforeNode = document.getElementById("locationBarGroup");
+        parentNode.insertBefore(prefGroup, insertBeforeNode);
+        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() {
     return this._profileStore;
@@ -90,19 +112,28 @@ let FormAutofillParent = {
    * @private
    */
   _uninit: function() {
     if (this._profileStore) {
       this._profileStore._saveImmediately();
       this._profileStore = null;
     }
 
+    if (this._formAutoFillPref) {
+      this._formAutofillPref.uninit();
+      this._formAutofillPref = null;
+    }
+
     let mm = Cc["@mozilla.org/globalmessagemanager;1"]
                .getService(Ci.nsIMessageListenerManager);
     mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
+
+    let os = Cc["@mozilla.org/observer-service;1"]
+               .getService(Ci.nsIObserverService);
+    os.removeObserver(this, "advanced-pane-loaded", false);
   },
 
   /**
    * 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
@@ -165,9 +196,136 @@ let FormAutofillParent = {
       let value = this._getDataByFieldName(profile, field.fieldName);
       if (value !== undefined) {
         field.value = value;
       }
     }
   },
 };
 
+/**
+ * Creates form autofill preferences group.
+ */
+function FormAutofillPref() {
+  this.bundle = Cc["@mozilla.org/intl/stringbundle;1"]
+                  .getService(Ci.nsIStringBundleService)
+                  .createBundle("chrome://formautofill/locale/formautofill.properties");
+
+  this.prefService = Cc["@mozilla.org/preferences-service;1"]
+                       .getService(Ci.nsIPrefService)
+                       .getBranch("browser.formautofill.");
+}
+
+FormAutofillPref.prototype = {
+  /**
+   * Check if Form Autofill feature is enabled.
+   *
+   * @return {Boolean}
+   */
+  get isAutofillEnabled() {
+    return this.prefService.getBoolPref("enabled");
+  },
+
+  /**
+   * Check if the current page is Preferences/Privacy.
+   *
+   * @return {Boolean}
+   */
+  get isPrivacyPane() {
+    return this.refs.document.location.href == "about:preferences#privacy";
+  },
+
+  /**
+   * Create the Form Autofill preference group.
+   *
+   * @param  {XULDocument} document
+   * @return {XULElement}
+   */
+  init(document) {
+    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+    let formAutofillGroup = document.createElementNS(XUL_NS, "groupbox");
+    let caption = document.createElementNS(XUL_NS, "caption");
+    let captionLabel = document.createElementNS(XUL_NS, "label");
+    let hbox = document.createElementNS(XUL_NS, "hbox");
+    let checkboxEnable = document.createElementNS(XUL_NS, "checkbox");
+    let spacer = document.createElementNS(XUL_NS, "spacer");
+    let btnSavedProfiles = document.createElementNS(XUL_NS, "button");
+
+    this.refs = {
+      document,
+      formAutofillGroup,
+      checkboxEnable,
+      btnSavedProfiles
+    };
+
+    formAutofillGroup.setAttribute("id", "formAutofillGroup");
+    formAutofillGroup.setAttribute("data-category", "panePrivacy");
+    formAutofillGroup.setAttribute("hidden", !this.isPrivacyPane);
+
+    captionLabel.textContent = this.bundle.GetStringFromName("preferenceGroupTitle");
+    btnSavedProfiles.setAttribute("label", this.bundle.GetStringFromName("savedProfiles"));
+    checkboxEnable.setAttribute("label", this.bundle.GetStringFromName("enableProfileAutofill"));
+
+    // Manually set the checked state
+    if (this.isAutofillEnabled) {
+      checkboxEnable.setAttribute("checked", true);
+    }
+
+    spacer.setAttribute("flex", 1);
+
+    formAutofillGroup.appendChild(caption);
+    caption.appendChild(captionLabel);
+    formAutofillGroup.appendChild(hbox);
+    hbox.appendChild(checkboxEnable);
+    hbox.appendChild(spacer);
+    hbox.appendChild(btnSavedProfiles);
+
+    this.attachEventListeners();
+
+    return formAutofillGroup;
+  },
+
+  /**
+   * Remove event listeners and the preference group.
+   */
+  uninit() {
+    this.detachEventListeners();
+    this.refs.formAutofillGroup.remove();
+  },
+
+  /**
+   * Handle events
+   *
+   * @param  {DOMEvent} aEvent
+   */
+  handleEvent(aEvent) {
+    switch (aEvent.type) {
+      case "command": {
+        let target = aEvent.target;
+
+        if (target == this.refs.checkboxEnable) {
+          // Set preference directly instead of relying on <Preference>
+          this.prefService.setBoolPref("enabled", target.checked);
+        } else if (target == this.refs.btnSavedProfiles) {
+          // Open Saved Profiles dialog
+        }
+        break;
+      }
+    }
+  },
+
+  /**
+   * Attach event listener
+   */
+  attachEventListeners() {
+    this.refs.formAutofillGroup.addEventListener("command", this, false);
+  },
+
+  /**
+   * Remove event listener
+   */
+  detachEventListeners() {
+    this.refs.formAutofillGroup.removeEventListener("command", this, false);
+  }
+};
+
 this.EXPORTED_SYMBOLS = ["FormAutofillParent"];
--- a/browser/extensions/formautofill/content/FormAutofillContent.js
+++ b/browser/extensions/formautofill/content/FormAutofillContent.js
@@ -5,16 +5,18 @@
 /*
  * Form Autofill frame script.
  */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
 
+const PREF_ENABLED = "browser.formautofill.enabled";
+
 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"]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/locale/en-US/formautofill.properties
@@ -0,0 +1,7 @@
+# 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/.
+
+preferenceGroupTitle = Form Autofill
+enableProfileAutofill = Enable Profile Autofill
+savedProfiles = Saved Profiles...
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/locale/jar.mn
@@ -0,0 +1,8 @@
+#filter substitution
+# 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/.
+
+@AB_CD@.jar:
+% locale formautofill @AB_CD@ %locale/@AB_CD@/
+  locale/@AB_CD@/                (en-US/*)
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/locale/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+JAR_MANIFESTS += ['jar.mn']
--- a/browser/extensions/formautofill/moz.build
+++ b/browser/extensions/formautofill/moz.build
@@ -2,16 +2,18 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
 
+DIRS += ['locale']
+
 FINAL_TARGET_FILES.features['formautofill@mozilla.org'] += [
   'bootstrap.js'
 ]
 
 FINAL_TARGET_PP_FILES.features['formautofill@mozilla.org'] += [
   'install.rdf.in'
 ]