Bug 1338420 - Part 1: Add initialProcessData autofillSavedFieldNames set and broadcast the changes to content. r?MattN draft
authorsteveck-chung <schung@mozilla.com>
Thu, 23 Feb 2017 17:09:14 +0800
changeset 498990 a8b6a9839b2fad84d3a6282d06ec10a52d5d55cf
parent 497875 6d38ad302429c98115c354d643e81987ecec5d3c
child 498991 ccfa7c934cce69353512126ddcc281e71a72c39e
push id49304
push userbmo:schung@mozilla.com
push dateWed, 15 Mar 2017 09:03:48 +0000
reviewersMattN
bugs1338420
milestone55.0a1
Bug 1338420 - Part 1: Add initialProcessData autofillSavedFieldNames set and broadcast the changes to content. r?MattN MozReview-Commit-ID: Dy2xxNQlJig
browser/extensions/formautofill/FormAutofillContent.jsm
browser/extensions/formautofill/FormAutofillParent.jsm
browser/extensions/formautofill/ProfileStorage.jsm
browser/extensions/formautofill/test/unit/test_enabledStatus.js
browser/extensions/formautofill/test/unit/test_savedFieldNames.js
browser/extensions/formautofill/test/unit/xpcshell.ini
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -245,30 +245,49 @@ let ProfileAutocomplete = {
  * NOTE: Declares it by "var" to make it accessible in unit tests.
  */
 var FormAutofillContent = {
   /**
    * @type {WeakMap} mapping FormLike root HTML elements to FormAutofillHandler objects.
    */
   _formsDetails: new WeakMap(),
 
+  /**
+   * @type {Set} Set of the fields with usable values in any saved profile.
+   */
+  savedFieldNames: null,
+
   init() {
     FormAutofillUtils.defineLazyLogGetter(this, "FormAutofillContent");
 
-    Services.cpmm.addMessageListener("FormAutofill:enabledStatus", (result) => {
-      if (result.data) {
-        ProfileAutocomplete.ensureRegistered();
-      } else {
-        ProfileAutocomplete.ensureUnregistered();
-      }
-    });
+    Services.cpmm.addMessageListener("FormAutofill:enabledStatus", this);
+    Services.cpmm.addMessageListener("FormAutofill:savedFieldNames", this);
 
     if (Services.cpmm.initialProcessData.autofillEnabled) {
       ProfileAutocomplete.ensureRegistered();
     }
+
+    this.savedFieldNames =
+      Services.cpmm.initialProcessData.autofillSavedFieldNames || new Set();
+  },
+
+  receiveMessage({name, data}) {
+    switch (name) {
+      case "FormAutofill:enabledStatus": {
+        if (data) {
+          ProfileAutocomplete.ensureRegistered();
+        } else {
+          ProfileAutocomplete.ensureUnregistered();
+        }
+        break;
+      }
+      case "FormAutofill:savedFieldNames": {
+        this.savedFieldNames = data;
+      }
+    }
   },
 
   /**
    * 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
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -80,16 +80,17 @@ FormAutofillParent.prototype = {
 
     // Observing the pref and storage changes
     Services.prefs.addObserver(ENABLED_PREF, this, false);
     Services.obs.addObserver(this, "formautofill-storage-changed", false);
 
     // Force to trigger the onStatusChanged function for setting listeners properly
     // while initizlization
     this._setStatus(this._getStatus());
+    this._updateSavedFieldNames();
   },
 
   observe(subject, topic, data) {
     log.debug("observe:", topic, "with data:", data);
     switch (topic) {
       case "advanced-pane-loaded": {
         let formAutofillPreferences = new FormAutofillPreferences();
         let document = subject.document;
@@ -110,16 +111,17 @@ FormAutofillParent.prototype = {
       }
 
       case "formautofill-storage-changed": {
         // Early exit if the action is not "add" nor "remove"
         if (data != "add" && data != "remove") {
           break;
         }
 
+        this._updateSavedFieldNames();
         let currentStatus = this._getStatus();
         if (currentStatus !== this._enabled) {
           this._setStatus(currentStatus);
         }
         break;
       }
 
       default: {
@@ -239,9 +241,34 @@ FormAutofillParent.prototype = {
     if (info && info.fieldName) {
       profiles = this._profileStore.getByFilter({searchString, info});
     } else {
       profiles = this._profileStore.getAll();
     }
 
     target.sendAsyncMessage("FormAutofill:Profiles", profiles);
   },
+
+  _updateSavedFieldNames() {
+    if (!Services.ppmm.initialProcessData.autofillSavedFieldNames) {
+      Services.ppmm.initialProcessData.autofillSavedFieldNames = new Set();
+    } else {
+      Services.ppmm.initialProcessData.autofillSavedFieldNames.clear();
+    }
+
+    this._profileStore.getAll().forEach((profile) => {
+      Object.keys(profile).forEach((fieldName) => {
+        if (!profile[fieldName]) {
+          return;
+        }
+        Services.ppmm.initialProcessData.autofillSavedFieldNames.add(fieldName);
+      });
+    });
+
+    // Remove the internal guid and metadata fields.
+    this._profileStore.INTERNAL_FIELDS.forEach((fieldName) => {
+      Services.ppmm.initialProcessData.autofillSavedFieldNames.delete(fieldName);
+    });
+
+    Services.ppmm.broadcastAsyncMessage("FormAutofill:savedFieldNames",
+                                        Services.ppmm.initialProcessData.autofillSavedFieldNames);
+  },
 };
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -87,16 +87,19 @@ const MOCK_STORAGE = [{
   tel: "1-650-903-0800",
 }];
 
 function ProfileStorage(path) {
   this._path = path;
 }
 
 ProfileStorage.prototype = {
+  // These fields are defined internally for each profile.
+  INTERNAL_FIELDS:
+    ["guid", "timeCreated", "timeLastUsed", "timeLastModified", "timesUsed"],
   /**
    * Loads the profile data from file to memory.
    *
    * @returns {Promise}
    * @resolves When the operation finished successfully.
    * @rejects  JavaScript exception.
    */
   initialize() {
--- a/browser/extensions/formautofill/test/unit/test_enabledStatus.js
+++ b/browser/extensions/formautofill/test/unit/test_enabledStatus.js
@@ -20,16 +20,17 @@ add_task(function* test_enabledStatus_in
 
   formAutofillParent._uninit();
 });
 
 add_task(function* test_enabledStatus_observe() {
   let formAutofillParent = new FormAutofillParent();
   sinon.stub(formAutofillParent, "_getStatus");
   sinon.spy(formAutofillParent, "_setStatus");
+  sinon.stub(formAutofillParent, "_updateSavedFieldNames");
 
   // _enabled = _getStatus() => No need to trigger onStatusChanged
   formAutofillParent._enabled = true;
   formAutofillParent._getStatus.returns(true);
   formAutofillParent.observe(null, "nsPref:changed", "browser.formautofill.enabled");
   do_check_eq(formAutofillParent._setStatus.called, false);
 
   // _enabled != _getStatus() => Need to trigger onStatusChanged
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
@@ -0,0 +1,89 @@
+/*
+ * Test for keeping the valid fields information in initialProcessData.
+ */
+
+"use strict";
+
+Cu.import("resource://formautofill/FormAutofillParent.jsm");
+Cu.import("resource://formautofill/ProfileStorage.jsm");
+
+add_task(function* test_profileSavedFieldNames_init() {
+  let formAutofillParent = new FormAutofillParent();
+  sinon.stub(formAutofillParent, "_updateSavedFieldNames");
+
+  formAutofillParent.init();
+  do_check_eq(formAutofillParent._updateSavedFieldNames.called, true);
+
+  formAutofillParent._uninit();
+});
+
+add_task(function* test_profileSavedFieldNames_observe() {
+  let formAutofillParent = new FormAutofillParent();
+  sinon.stub(formAutofillParent, "_updateSavedFieldNames");
+
+  // profile added => Need to trigger updateValidFields
+  formAutofillParent.observe(null, "formautofill-storage-changed", "add");
+  do_check_eq(formAutofillParent._updateSavedFieldNames.called, true);
+
+  // profile removed => Need to trigger updateValidFields
+  formAutofillParent._updateSavedFieldNames.reset();
+  formAutofillParent.observe(null, "formautofill-storage-changed", "remove");
+  do_check_eq(formAutofillParent._updateSavedFieldNames.called, true);
+
+  // profile updated => no need to trigger updateValidFields
+  formAutofillParent._updateSavedFieldNames.reset();
+  formAutofillParent.observe(null, "formautofill-storage-changed", "update");
+  do_check_eq(formAutofillParent._updateSavedFieldNames.called, false);
+});
+
+add_task(function* test_profileSavedFieldNames_update() {
+  let formAutofillParent = new FormAutofillParent();
+  formAutofillParent.init();
+  do_register_cleanup(function cleanup() {
+    Services.prefs.clearUserPref("browser.formautofill.enabled");
+  });
+
+  sinon.stub(formAutofillParent._profileStore, "getAll");
+  formAutofillParent._profileStore.getAll.returns([]);
+
+  // The set is empty if there's no profile in the store.
+  formAutofillParent._updateSavedFieldNames();
+  do_check_eq(Services.ppmm.initialProcessData.autofillSavedFieldNames.size, 0);
+
+  // 2 profiles with 4 valid fields.
+  let fakeStorage = [{
+    guid: "test-guid-1",
+    organization: "Sesame Street",
+    "street-address": "123 Sesame Street.",
+    tel: "1-345-345-3456",
+    email: "",
+    timeCreated: 0,
+    timeLastUsed: 0,
+    timeLastModified: 0,
+    timesUsed: 0,
+  }, {
+    guid: "test-guid-2",
+    organization: "Mozilla",
+    "street-address": "331 E. Evelyn Avenue",
+    tel: "1-650-903-0800",
+    country: "US",
+    timeCreated: 0,
+    timeLastUsed: 0,
+    timeLastModified: 0,
+    timesUsed: 0,
+  }];
+  formAutofillParent._profileStore.getAll.returns(fakeStorage);
+  formAutofillParent._updateSavedFieldNames();
+
+  let autofillSavedFieldNames = Services.ppmm.initialProcessData.autofillSavedFieldNames;
+  do_check_eq(autofillSavedFieldNames.size, 4);
+  do_check_eq(autofillSavedFieldNames.has("organization"), true);
+  do_check_eq(autofillSavedFieldNames.has("street-address"), true);
+  do_check_eq(autofillSavedFieldNames.has("tel"), true);
+  do_check_eq(autofillSavedFieldNames.has("email"), false);
+  do_check_eq(autofillSavedFieldNames.has("guid"), false);
+  do_check_eq(autofillSavedFieldNames.has("timeCreated"), false);
+  do_check_eq(autofillSavedFieldNames.has("timeLastUsed"), false);
+  do_check_eq(autofillSavedFieldNames.has("timeLastModified"), false);
+  do_check_eq(autofillSavedFieldNames.has("timesUsed"), false);
+});
--- a/browser/extensions/formautofill/test/unit/xpcshell.ini
+++ b/browser/extensions/formautofill/test/unit/xpcshell.ini
@@ -5,8 +5,10 @@ support-files =
 
 [test_autofillFormFields.js]
 [test_collectFormFields.js]
 [test_enabledStatus.js]
 [test_getFormInputDetails.js]
 [test_markAsAutofillField.js]
 [test_profileAutocompleteResult.js]
 [test_profileStorage.js]
+[test_savedFieldNames.js]
+