Bug 1022925 - Part 2: Move alternative name to extension file. r=scottwu, lchang draft
authorsteveck-chung <schung@mozilla.com>
Fri, 10 Nov 2017 18:36:22 +0800
changeset 701735 61e86f1ffe697e6dfcb7c0637e7f2b40e74172c9
parent 701734 323b9ebeee4b61c0f34cdc1a35584ffca7115cbc
child 702403 e9023b4edf9304d41ab54620752c3c93324d8222
push id90256
push userbmo:schung@mozilla.com
push dateWed, 22 Nov 2017 06:32:16 +0000
reviewersscottwu, lchang
bugs1022925
milestone59.0a1
Bug 1022925 - Part 2: Move alternative name to extension file. r=scottwu, lchang MozReview-Commit-ID: 4rFagXU5iit
browser/extensions/formautofill/FormAutofillUtils.jsm
browser/extensions/formautofill/addressmetadata/addressReferences.js
browser/extensions/formautofill/addressmetadata/addressReferencesExt.js
browser/extensions/formautofill/test/unit/test_addressDataLoader.js
browser/extensions/formautofill/test/unit/xpcshell.ini
tools/lint/eslint/modules.json
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillUtils.jsm
@@ -1,25 +1,24 @@
 /* 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/. */
 
 "use strict";
 
