Bug 1417818 - Add CA/DE metadata from libaddressinput. r=scottwu, lchang draft
authorsteveck-chung <schung@mozilla.com>
Mon, 20 Nov 2017 18:07:50 +0800
changeset 705544 8350b485fb5995ca9dad659d615fa0683bc26850
parent 705442 38f49346a200cc25492236c7b3c536fc835fe031
child 742392 743cd103f8dce9554fd06b516e97f968fd46a9d2
push id91509
push userbmo:schung@mozilla.com
push dateThu, 30 Nov 2017 09:56:38 +0000
reviewersscottwu, lchang
bugs1417818
milestone59.0a1
Bug 1417818 - Add CA/DE metadata from libaddressinput. r=scottwu, lchang MozReview-Commit-ID: 56eHfN8DaUg
browser/app/profile/firefox.js
browser/extensions/formautofill/FormAutofillHandler.jsm
browser/extensions/formautofill/FormAutofillUtils.jsm
browser/extensions/formautofill/ProfileStorage.jsm
browser/extensions/formautofill/addressmetadata/addressReferences.js
browser/extensions/formautofill/test/unit/test_addressDataLoader.js
browser/extensions/formautofill/test/unit/test_transformFields.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1716,17 +1716,17 @@ pref("extensions.formautofill.creditCard
 // 2: saw the doorhanger
 // 3: submitted an autofill'ed credit card form
 pref("extensions.formautofill.creditCards.used", 0);
 pref("extensions.formautofill.firstTimeUse", true);
 pref("extensions.formautofill.heuristics.enabled", true);
 pref("extensions.formautofill.section.enabled", true);
 pref("extensions.formautofill.loglevel", "Warn");
 // Comma separated list of countries Form Autofill supports
-pref("extensions.formautofill.supportedCountries", "US");
+pref("extensions.formautofill.supportedCountries", "US,CA,DE");
 
 // Whether or not to restore a session with lazy-browser tabs.
 pref("browser.sessionstore.restore_tabs_lazily", true);
 
 pref("browser.suppress_first_window_animation", true);
 
 // Preferences for Photon onboarding system extension
 pref("browser.onboarding.enabled", true);
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -637,17 +637,17 @@ class FormAutofillSection {
           // assumed to be default along with a meaningless text to users.
           if (!value || element.selectedOptions.length != 1) {
             // Keep the property and preserve more information for address updating
             data[type].record[detail.fieldName] = "";
             return;
           }
 
           let text = element.selectedOptions[0].text.trim();
-          value = FormAutofillUtils.getAbbreviatedStateName([value, text]) || text;
+          value = FormAutofillUtils.getAbbreviatedSubregionName([value, text]) || text;
         }
 
         if (!value || value.length > FormAutofillUtils.MAX_FIELD_VALUE_LENGTH) {
           // Keep the property and preserve more information for updating
           data[type].record[detail.fieldName] = "";
           return;
         }
 
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillUtils.jsm
@@ -282,25 +282,41 @@ this.FormAutofillUtils = {
     return true;
   },
 
   loadDataFromScript(url, sandbox = {}) {
     Services.scriptloader.loadSubScript(url, sandbox, "utf-8");
     return sandbox;
   },
 
-  // Get country address data and fallback to US if not found.
-  // See AddressDataLoader.getData for more details of addressData structure.
-  getCountryAddressData(country, level1 = null) {
+  /**
+   * Get country address data and fallback to US if not found.
+   * See AddressDataLoader.getData for more details of addressData structure.
+   * @param {string} [country=FormAutofillUtils.DEFAULT_REGION]
+   *        The country code for requesting specific country's metadata. It'll be
+   *        default region if parameter is not set.
+   * @param {string} [level1=null]
+   *        Retrun address level 1/level 2 metadata if parameter is set.
+   * @returns {object}
+   *          Return the metadata of specific region.
+   */
+  getCountryAddressData(country = FormAutofillUtils.DEFAULT_REGION, level1 = null) {
     let metadata = AddressDataLoader.getData(country, level1);
     if (!metadata) {
-      metadata = level1 ? null : AddressDataLoader.getData("US");
+      if (level1) {
+        return null;
+      }
+      // Fallback to default region if we couldn't get data from given country.
+      if (country != FormAutofillUtils.DEFAULT_REGION) {
+        metadata = AddressDataLoader.getData(FormAutofillUtils.DEFAULT_REGION);
+      }
     }
 
-    return metadata;
+    // Fallback to US if we couldn't get data from default region.
+    return metadata || AddressDataLoader.getData("US");
   },
 
   /**
    * Get the collators based on the specified country.
    * @param   {string} country The specified country.
    * @returns {array} An array containing several collator objects.
    */
   getCollators(country) {
@@ -408,23 +424,23 @@ this.FormAutofillUtils = {
     }
     if (this.isCreditCardField(fieldName)) {
       return this.findCreditCardSelectOption(selectEl, record, fieldName);
     }
     return null;
   },
 
   /**
-   * Try to find the abbreviation of the given state name
-   * @param   {string[]} stateValues A list of inferable state values.
-   * @param   {string} country A country name to be identified.
-   * @returns {string} The matching state abbreviation.
+   * Try to find the abbreviation of the given sub-region name
+   * @param   {string[]} subregionValues A list of inferable sub-region values.
+   * @param   {string} [country] A country name to be identified.
+   * @returns {string} The matching sub-region abbreviation.
    */
-  getAbbreviatedStateName(stateValues, country = this.DEFAULT_COUNTRY_CODE) {
-    let values = Array.isArray(stateValues) ? stateValues : [stateValues];
+  getAbbreviatedSubregionName(subregionValues, country) {
+    let values = Array.isArray(subregionValues) ? subregionValues : [subregionValues];
 
     let collators = this.getCollators(country);
     let {sub_keys: subKeys, sub_names: subNames} = this.getCountryAddressData(country);
 
     if (!Array.isArray(subKeys)) {
       subKeys = subKeys.split("~");
     }
     if (!Array.isArray(subNames)) {
@@ -463,19 +479,18 @@ this.FormAutofillUtils = {
    * @returns {DOMElement}
    */
   findAddressSelectOption(selectEl, address, fieldName) {
     let value = address[fieldName];
     if (!value) {
       return null;
     }
 
-    let country = address.country || this.DEFAULT_COUNTRY_CODE;
-    let dataset = this.getCountryAddressData(country);
-    let collators = this.getCollators(country);
+    let dataset = this.getCountryAddressData(address.country);
+    let collators = this.getCollators(address.country);
 
     for (let option of selectEl.options) {
       if (this.strCompare(value, option.value, collators) ||
           this.strCompare(value, option.text, collators)) {
         return option;
       }
     }
 
@@ -662,18 +677,18 @@ this.FormAutofillUtils = {
     let elements = root.querySelectorAll("[data-localization]");
     for (let element of elements) {
       element.textContent = bundle.GetStringFromName(element.getAttribute("data-localization"));
       element.removeAttribute("data-localization");
     }
   },
 };
 
-XPCOMUtils.defineLazyGetter(this.FormAutofillUtils, "DEFAULT_COUNTRY_CODE", () => {
-  return Services.prefs.getCharPref("browser.search.countryCode", "US");
+XPCOMUtils.defineLazyGetter(this.FormAutofillUtils, "DEFAULT_REGION", () => {
+  return Services.prefs.getCharPref("browser.search.region", "US");
 });
 
 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");
 });
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -1272,17 +1272,17 @@ class Addresses extends AutofillRecords 
         address["country-name"] = "";
       }
       hasNewComputedFields = true;
     }
 
     // Compute tel
     if (!("tel-national" in address)) {
       if (address.tel) {
-        let tel = PhoneNumber.Parse(address.tel, address.country || FormAutofillUtils.DEFAULT_COUNTRY_CODE);
+        let tel = PhoneNumber.Parse(address.tel, address.country || FormAutofillUtils.DEFAULT_REGION);
         if (tel) {
           if (tel.countryCode) {
             address["tel-country-code"] = tel.countryCode;
           }
           if (tel.nationalNumber) {
             address["tel-national"] = tel.nationalNumber;
           }
 
@@ -1372,17 +1372,17 @@ class Addresses extends AutofillRecords 
 
     delete address["country-name"];
   }
 
   _normalizeTel(address) {
     if (address.tel || TEL_COMPONENTS.some(c => !!address[c])) {
       FormAutofillUtils.compressTel(address);
 
-      let possibleRegion = address.country || FormAutofillUtils.DEFAULT_COUNTRY_CODE;
+      let possibleRegion = address.country || FormAutofillUtils.DEFAULT_REGION;
       let tel = PhoneNumber.Parse(address.tel, possibleRegion);
 
       if (tel && tel.internationalNumber) {
         // Force to save numbers in E.164 format if parse success.
         address.tel = tel.internationalNumber;
       }
     }
     TEL_COMPONENTS.forEach(c => delete address[c]);
--- a/browser/extensions/formautofill/addressmetadata/addressReferences.js
+++ b/browser/extensions/formautofill/addressmetadata/addressReferences.js
@@ -11,10 +11,13 @@
 // https://chromium-i18n.appspot.com/ssl-aggregate-address
 
 // WARNING: DO NOT change any value or add additional properties in addressData.
 // We only accept the metadata of the supported countries that is copied from libaddressinput directly.
 // Please edit addressReferencesExt.js instead if you want to add new property as complement
 // or overwrite the existing properties.
 
 var addressData = {
+  "data/CA": {"lang": "en", "upper": "ACNOSZ", "zipex": "H3Z 2Y7,V8X 3X4,T0L 1K0,T0H 1A0,K1A 0B1", "name": "CANADA", "zip": "[ABCEGHJKLMNPRSTVXY]\\d[ABCEGHJ-NPRSTV-Z] ?\\d[ABCEGHJ-NPRSTV-Z]\\d", "fmt": "%N%n%O%n%A%n%C %S %Z", "id": "data/CA", "languages": "en~fr", "sub_keys": "AB~BC~MB~NB~NL~NT~NS~NU~ON~PE~QC~SK~YT", "key": "CA", "posturl": "https://www.canadapost.ca/cpo/mc/personal/postalcode/fpc.jsf", "require": "ACSZ", "sub_names": "Alberta~British Columbia~Manitoba~New Brunswick~Newfoundland and Labrador~Northwest Territories~Nova Scotia~Nunavut~Ontario~Prince Edward Island~Quebec~Saskatchewan~Yukon", "sub_zips": "T~V~R~E~A~X0E|X0G|X1A~B~X0A|X0B|X0C~K|L|M|N|P~C~G|H|J|K1A~S|R8A~Y"},
+  "data/CA--fr": {"lang": "fr", "upper": "ACNOSZ", "zipex": "H3Z 2Y7,V8X 3X4,T0L 1K0,T0H 1A0,K1A 0B1", "name": "CANADA", "zip": "[ABCEGHJKLMNPRSTVXY]\\d[ABCEGHJ-NPRSTV-Z] ?\\d[ABCEGHJ-NPRSTV-Z]\\d", "fmt": "%N%n%O%n%A%n%C %S %Z", "require": "ACSZ", "sub_keys": "AB~BC~PE~MB~NB~NS~NU~ON~QC~SK~NL~NT~YT", "key": "CA", "posturl": "https://www.canadapost.ca/cpo/mc/personal/postalcode/fpc.jsf", "id": "data/CA--fr", "sub_names": "Alberta~Colombie-Britannique~Île-du-Prince-Édouard~Manitoba~Nouveau-Brunswick~Nouvelle-Écosse~Nunavut~Ontario~Québec~Saskatchewan~Terre-Neuve-et-Labrador~Territoires du Nord-Ouest~Yukon", "sub_zips": "T~V~C~R~E~B~X0A|X0B|X0C~K|L|M|N|P~G|H|J|K1A~S|R8A~A~X0E|X0G|X1A~Y"},
+  "data/DE": {"zipex": "26133,53225", "name": "GERMANY", "zip": "\\d{5}", "fmt": "%N%n%O%n%A%n%Z %C", "id": "data/DE", "key": "DE", "posturl": "http://www.postdirekt.de/plzserver/", "require": "ACZ"},
   "data/US": {"lang": "en", "upper": "CS", "sub_zipexs": "35000,36999~99500,99999~96799~85000,86999~71600,72999~34000,34099~09000,09999~96200,96699~90000,96199~80000,81999~06000,06999~19700,19999~20000,56999~32000,34999~30000,39901~96910,96932~96700,96899~83200,83999~60000,62999~46000,47999~50000,52999~66000,67999~40000,42799~70000,71599~03900,04999~96960,96979~20600,21999~01000,05544~48000,49999~96941,96944~55000,56799~38600,39799~63000,65999~59000,59999~68000,69999~88900,89999~03000,03899~07000,08999~87000,88499~10000,00544~27000,28999~58000,58999~96950,96952~43000,45999~73000,74999~97000,97999~96940~15000,19699~00600,00999~02800,02999~29000,29999~57000,57999~37000,38599~75000,73344~84000,84999~05000,05999~00800,00899~20100,24699~98000,99499~24700,26999~53000,54999~82000,83414", "zipex": "95014,22162-1010", "name": "UNITED STATES", "zip": "(\\d{5})(?:[ \\-](\\d{4}))?", "zip_name_type": "zip", "fmt": "%N%n%O%n%A%n%C, %S %Z", "state_name_type": "state", "id": "data/US", "languages": "en", "sub_keys": "AL~AK~AS~AZ~AR~AA~AE~AP~CA~CO~CT~DE~DC~FL~GA~GU~HI~ID~IL~IN~IA~KS~KY~LA~ME~MH~MD~MA~MI~FM~MN~MS~MO~MT~NE~NV~NH~NJ~NM~NY~NC~ND~MP~OH~OK~OR~PW~PA~PR~RI~SC~SD~TN~TX~UT~VT~VI~VA~WA~WV~WI~WY", "key": "US", "posturl": "https://tools.usps.com/go/ZipLookupAction!input.action", "require": "ACSZ", "sub_names": "Alabama~Alaska~American Samoa~Arizona~Arkansas~Armed Forces (AA)~Armed Forces (AE)~Armed Forces (AP)~California~Colorado~Connecticut~Delaware~District of Columbia~Florida~Georgia~Guam~Hawaii~Idaho~Illinois~Indiana~Iowa~Kansas~Kentucky~Louisiana~Maine~Marshall Islands~Maryland~Massachusetts~Michigan~Micronesia~Minnesota~Mississippi~Missouri~Montana~Nebraska~Nevada~New Hampshire~New Jersey~New Mexico~New York~North Carolina~North Dakota~Northern Mariana Islands~Ohio~Oklahoma~Oregon~Palau~Pennsylvania~Puerto Rico~Rhode Island~South Carolina~South Dakota~Tennessee~Texas~Utah~Vermont~Virgin Islands~Virginia~Washington~West Virginia~Wisconsin~Wyoming", "sub_zips": "3[56]~99[5-9]~96799~8[56]~71[6-9]|72~340~09~96[2-6]~9[0-5]|96[01]~8[01]~06~19[7-9]~20[02-5]|569~3[23]|34[1-9]~3[01]|398|39901~969([1-2]\\d|3[12])~967[0-8]|9679[0-8]|968~83[2-9]~6[0-2]~4[67]~5[0-2]~6[67]~4[01]|42[0-7]~70|71[0-5]~039|04~969[67]~20[6-9]|21~01|02[0-7]|05501|05544~4[89]~9694[1-4]~55|56[0-7]~38[6-9]|39[0-7]~6[3-5]~59~6[89]~889|89~03[0-8]~0[78]~87|88[0-4]~1[0-4]|06390|00501|00544~2[78]~58~9695[0-2]~4[3-5]~7[34]~97~969(39|40)~1[5-8]|19[0-6]~00[679]~02[89]~29~57~37|38[0-5]~7[5-9]|885|73301|73344~84~05~008~201|2[23]|24[0-6]~98|99[0-4]~24[7-9]|2[56]~5[34]~82|83[01]|83414"},
 };
--- a/browser/extensions/formautofill/test/unit/test_addressDataLoader.js
+++ b/browser/extensions/formautofill/test/unit/test_addressDataLoader.js
@@ -1,12 +1,27 @@
 "use strict";
 
 Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
+const SUPPORT_COUNTRIES_TESTCASES = [
+  {
+    country: "US",
+    properties: ["languages", "alternative_names", "sub_keys", "sub_names"],
+  },
+  {
+    country: "CA",
+    properties: ["languages", "name", "sub_keys", "sub_names"],
+  },
+  {
+    country: "DE",
+    properties: ["name"],
+  },
+];
+
 add_task(async function test_initalState() {
   // addressData should not exist
   Assert.equal(AddressDataLoader._addressData, undefined);
   // Verify _dataLoaded state
   Assert.equal(AddressDataLoader._dataLoaded.country, false);
   Assert.equal(AddressDataLoader._dataLoaded.level1.size, 0);
 });
 
@@ -42,8 +57,17 @@ add_task(async function test_loadDataSta
   AddressDataLoader._loadScripts.reset();
 
   // Load level 1 data again
   undefinedMetadata = FormAutofillUtils.getCountryAddressData("US", "AS");
   Assert.equal(undefinedMetadata, undefined, "metadata should be undefined");
   // _loadScripts should not be called
   sinon.assert.notCalled(AddressDataLoader._loadScripts);
 });
+
+SUPPORT_COUNTRIES_TESTCASES.forEach(testcase => {
+  add_task(async function test_support_country() {
+    do_print("Starting testcase: Check " + testcase.country + " metadata");
+    let metadata = FormAutofillUtils.getCountryAddressData(testcase.country);
+    Assert.ok(testcase.properties.every(key => metadata[key]),
+              "These properties should exist: " + testcase.properties);
+  });
+});
--- a/browser/extensions/formautofill/test/unit/test_transformFields.js
+++ b/browser/extensions/formautofill/test/unit/test_transformFields.js
@@ -406,17 +406,17 @@ const ADDRESS_NORMALIZE_TESTCASES = [
       "country": undefined,
       "country-name": undefined,
     },
   },
   {
     description: "Has unsupported \"country\"",
     address: {
       "given-name": "John", // Make sure it won't be an empty record.
-      "country": "CA",
+      "country": "TV",
     },
     expectedResult: {
       "country": undefined,
       "country-name": undefined,
     },
   },
 
   // Tel