summaryrefslogtreecommitdiffstats
path: root/toolkit/components/formautofill/shared/AddressMetaDataLoader.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/formautofill/shared/AddressMetaDataLoader.sys.mjs')
-rw-r--r--toolkit/components/formautofill/shared/AddressMetaDataLoader.sys.mjs168
1 files changed, 168 insertions, 0 deletions
diff --git a/toolkit/components/formautofill/shared/AddressMetaDataLoader.sys.mjs b/toolkit/components/formautofill/shared/AddressMetaDataLoader.sys.mjs
new file mode 100644
index 0000000000..a7be227921
--- /dev/null
+++ b/toolkit/components/formautofill/shared/AddressMetaDataLoader.sys.mjs
@@ -0,0 +1,168 @@
+/* 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/. */
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ AddressMetaData: "resource://gre/modules/shared/AddressMetaData.sys.mjs",
+ AddressMetaDataExtension:
+ "resource://gre/modules/shared/AddressMetaDataExtension.sys.mjs",
+});
+
+export class AddressMetaDataLoader {
+ // 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.
+ static dataLoaded = {
+ country: false,
+ level1: new Set(),
+ };
+
+ static addressData = {};
+
+ static DATA_PREFIX = "data/";
+
+ /**
+ * Load address meta data and extension into one object.
+ *
+ * @returns {object}
+ * An object containing address data object with properties from extension.
+ */
+ static loadAddressMetaData() {
+ const addressMetaData = lazy.AddressMetaData;
+
+ for (const key in lazy.AddressMetaDataExtension) {
+ let addressDataForKey = addressMetaData[key];
+ if (!addressDataForKey) {
+ addressDataForKey = addressMetaData[key] = {};
+ }
+
+ Object.assign(addressDataForKey, lazy.AddressMetaDataExtension[key]);
+ }
+ return addressMetaData;
+ }
+
+ /**
+ * Convert certain properties' string value into array. We should make sure
+ * the cached data is parsed.
+ *
+ * @param {object} data Original metadata from addressReferences.
+ * @returns {object} parsed metadata with property value that converts to array.
+ */
+ static #parse(data) {
+ if (!data) {
+ return null;
+ }
+
+ const properties = [
+ "languages",
+ "sub_keys",
+ "sub_isoids",
+ "sub_names",
+ "sub_lnames",
+ ];
+ for (const key of properties) {
+ if (!data[key]) {
+ continue;
+ }
+ // No need to normalize data if the value is array already.
+ if (Array.isArray(data[key])) {
+ return data;
+ }
+
+ data[key] = data[key].split("~");
+ }
+ return data;
+ }
+
+ /**
+ * 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} Default locale metadata
+ */
+ static #loadData(country, level1 = null) {
+ // Load the addressData if needed
+ if (!this.dataLoaded.country) {
+ this.addressData = this.loadAddressMetaData();
+ this.dataLoaded.country = true;
+ }
+ if (!level1) {
+ return this.#parse(this.addressData[`${this.DATA_PREFIX}${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.loadAddressMetaData());
+ this.dataLoaded.level1.add(country);
+ }
+ return this.#parse(
+ this.addressData[`${this.DATA_PREFIX}${country}/${level1}`]
+ );
+ }
+
+ /**
+ * Return the region metadata with default locale and other locales (if exists).
+ *
+ * @param {string} country
+ * @param {string?} level1
+ * @returns {object} Return default locale and other locales metadata.
+ */
+ static getData(country, level1 = null) {
+ const defaultLocale = this.#loadData(country, level1);
+ if (!defaultLocale) {
+ return null;
+ }
+
+ const countryData = this.#parse(
+ this.addressData[`${this.DATA_PREFIX}${country}`]
+ );
+ let locales = [];
+ // TODO: Should be able to support multi-locale level 1/ level 2 metadata query
+ // in Bug 1421886
+ if (countryData.languages) {
+ const list = countryData.languages.filter(
+ key => key !== countryData.lang
+ );
+ locales = list.map(key =>
+ this.#parse(this.addressData[`${defaultLocale.id}--${key}`])
+ );
+ }
+ return { defaultLocale, locales };
+ }
+
+ /**
+ * Return an array containing countries alpha2 codes.
+ *
+ * @returns {Array} Return an array containing countries alpha2 codes.
+ */
+ static get #countryCodes() {
+ return Object.keys(lazy.AddressMetaDataExtension).map(dataKey =>
+ dataKey.replace(this.DATA_PREFIX, "")
+ );
+ }
+
+ static getCountries(locales = []) {
+ const displayNames = new Intl.DisplayNames(locales, {
+ type: "region",
+ fallback: "none",
+ });
+ const countriesMap = new Map();
+ for (const countryCode of this.#countryCodes) {
+ countriesMap.set(countryCode, displayNames.of(countryCode));
+ }
+ return countriesMap;
+ }
+}
+
+export default AddressMetaDataLoader;