-this.EXPORTED_SYMBOLS = ["FormAutofillUtils"];
+this.EXPORTED_SYMBOLS = ["FormAutofillUtils", "AddressDataLoader"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
-const ADDRESS_REFERENCES = "resource://formautofill/addressmetadata/addressReferences.js";
+const ADDRESS_METADATA_PATH = "resource://formautofill/addressmetadata/";
+const ADDRESS_REFERENCES = "addressReferences.js";
+const ADDRESS_REFERENCES_EXT = "addressReferencesExt.js";
 
-// TODO: We only support US in MVP. We are going to support more countries in
-//       bug 1370193.
-const ALTERNATIVE_COUNTRY_NAMES = {
-  "US": ["US", "United States of America", "United States", "America", "U.S.", "USA", "U.S.A.", "U.S.A"],
-};
+// TODO: This list should become a pref in Bug 1413494
+const SUPPORTED_COUNTRY_LIST = ["US"];
 
 const ADDRESSES_COLLECTION_NAME = "addresses";
 const CREDITCARDS_COLLECTION_NAME = "creditCards";
 const ADDRESSES_FIRST_TIME_USE_PREF = "extensions.formautofill.firstTimeUse";
 const ENABLED_AUTOFILL_ADDRESSES_PREF = "extensions.formautofill.addresses.enabled";
 const CREDITCARDS_USED_STATUS_PREF = "extensions.formautofill.creditCards.used";
 const AUTOFILL_CREDITCARDS_AVAILABLE_PREF = "extensions.formautofill.creditCards.available";
 const ENABLED_AUTOFILL_CREDITCARDS_PREF = "extensions.formautofill.creditCards.enabled";
@@ -33,16 +32,87 @@ const EDIT_CREDITCARD_KEYWORDS = ["cardN
 
 // The maximum length of data to be saved in a single field for preventing DoS
 // attacks that fill the user's hard drive(s).
 const MAX_FIELD_VALUE_LENGTH = 200;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+let AddressDataLoader = {
+  // Status of address data loading. We'll load all the countries with basic level 1
+  // information while requesting conutry information, and set country to true.
+  // Level 1 Set is for recording which country's level 1/level 2 data is loaded,
+  // since we only load this when getCountryAddressData called with level 1 parameter.
+  _dataLoaded: {
+    country: false,
+    level1: new Set(),
+  },
+  /**
+   * Load address data and extension script into a sandbox from different paths.
+   * @param   {string} path
+   *          The path for address data and extension script. It could be root of the address
+   *          metadata folder(addressmetadata/) or under specific country(addressmetadata/TW/).
+   * @returns {object}
+   *          A sandbox that contains address data object with properties from extension.
+   */
+  _loadScripts(path) {
+    let sandbox = {};
+    let extSandbox = {};
+
+    try {
+      sandbox = FormAutofillUtils.loadDataFromScript(path + ADDRESS_REFERENCES);
+      extSandbox = FormAutofillUtils.loadDataFromScript(path + ADDRESS_REFERENCES_EXT);
+    } catch (e) {
+      // Will return only address references if extension loading failed or empty sandbox if
+      // address references loading failed.
+      return sandbox;
+    }
+
+    if (extSandbox.addressDataExt) {
+      for (let key in extSandbox.addressDataExt) {
+        Object.assign(sandbox.addressData[key], extSandbox.addressDataExt[key]);
+      }
+    }
+    return sandbox;
+  },
+  /**
+   * We'll cache addressData in the loader once the data loaded from scripts.
+   * It'll become the example below after loading addressReferences with extension:
+   * addressData: {
+                   "data/US": {"lang": "en", ...// Data defined in libaddressinput metadata
+   *                           "alternative_names": ... // Data defined in extension }
+   *               "data/CA": {} // Other supported country metadata
+   *               "data/TW": {} // Other supported country metadata
+   *               "data/TW/台北市": {} // Other supported country level 1 metadata
+   *              }
+   * @param   {string} country
+   * @param   {string} level1
+   * @returns {object}
+   */
+  getData(country, level1 = null) {
+    // Load the addressData if needed
+    if (!this._dataLoaded.country) {
+      this._addressData = this._loadScripts(ADDRESS_METADATA_PATH).addressData;
+      this._dataLoaded.country = true;
+    }
+    if (!level1) {
+      return this._addressData[`data/${country}`];
+    }
+    // If level1 is set, load addressReferences under country folder with specific
+    // country/level 1 for level 2 information.
+    if (!this._dataLoaded.level1.has(country)) {
+      Object.assign(this._addressData,
+                    this._loadScripts(`${ADDRESS_METADATA_PATH}${country}/`).addressData);
+      this._dataLoaded.level1.add(country);
+    }
+    return this._addressData[`data/${country}/${level1}`];
+  },
+};
+
 this.FormAutofillUtils = {
   get AUTOFILL_FIELDS_THRESHOLD() { return 3; },
   get isAutofillEnabled() { return this.isAutofillAddressesEnabled || this.isAutofillCreditCardsEnabled; },
   get isAutofillCreditCardsEnabled() { return this.isAutofillCreditCardsAvailable && this._isAutofillCreditCardsEnabled; },
 
   ADDRESSES_COLLECTION_NAME,
   CREDITCARDS_COLLECTION_NAME,
   ENABLED_AUTOFILL_ADDRESSES_PREF,
@@ -83,17 +153,17 @@ this.FormAutofillUtils = {
     "cc-given-name": "creditCard",
     "cc-additional-name": "creditCard",
     "cc-family-name": "creditCard",
     "cc-number": "creditCard",
     "cc-exp-month": "creditCard",
     "cc-exp-year": "creditCard",
     "cc-exp": "creditCard",
   },
-  _addressDataLoaded: false,
+
   _collators: {},
   _reAlternativeCountryNames: {},
 
   isAddressField(fieldName) {
     return !!this._fieldNameInfo[fieldName] && !this.isCreditCardField(fieldName);
   },
 
   isCreditCardField(fieldName) {
@@ -204,28 +274,25 @@ this.FormAutofillUtils = {
 
   loadDataFromScript(url, sandbox = {}) {
     let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                          .getService(Ci.mozIJSSubScriptLoader);
     scriptLoader.loadSubScript(url, sandbox, "utf-8");
     return sandbox;
   },
 
-  /**
-   * Get country address data. Fallback to US if not found.
-   * @param   {string} country
-   * @returns {object}
-   */
-  getCountryAddressData(country) {
-    // Load the addressData if needed
-    if (!this._addressDataLoaded) {
-      Object.assign(this, this.loadDataFromScript(ADDRESS_REFERENCES));
-      this._addressDataLoaded = true;
+  // Get country address data and fallback to US if not found.
+  // See AddressDataLoader.getData for more details of addressData structure.
+  getCountryAddressData(country, level1 = null) {
+    let metadata = AddressDataLoader.getData(country, level1);
+    if (!metadata) {
+      metadata = level1 ? null : AddressDataLoader.getData("US");
     }
-    return this.addressData[`data/${country}`] || this.addressData["data/US"];
+
+    return metadata;
   },
 
   /**
    * Get the collators based on the specified country.
    * @param   {string} country The specified country.
    * @returns {array} An array containing several collator objects.
    */
   getCollators(country) {
@@ -294,22 +361,23 @@ this.FormAutofillUtils = {
    * Use alternative country name list to identify a country code from a
    * specified country name.
    * @param   {string} countryName A country name to be identified
    * @param   {string} [countrySpecified] A country code indicating that we only
    *                                      search its alternative names if specified.
    * @returns {string} The matching country code.
    */
   identifyCountryCode(countryName, countrySpecified) {
-    let countries = countrySpecified ? [countrySpecified] : Object.keys(ALTERNATIVE_COUNTRY_NAMES);
+    let countries = countrySpecified ? [countrySpecified] : SUPPORTED_COUNTRY_LIST;
 
     for (let country of countries) {
       let collators = this.getCollators(country);
 
-      let alternativeCountryNames = ALTERNATIVE_COUNTRY_NAMES[country];
+      let metadata = this.getCountryAddressData(country);
+      let alternativeCountryNames = metadata.alternative_names || [metadata.name];
       let reAlternativeCountryNames = this._reAlternativeCountryNames[country];
       if (!reAlternativeCountryNames) {
         reAlternativeCountryNames = this._reAlternativeCountryNames[country] = [];
       }
 
       for (let i = 0; i < alternativeCountryNames.length; i++) {
         let name = alternativeCountryNames[i];
         let reName = reAlternativeCountryNames[i];
@@ -428,17 +496,17 @@ this.FormAutofillUtils = {
           let optionText = this.identifyValue(keys, names, option.text, collators);
           if (identifiedValue === optionValue || identifiedValue === optionText || pattern.test(option.value)) {
             return option;
           }
         }
         break;
       }
       case "country": {
-        if (ALTERNATIVE_COUNTRY_NAMES[value]) {
+        if (this.getCountryAddressData(value).alternative_names) {
           for (let option of selectEl.options) {
             if (this.identifyCountryCode(option.text, value) || this.identifyCountryCode(option.value, value)) {
               return option;
             }
           }
         }
         break;
       }
--- a/browser/extensions/formautofill/addressmetadata/addressReferences.js
+++ b/browser/extensions/formautofill/addressmetadata/addressReferences.js
@@ -5,11 +5,16 @@
 /* exported addressData */
 /* eslint max-len: 0 */
 
 "use strict";
 
 // The data below is initially copied from
 // 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/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"},
 };
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/addressmetadata/addressReferencesExt.js
@@ -0,0 +1,17 @@
+/* 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/. */
+
+/* exported addressDataExt */
+/* eslint max-len: 0 */
+
+"use strict";
+
+// "addressDataExt" uses the same key as "addressData" in "addressReferences.js" and contains
+//  contains the information we need but absent in "libaddressinput" such as alternative names.
+
+// TODO: We only support the alternative name of US in MVP. We are going to support more countries in
+//       bug 1370193.
+var addressDataExt = {
+  "data/US": {"alternative_names": ["US", "United States of America", "United States", "America", "U.S.", "USA", "U.S.A.", "U.S.A"]},
+};
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_addressDataLoader.js
@@ -0,0 +1,49 @@
+"use strict";
+
+Cu.import("resource://formautofill/FormAutofillUtils.jsm");
+
+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);
+});
+
+add_task(async function test_loadDataState() {
+  sinon.spy(AddressDataLoader, "_loadScripts");
+  let metadata = FormAutofillUtils.getCountryAddressData("US");
+  Assert.ok(AddressDataLoader._addressData, "addressData exists");
+  // Verify _dataLoaded state
+  Assert.equal(AddressDataLoader._dataLoaded.country, true);
+  Assert.equal(AddressDataLoader._dataLoaded.level1.size, 0);
+  // _loadScripts should be called
+  sinon.assert.called(AddressDataLoader._loadScripts);
+  // Verify metadata
+  Assert.equal(metadata.id, "data/US");
+  Assert.ok(metadata.alternative_names,
+            "US alternative names should be loaded from extension");
+  AddressDataLoader._loadScripts.reset();
+
+  // Load data without country
+  let newMetadata = FormAutofillUtils.getCountryAddressData();
+  // _loadScripts should not be called
+  sinon.assert.notCalled(AddressDataLoader._loadScripts);
+  Assert.deepEqual(metadata, newMetadata, "metadata should be US if country is not specified");
+  AddressDataLoader._loadScripts.reset();
+
+  // Load level 1 data that does not exist
+  let undefinedMetadata = FormAutofillUtils.getCountryAddressData("US", "CA");
+  // _loadScripts should be called
+  sinon.assert.called(AddressDataLoader._loadScripts);
+  Assert.equal(undefinedMetadata, undefined, "metadata should be undefined");
+  Assert.ok(AddressDataLoader._dataLoaded.level1.has("US"),
+               "level 1 state array should be set even there's no valid metadata");
+  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);
+});
--- a/browser/extensions/formautofill/test/unit/xpcshell.ini
+++ b/browser/extensions/formautofill/test/unit/xpcshell.ini
@@ -13,16 +13,17 @@ support-files =
 [heuristics/third_party/test_Macys.js]
 [heuristics/third_party/test_NewEgg.js]
 [heuristics/third_party/test_OfficeDepot.js]
 [heuristics/third_party/test_QVC.js]
 [heuristics/third_party/test_Sears.js]
 [heuristics/third_party/test_Staples.js]
 [heuristics/third_party/test_Walmart.js]
 [test_activeStatus.js]
+[test_addressDataLoader.js]
 [test_addressRecords.js]
 [test_autofillFormFields.js]
 [test_collectFormFields.js]
 [test_createRecords.js]
 [test_creditCardRecords.js]
 [test_extractLabelStrings.js]
 [test_findLabelElements.js]
 [test_getAdaptedProfiles.js]
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -67,16 +67,17 @@
   "file_stringencoding.jsm": ["checkFromJSM"],
   "file_url.jsm": ["checkFromJSM"],
   "file_worker_url.jsm": ["checkFromJSM"],
   "Finder.jsm": ["Finder", "GetClipboardSearchString"],
   "forms.js": ["FormEngine", "FormRec", "FormValidator"],
   "forms.jsm": ["FormData"],
   "FormAutofillHeuristics.jsm": ["FormAutofillHeuristics", "LabelUtils"],
   "FormAutofillSync.jsm": ["AddressesEngine", "CreditCardsEngine"],
+  "FormAutofillUtils.jsm": ["FormAutofillUtils", "AddressDataLoader"],
   "FrameScriptManager.jsm": ["getNewLoaderID"],
   "fxa_utils.js": ["initializeIdentityWithTokenServerResponse"],
   "fxaccounts.jsm": ["Authentication"],
   "FxAccounts.jsm": ["fxAccounts", "FxAccounts"],
   "FxAccountsCommon.js": ["log", "logPII", "FXACCOUNTS_PERMISSION", "DATA_FORMAT_VERSION", "DEFAULT_STORAGE_FILENAME", "ASSERTION_LIFETIME", "ASSERTION_USE_PERIOD", "CERT_LIFETIME", "KEY_LIFETIME", "POLL_SESSION", "ONLOGIN_NOTIFICATION", "ONVERIFIED_NOTIFICATION", "ONLOGOUT_NOTIFICATION", "ON_FXA_UPDATE_NOTIFICATION", "ON_DEVICE_CONNECTED_NOTIFICATION", "ON_DEVICE_DISCONNECTED_NOTIFICATION", "ON_PROFILE_UPDATED_NOTIFICATION", "ON_PASSWORD_CHANGED_NOTIFICATION", "ON_PASSWORD_RESET_NOTIFICATION", "ON_VERIFY_LOGIN_NOTIFICATION", "ON_ACCOUNT_DESTROYED_NOTIFICATION", "ON_COLLECTION_CHANGED_NOTIFICATION", "FXA_PUSH_SCOPE_ACCOUNT_UPDATE", "ON_PROFILE_CHANGE_NOTIFICATION", "ON_ACCOUNT_STATE_CHANGE_NOTIFICATION", "UI_REQUEST_SIGN_IN_FLOW", "UI_REQUEST_REFRESH_AUTH", "FX_OAUTH_CLIENT_ID", "WEBCHANNEL_ID", "PREF_LAST_FXA_USER", "ERRNO_ACCOUNT_ALREADY_EXISTS", "ERRNO_ACCOUNT_DOES_NOT_EXIST", "ERRNO_INCORRECT_PASSWORD", "ERRNO_UNVERIFIED_ACCOUNT", "ERRNO_INVALID_VERIFICATION_CODE", "ERRNO_NOT_VALID_JSON_BODY", "ERRNO_INVALID_BODY_PARAMETERS", "ERRNO_MISSING_BODY_PARAMETERS", "ERRNO_INVALID_REQUEST_SIGNATURE", "ERRNO_INVALID_AUTH_TOKEN", "ERRNO_INVALID_AUTH_TIMESTAMP", "ERRNO_MISSING_CONTENT_LENGTH", "ERRNO_REQUEST_BODY_TOO_LARGE", "ERRNO_TOO_MANY_CLIENT_REQUESTS", "ERRNO_INVALID_AUTH_NONCE", "ERRNO_ENDPOINT_NO_LONGER_SUPPORTED", "ERRNO_INCORRECT_LOGIN_METHOD", "ERRNO_INCORRECT_KEY_RETRIEVAL_METHOD", "ERRNO_INCORRECT_API_VERSION", "ERRNO_INCORRECT_EMAIL_CASE", "ERRNO_ACCOUNT_LOCKED", "ERRNO_ACCOUNT_UNLOCKED", "ERRNO_UNKNOWN_DEVICE", "ERRNO_DEVICE_SESSION_CONFLICT", "ERRNO_SERVICE_TEMP_UNAVAILABLE", "ERRNO_PARSE", "ERRNO_NETWORK", "ERRNO_UNKNOWN_ERROR", "OAUTH_SERVER_ERRNO_OFFSET", "ERRNO_UNKNOWN_CLIENT_ID", "ERRNO_INCORRECT_CLIENT_SECRET", "ERRNO_INCORRECT_REDIRECT_URI", "ERRNO_INVALID_FXA_ASSERTION", "ERRNO_UNKNOWN_CODE", "ERRNO_INCORRECT_CODE", "ERRNO_EXPIRED_CODE", "ERRNO_OAUTH_INVALID_TOKEN", "ERRNO_INVALID_REQUEST_PARAM", "ERRNO_INVALID_RESPONSE_TYPE", "ERRNO_UNAUTHORIZED", "ERRNO_FORBIDDEN", "ERRNO_INVALID_CONTENT_TYPE", "ERROR_ACCOUNT_ALREADY_EXISTS", "ERROR_ACCOUNT_DOES_NOT_EXIST", "ERROR_ACCOUNT_LOCKED", "ERROR_ACCOUNT_UNLOCKED", "ERROR_ALREADY_SIGNED_IN_USER", "ERROR_DEVICE_SESSION_CONFLICT", "ERROR_ENDPOINT_NO_LONGER_SUPPORTED", "ERROR_INCORRECT_API_VERSION", "ERROR_INCORRECT_EMAIL_CASE", "ERROR_INCORRECT_KEY_RETRIEVAL_METHOD", "ERROR_INCORRECT_LOGIN_METHOD", "ERROR_INVALID_EMAIL", "ERROR_INVALID_AUDIENCE", "ERROR_INVALID_AUTH_TOKEN", "ERROR_INVALID_AUTH_TIMESTAMP", "ERROR_INVALID_AUTH_NONCE", "ERROR_INVALID_BODY_PARAMETERS", "ERROR_INVALID_PASSWORD", "ERROR_INVALID_VERIFICATION_CODE", "ERROR_INVALID_REFRESH_AUTH_VALUE", "ERROR_INVALID_REQUEST_SIGNATURE", "ERROR_INTERNAL_INVALID_USER", "ERROR_MISSING_BODY_PARAMETERS", "ERROR_MISSING_CONTENT_LENGTH", "ERROR_NO_TOKEN_SESSION", "ERROR_NO_SILENT_REFRESH_AUTH", "ERROR_NOT_VALID_JSON_BODY", "ERROR_OFFLINE", "ERROR_PERMISSION_DENIED", "ERROR_REQUEST_BODY_TOO_LARGE", "ERROR_SERVER_ERROR", "ERROR_SYNC_DISABLED", "ERROR_TOO_MANY_CLIENT_REQUESTS", "ERROR_SERVICE_TEMP_UNAVAILABLE", "ERROR_UI_ERROR", "ERROR_UI_REQUEST", "ERROR_PARSE", "ERROR_NETWORK", "ERROR_UNKNOWN", "ERROR_UNKNOWN_DEVICE", "ERROR_UNVERIFIED_ACCOUNT", "ERROR_UNKNOWN_CLIENT_ID", "ERROR_INCORRECT_CLIENT_SECRET", "ERROR_INCORRECT_REDIRECT_URI", "ERROR_INVALID_FXA_ASSERTION", "ERROR_UNKNOWN_CODE", "ERROR_INCORRECT_CODE", "ERROR_EXPIRED_CODE", "ERROR_OAUTH_INVALID_TOKEN", "ERROR_INVALID_REQUEST_PARAM", "ERROR_INVALID_RESPONSE_TYPE", "ERROR_UNAUTHORIZED", "ERROR_FORBIDDEN", "ERROR_INVALID_CONTENT_TYPE", "ERROR_NO_ACCOUNT", "ERROR_AUTH_ERROR", "ERROR_INVALID_PARAMETER", "ERROR_CODE_METHOD_NOT_ALLOWED", "ERROR_MSG_METHOD_NOT_ALLOWED", "FXA_PWDMGR_PLAINTEXT_FIELDS", "FXA_PWDMGR_SECURE_FIELDS", "FXA_PWDMGR_MEMORY_FIELDS", "FXA_PWDMGR_REAUTH_WHITELIST", "FXA_PWDMGR_HOST", "FXA_PWDMGR_REALM", "SERVER_ERRNO_TO_ERROR", "ERROR_TO_GENERAL_ERROR_CLASS"],
   "FxAccountsOAuthGrantClient.jsm": ["FxAccountsOAuthGrantClient", "FxAccountsOAuthGrantClientError"],
   "FxAccountsProfileClient.jsm": ["FxAccountsProfileClient", "FxAccountsProfileClientError"],
   "FxAccountsPush.js": ["FxAccountsPushService"],