1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5"use strict";
6
7var EXPORTED_SYMBOLS = ["FormAutofill"];
8
9const { XPCOMUtils } = ChromeUtils.import(
10  "resource://gre/modules/XPCOMUtils.jsm"
11);
12const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
13
14XPCOMUtils.defineLazyModuleGetters(this, {
15  Region: "resource://gre/modules/Region.jsm",
16});
17
18const ADDRESSES_FIRST_TIME_USE_PREF = "extensions.formautofill.firstTimeUse";
19const AUTOFILL_ADDRESSES_AVAILABLE_PREF =
20  "extensions.formautofill.addresses.supported";
21// This pref should be refactored after the migration of the old bool pref
22const AUTOFILL_CREDITCARDS_AVAILABLE_PREF =
23  "extensions.formautofill.creditCards.supported";
24const BROWSER_SEARCH_REGION_PREF = "browser.search.region";
25const CREDITCARDS_USED_STATUS_PREF = "extensions.formautofill.creditCards.used";
26const CREDITCARDS_AUTOFILL_SUPPORTED_COUNTRIES_PREF =
27  "extensions.formautofill.creditCards.supportedCountries";
28const ENABLED_AUTOFILL_ADDRESSES_PREF =
29  "extensions.formautofill.addresses.enabled";
30const ENABLED_AUTOFILL_ADDRESSES_CAPTURE_PREF =
31  "extensions.formautofill.addresses.capture.enabled";
32const ENABLED_AUTOFILL_ADDRESSES_SUPPORTED_COUNTRIES_PREF =
33  "extensions.formautofill.addresses.supportedCountries";
34const ENABLED_AUTOFILL_CREDITCARDS_PREF =
35  "extensions.formautofill.creditCards.enabled";
36const ENABLED_AUTOFILL_CREDITCARDS_REAUTH_PREF =
37  "extensions.formautofill.reauth.enabled";
38const AUTOFILL_CREDITCARDS_HIDE_UI_PREF =
39  "extensions.formautofill.creditCards.hideui";
40const FORM_AUTOFILL_SUPPORT_RTL_PREF = "extensions.formautofill.supportRTL";
41
42XPCOMUtils.defineLazyPreferenceGetter(
43  this,
44  "logLevel",
45  "extensions.formautofill.loglevel",
46  "Warn"
47);
48
49// A logging helper for debug logging to avoid creating Console objects
50// or triggering expensive JS -> C++ calls when debug logging is not
51// enabled.
52//
53// Console objects, even natively-implemented ones, can consume a lot of
54// memory, and since this code may run in every content process, that
55// memory can add up quickly. And, even when debug-level messages are
56// being ignored, console.debug() calls can be expensive.
57//
58// This helper avoids both of those problems by never touching the
59// console object unless debug logging is enabled.
60function debug() {
61  if (logLevel.toLowerCase() == "debug") {
62    this.log.debug(...arguments);
63  }
64}
65
66var FormAutofill = {
67  ENABLED_AUTOFILL_ADDRESSES_PREF,
68  ENABLED_AUTOFILL_ADDRESSES_CAPTURE_PREF,
69  ENABLED_AUTOFILL_CREDITCARDS_PREF,
70  ENABLED_AUTOFILL_CREDITCARDS_REAUTH_PREF,
71  ADDRESSES_FIRST_TIME_USE_PREF,
72  CREDITCARDS_USED_STATUS_PREF,
73
74  get DEFAULT_REGION() {
75    return Region.home || "US";
76  },
77  /**
78   * Determines if an autofill feature should be enabled based on the "available"
79   * and "supportedCountries" parameters.
80   *
81   * @param {string} available Available can be one of the following: "on", "detect", "off".
82   * "on" forces the particular Form Autofill feature on, while "detect" utilizes the supported countries
83   * to see if the feature should be available.
84   * @param {string[]} supportedCountries
85   * @returns {boolean} `true` if autofill feature is supported in the current browser search region
86   */
87  _isSupportedRegion(available, supportedCountries) {
88    if (available == "on") {
89      return true;
90    } else if (available == "detect") {
91      if (!FormAutofill.supportRTL && Services.locale.isAppLocaleRTL) {
92        return false;
93      }
94      // TODO: Bug 1747284. Use Region.home instead of reading "browser.serach.region"
95      // by default. However, Region.home doesn't observe preference change at this point,
96      // we should also fix that issue.
97      let region = Services.prefs.getCharPref(
98        BROWSER_SEARCH_REGION_PREF,
99        this.DEFAULT_REGION
100      );
101      return supportedCountries.includes(region);
102    }
103    return false;
104  },
105  isAutofillAddressesAvailableInCountry(country) {
106    return FormAutofill._addressAutofillSupportedCountries.includes(country);
107  },
108  get isAutofillEnabled() {
109    return this.isAutofillAddressesEnabled || this.isAutofillCreditCardsEnabled;
110  },
111  /**
112   * Determines if the credit card autofill feature is available to use in the browser.
113   * If the feature is not available, then there are no user facing ways to enable it.
114   * @returns {boolean} `true` if credit card autofill is available
115   */
116  get isAutofillCreditCardsAvailable() {
117    return this._isSupportedRegion(
118      FormAutofill._isAutofillCreditCardsAvailable,
119      FormAutofill._creditCardAutofillSupportedCountries
120    );
121  },
122  /**
123   * Determines if the address autofill feature is available to use in the browser.
124   * If the feature is not available, then there are no user facing ways to enable it.
125   * @returns {boolean} `true` if address autofill is available
126   */
127  get isAutofillAddressesAvailable() {
128    return this._isSupportedRegion(
129      FormAutofill._isAutofillAddressesAvailable,
130      FormAutofill._addressAutofillSupportedCountries
131    );
132  },
133  /**
134   * Determines if the user has enabled or disabled credit card autofill.
135   * @returns {boolean} `true` if credit card autofill is enabled
136   */
137  get isAutofillCreditCardsEnabled() {
138    return (
139      this.isAutofillCreditCardsAvailable &&
140      FormAutofill._isAutofillCreditCardsEnabled
141    );
142  },
143  /**
144   * Determines if the user has enabled or disabled address autofill.
145   * @returns {boolean} `true` if address autofill is enabled
146   */
147  get isAutofillAddressesEnabled() {
148    return (
149      this.isAutofillAddressesAvailable &&
150      FormAutofill._isAutofillAddressesEnabled
151    );
152  },
153
154  defineLazyLogGetter(scope, logPrefix) {
155    scope.debug = debug;
156
157    XPCOMUtils.defineLazyGetter(scope, "log", () => {
158      let ConsoleAPI = ChromeUtils.import(
159        "resource://gre/modules/Console.jsm",
160        {}
161      ).ConsoleAPI;
162      return new ConsoleAPI({
163        maxLogLevelPref: "extensions.formautofill.loglevel",
164        prefix: logPrefix,
165      });
166    });
167  },
168};
169
170XPCOMUtils.defineLazyPreferenceGetter(
171  FormAutofill,
172  "_isAutofillAddressesAvailable",
173  AUTOFILL_ADDRESSES_AVAILABLE_PREF
174);
175XPCOMUtils.defineLazyPreferenceGetter(
176  FormAutofill,
177  "_isAutofillAddressesEnabled",
178  ENABLED_AUTOFILL_ADDRESSES_PREF
179);
180XPCOMUtils.defineLazyPreferenceGetter(
181  FormAutofill,
182  "isAutofillAddressesCaptureEnabled",
183  ENABLED_AUTOFILL_ADDRESSES_CAPTURE_PREF
184);
185XPCOMUtils.defineLazyPreferenceGetter(
186  FormAutofill,
187  "_isAutofillCreditCardsAvailable",
188  AUTOFILL_CREDITCARDS_AVAILABLE_PREF
189);
190XPCOMUtils.defineLazyPreferenceGetter(
191  FormAutofill,
192  "_isAutofillCreditCardsEnabled",
193  ENABLED_AUTOFILL_CREDITCARDS_PREF
194);
195XPCOMUtils.defineLazyPreferenceGetter(
196  FormAutofill,
197  "isAutofillCreditCardsHideUI",
198  AUTOFILL_CREDITCARDS_HIDE_UI_PREF
199);
200XPCOMUtils.defineLazyPreferenceGetter(
201  FormAutofill,
202  "isAutofillAddressesFirstTimeUse",
203  ADDRESSES_FIRST_TIME_USE_PREF
204);
205XPCOMUtils.defineLazyPreferenceGetter(
206  FormAutofill,
207  "AutofillCreditCardsUsedStatus",
208  CREDITCARDS_USED_STATUS_PREF
209);
210XPCOMUtils.defineLazyPreferenceGetter(
211  FormAutofill,
212  "_addressAutofillSupportedCountries",
213  ENABLED_AUTOFILL_ADDRESSES_SUPPORTED_COUNTRIES_PREF,
214  null,
215  val => val.split(",")
216);
217XPCOMUtils.defineLazyPreferenceGetter(
218  FormAutofill,
219  "_creditCardAutofillSupportedCountries",
220  CREDITCARDS_AUTOFILL_SUPPORTED_COUNTRIES_PREF,
221  null,
222  null,
223  val => val.split(",")
224);
225XPCOMUtils.defineLazyPreferenceGetter(
226  FormAutofill,
227  "supportRTL",
228  FORM_AUTOFILL_SUPPORT_RTL_PREF
229);
230
231// XXX: This should be invalidated on intl:app-locales-changed.
232XPCOMUtils.defineLazyGetter(FormAutofill, "countries", () => {
233  let availableRegionCodes = Services.intl.getAvailableLocaleDisplayNames(
234    "region"
235  );
236  let displayNames = Services.intl.getRegionDisplayNames(
237    undefined,
238    availableRegionCodes
239  );
240  let result = new Map();
241  for (let i = 0; i < availableRegionCodes.length; i++) {
242    result.set(availableRegionCodes[i].toUpperCase(), displayNames[i]);
243  }
244  return result;
245});
246