Pier Angelo Vendrame pushed to branch tor-browser-115.8.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
- 
9cecc61f
by Pier Angelo Vendrame at 2024-03-11T09:33:51+01:00
- 
138e8d6b
by Pier Angelo Vendrame at 2024-03-11T16:19:47+01:00
- 
6265cc34
by Pier Angelo Vendrame at 2024-03-11T16:19:52+01:00
- 
e409c32a
by Pier Angelo Vendrame at 2024-03-11T16:19:52+01:00
- 
f8451ae0
by Pier Angelo Vendrame at 2024-03-11T16:19:53+01:00
10 changed files:
- browser/components/torpreferences/content/connectionPane.js
- browser/components/torpreferences/content/connectionSettingsDialog.js
- toolkit/components/tor-launcher/TorControlPort.sys.mjs
- toolkit/components/tor-launcher/TorProvider.sys.mjs
- toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
- toolkit/components/tor-launcher/TorStartupService.sys.mjs
- toolkit/modules/Moat.sys.mjs
- toolkit/modules/TorAndroidIntegration.sys.mjs
- toolkit/modules/TorConnect.sys.mjs
- toolkit/modules/TorSettings.sys.mjs
Changes:
| ... | ... | @@ -114,10 +114,9 @@ async function setTorSettings(changes) { | 
| 114 | 114 |      // This will trigger TorSettings.#cleanupSettings()
 | 
| 115 | 115 |      TorSettings.saveToPrefs();
 | 
| 116 | 116 |      try {
 | 
| 117 | -      // May throw.
 | |
| 118 | 117 |        await TorSettings.applySettings();
 | 
| 119 | 118 |      } catch (e) {
 | 
| 120 | -      console.error("Failed to save Tor settings", e);
 | |
| 119 | +      console.error("Failed to apply Tor settings", e);
 | |
| 121 | 120 |      }
 | 
| 122 | 121 |    } finally {
 | 
| 123 | 122 |      TorSettings.thawNotifications();
 | 
| ... | ... | @@ -362,6 +362,8 @@ const gConnectionSettingsDialog = { | 
| 362 | 362 |      }
 | 
| 363 | 363 | |
| 364 | 364 |      TorSettings.saveToPrefs();
 | 
| 365 | +    // FIXME: What if this fails? Should we prevent the dialog to close and show
 | |
| 366 | +    // an error?
 | |
| 365 | 367 |      TorSettings.applySettings();
 | 
| 366 | 368 |    },
 | 
| 367 | 369 |  };
 | 
| ... | ... | @@ -838,10 +838,12 @@ export class TorController { | 
| 838 | 838 |    /**
 | 
| 839 | 839 |     * Send multiple configuration values to tor.
 | 
| 840 | 840 |     *
 | 
| 841 | -   * @param {object} values The values to set
 | |
| 841 | +   * @param {Array} values The values to set. It should be an array of
 | |
| 842 | +   * [key, value] pairs to pass to SETCONF. Keys can be repeated, and array
 | |
| 843 | +   * values will be automatically unrolled.
 | |
| 842 | 844 |     */
 | 
| 843 | 845 |    async setConf(values) {
 | 
| 844 | -    const args = Object.entries(values)
 | |
| 846 | +    const args = values
 | |
| 845 | 847 |        .flatMap(([key, value]) => {
 | 
| 846 | 848 |          if (value === undefined || value === null) {
 | 
| 847 | 849 |            return [key];
 | 
| ... | ... | @@ -871,7 +873,7 @@ export class TorController { | 
| 871 | 873 |     * @param {boolean} enabled Tell whether the network should be enabled
 | 
| 872 | 874 |     */
 | 
| 873 | 875 |    async setNetworkEnabled(enabled) {
 | 
| 874 | -    return this.setConf({ DisableNetwork: !enabled });
 | |
| 876 | +    return this.setConf([["DisableNetwork", !enabled]]);
 | |
| 875 | 877 |    }
 | 
| 876 | 878 | |
| 877 | 879 |    /**
 | 
| ... | ... | @@ -15,6 +15,8 @@ ChromeUtils.defineESModuleGetters(lazy, { | 
| 15 | 15 |    TorController: "resource://gre/modules/TorControlPort.sys.mjs",
 | 
| 16 | 16 |    TorProcess: "resource://gre/modules/TorProcess.sys.mjs",
 | 
| 17 | 17 |    TorProcessAndroid: "resource://gre/modules/TorProcessAndroid.sys.mjs",
 | 
| 18 | +  TorProxyType: "resource://gre/modules/TorSettings.sys.mjs",
 | |
| 19 | +  TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
 | |
| 18 | 20 |  });
 | 
| 19 | 21 | |
| 20 | 22 |  const logger = new ConsoleAPI({
 | 
| ... | ... | @@ -73,6 +75,20 @@ const Preferences = Object.freeze({ | 
| 73 | 75 |    PromptAtStartup: "extensions.torlauncher.prompt_at_startup",
 | 
| 74 | 76 |  });
 | 
| 75 | 77 | |
| 78 | +/* Config Keys used to configure tor daemon */
 | |
| 79 | +const TorConfigKeys = Object.freeze({
 | |
| 80 | +  useBridges: "UseBridges",
 | |
| 81 | +  bridgeList: "Bridge",
 | |
| 82 | +  socks4Proxy: "Socks4Proxy",
 | |
| 83 | +  socks5Proxy: "Socks5Proxy",
 | |
| 84 | +  socks5ProxyUsername: "Socks5ProxyUsername",
 | |
| 85 | +  socks5ProxyPassword: "Socks5ProxyPassword",
 | |
| 86 | +  httpsProxy: "HTTPSProxy",
 | |
| 87 | +  httpsProxyAuthenticator: "HTTPSProxyAuthenticator",
 | |
| 88 | +  reachableAddresses: "ReachableAddresses",
 | |
| 89 | +  clientTransportPlugin: "ClientTransportPlugin",
 | |
| 90 | +});
 | |
| 91 | + | |
| 76 | 92 |  /**
 | 
| 77 | 93 |   * This is a Tor provider for the C Tor daemon.
 | 
| 78 | 94 |   *
 | 
| ... | ... | @@ -166,15 +182,6 @@ export class TorProvider { | 
| 166 | 182 |     */
 | 
| 167 | 183 |    #currentBridge = null;
 | 
| 168 | 184 | |
| 169 | -  /**
 | |
| 170 | -   * Maintain a map of tor settings set by Tor Browser so that we don't
 | |
| 171 | -   * repeatedly set the same key/values over and over.
 | |
| 172 | -   * This map contains string keys to primitives or array values.
 | |
| 173 | -   *
 | |
| 174 | -   * @type {Map<string, any>}
 | |
| 175 | -   */
 | |
| 176 | -  #settingsCache = new Map();
 | |
| 177 | - | |
| 178 | 185 |    /**
 | 
| 179 | 186 |     * Starts a new tor process and connect to its control port, or connect to the
 | 
| 180 | 187 |     * control port of an existing tor daemon.
 | 
| ... | ... | @@ -219,13 +226,22 @@ export class TorProvider { | 
| 219 | 226 |        throw e;
 | 
| 220 | 227 |      }
 | 
| 221 | 228 | |
| 229 | +    try {
 | |
| 230 | +      await lazy.TorSettings.initializedPromise;
 | |
| 231 | +      await this.writeSettings(lazy.TorSettings.getSettings());
 | |
| 232 | +    } catch (e) {
 | |
| 233 | +      logger.warn(
 | |
| 234 | +        "Failed to initialize TorSettings or to write our settings, so uninitializing.",
 | |
| 235 | +        e
 | |
| 236 | +      );
 | |
| 237 | +      this.uninit();
 | |
| 238 | +      throw e;
 | |
| 239 | +    }
 | |
| 240 | + | |
| 222 | 241 |      TorLauncherUtil.setProxyConfiguration(this.#socksSettings);
 | 
| 223 | 242 | |
| 224 | 243 |      logger.info("The Tor provider is ready.");
 | 
| 225 | 244 | |
| 226 | -    logger.debug(`Notifying ${TorProviderTopics.ProcessIsReady}`);
 | |
| 227 | -    Services.obs.notifyObservers(null, TorProviderTopics.ProcessIsReady);
 | |
| 228 | - | |
| 229 | 245 |      // If we are using an external Tor daemon, we might need to fetch circuits
 | 
| 230 | 246 |      // already, in case streams use them. Do not await because we do not want to
 | 
| 231 | 247 |      // block the intialization on this (it should not fail anyway...).
 | 
| ... | ... | @@ -252,42 +268,74 @@ export class TorProvider { | 
| 252 | 268 | |
| 253 | 269 |    // Provider API
 | 
| 254 | 270 | |
| 255 | -  async writeSettings(settingsObj) {
 | |
| 256 | -    // TODO: Move the translation from settings object to settings understood by
 | |
| 257 | -    // tor here.
 | |
| 258 | -    const entries =
 | |
| 259 | -      settingsObj instanceof Map
 | |
| 260 | -        ? Array.from(settingsObj.entries())
 | |
| 261 | -        : Object.entries(settingsObj);
 | |
| 262 | -    // only write settings that have changed
 | |
| 263 | -    const newSettings = entries.filter(([setting, value]) => {
 | |
| 264 | -      if (!this.#settingsCache.has(setting)) {
 | |
| 265 | -        // no cached setting, so write
 | |
| 266 | -        return true;
 | |
| 267 | -      }
 | |
| 271 | +  /**
 | |
| 272 | +   * Send settings to the tor daemon.
 | |
| 273 | +   *
 | |
| 274 | +   * @param {object} settings A settings object, as returned by
 | |
| 275 | +   * TorSettings.getSettings(). This allow to try settings without passing
 | |
| 276 | +   * through TorSettings.
 | |
| 277 | +   */
 | |
| 278 | +  async writeSettings(settings) {
 | |
| 279 | +    logger.debug("TorProvider.writeSettings", settings);
 | |
| 280 | +    const torSettings = new Map();
 | |
| 281 | + | |
| 282 | +    // Bridges
 | |
| 283 | +    const haveBridges =
 | |
| 284 | +      settings.bridges?.enabled && !!settings.bridges.bridge_strings.length;
 | |
| 285 | +    torSettings.set(TorConfigKeys.useBridges, haveBridges);
 | |
| 286 | +    if (haveBridges) {
 | |
| 287 | +      torSettings.set(
 | |
| 288 | +        TorConfigKeys.bridgeList,
 | |
| 289 | +        settings.bridges.bridge_strings
 | |
| 290 | +      );
 | |
| 291 | +    } else {
 | |
| 292 | +      torSettings.set(TorConfigKeys.bridgeList, null);
 | |
| 293 | +    }
 | |
| 268 | 294 | |
| 269 | -      const cachedValue = this.#settingsCache.get(setting);
 | |
| 270 | -      // Arrays are the only special case for which === could fail.
 | |
| 271 | -      // The other values we accept (strings, booleans, numbers, null and
 | |
| 272 | -      // undefined) work correctly with ===.
 | |
| 273 | -      if (Array.isArray(value) && Array.isArray(cachedValue)) {
 | |
| 274 | -        return (
 | |
| 275 | -          value.length !== cachedValue.length ||
 | |
| 276 | -          value.some((val, idx) => val !== cachedValue[idx])
 | |
| 295 | +    // Proxy
 | |
| 296 | +    torSettings.set(TorConfigKeys.socks4Proxy, null);
 | |
| 297 | +    torSettings.set(TorConfigKeys.socks5Proxy, null);
 | |
| 298 | +    torSettings.set(TorConfigKeys.socks5ProxyUsername, null);
 | |
| 299 | +    torSettings.set(TorConfigKeys.socks5ProxyPassword, null);
 | |
| 300 | +    torSettings.set(TorConfigKeys.httpsProxy, null);
 | |
| 301 | +    torSettings.set(TorConfigKeys.httpsProxyAuthenticator, null);
 | |
| 302 | +    if (settings.proxy && !settings.proxy.enabled) {
 | |
| 303 | +      settings.proxy.type = null;
 | |
| 304 | +    }
 | |
| 305 | +    const address = settings.proxy?.address;
 | |
| 306 | +    const port = settings.proxy?.port;
 | |
| 307 | +    const username = settings.proxy?.username;
 | |
| 308 | +    const password = settings.proxy?.password;
 | |
| 309 | +    switch (settings.proxy?.type) {
 | |
| 310 | +      case lazy.TorProxyType.Socks4:
 | |
| 311 | +        torSettings.set(TorConfigKeys.socks4Proxy, `${address}:${port}`);
 | |
| 312 | +        break;
 | |
| 313 | +      case lazy.TorProxyType.Socks5:
 | |
| 314 | +        torSettings.set(TorConfigKeys.socks5Proxy, `${address}:${port}`);
 | |
| 315 | +        torSettings.set(TorConfigKeys.socks5ProxyUsername, username);
 | |
| 316 | +        torSettings.set(TorConfigKeys.socks5ProxyPassword, password);
 | |
| 317 | +        break;
 | |
| 318 | +      case lazy.TorProxyType.HTTPS:
 | |
| 319 | +        torSettings.set(TorConfigKeys.httpsProxy, `${address}:${port}`);
 | |
| 320 | +        torSettings.set(
 | |
| 321 | +          TorConfigKeys.httpsProxyAuthenticator,
 | |
| 322 | +          `${username}:${password}`
 | |
| 277 | 323 |          );
 | 
| 278 | -      }
 | |
| 279 | -      return value !== cachedValue;
 | |
| 280 | -    });
 | |
| 281 | - | |
| 282 | -    // only write if new setting to save
 | |
| 283 | -    if (newSettings.length) {
 | |
| 284 | -      await this.#controller.setConf(Object.fromEntries(newSettings));
 | |
| 324 | +        break;
 | |
| 325 | +    }
 | |
| 285 | 326 | |
| 286 | -      // save settings to cache after successfully writing to Tor
 | |
| 287 | -      for (const [setting, value] of newSettings) {
 | |
| 288 | -        this.#settingsCache.set(setting, value);
 | |
| 289 | -      }
 | |
| 327 | +    // Firewall
 | |
| 328 | +    if (settings.firewall?.enabled) {
 | |
| 329 | +      const reachableAddresses = settings.firewall.allowed_ports
 | |
| 330 | +        .map(port => `*:${port}`)
 | |
| 331 | +        .join(",");
 | |
| 332 | +      torSettings.set(TorConfigKeys.reachableAddresses, reachableAddresses);
 | |
| 333 | +    } else {
 | |
| 334 | +      torSettings.set(TorConfigKeys.reachableAddresses, null);
 | |
| 290 | 335 |      }
 | 
| 336 | + | |
| 337 | +    logger.debug("Mapped settings object", settings, torSettings);
 | |
| 338 | +    await this.#controller.setConf(Array.from(torSettings));
 | |
| 291 | 339 |    }
 | 
| 292 | 340 | |
| 293 | 341 |    async flushSettings() {
 | 
| ... | ... | @@ -9,7 +9,6 @@ ChromeUtils.defineESModuleGetters(lazy, { | 
| 9 | 9 |  });
 | 
| 10 | 10 | |
| 11 | 11 |  export const TorProviderTopics = Object.freeze({
 | 
| 12 | -  ProcessIsReady: "TorProcessIsReady",
 | |
| 13 | 12 |    ProcessExited: "TorProcessExited",
 | 
| 14 | 13 |    BootstrapStatus: "TorBootstrapStatus",
 | 
| 15 | 14 |    BootstrapError: "TorBootstrapError",
 | 
| ... | ... | @@ -31,15 +31,16 @@ export class TorStartupService { | 
| 31 | 31 |      }
 | 
| 32 | 32 |    }
 | 
| 33 | 33 | |
| 34 | -  async #init() {
 | |
| 34 | +  #init() {
 | |
| 35 | 35 |      Services.obs.addObserver(this, BrowserTopics.QuitApplicationGranted);
 | 
| 36 | 36 | |
| 37 | -    // Do not await on this init. build() is expected to await the
 | |
| 38 | -    // initialization, so anything that should need the Tor Provider should
 | |
| 39 | -    // block there, instead.
 | |
| 37 | +    lazy.TorSettings.init();
 | |
| 38 | + | |
| 39 | +    // Theoretically, build() is expected to await the initialization of the
 | |
| 40 | +    // provider, and anything needing the Tor Provider should be able to just
 | |
| 41 | +    // await on TorProviderBuilder.build().
 | |
| 40 | 42 |      lazy.TorProviderBuilder.init();
 | 
| 41 | 43 | |
| 42 | -    await lazy.TorSettings.init();
 | |
| 43 | 44 |      lazy.TorConnect.init();
 | 
| 44 | 45 | |
| 45 | 46 |      lazy.TorDomainIsolator.init();
 | 
| ... | ... | @@ -2,13 +2,13 @@ | 
| 2 | 2 |   * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
| 3 | 3 |   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
| 4 | 4 | |
| 5 | -import { TorBridgeSource } from "resource://gre/modules/TorSettings.sys.mjs";
 | |
| 6 | - | |
| 7 | 5 |  const lazy = {};
 | 
| 8 | 6 | |
| 9 | 7 |  ChromeUtils.defineESModuleGetters(lazy, {
 | 
| 10 | 8 |    DomainFrontRequestBuilder:
 | 
| 11 | 9 |      "resource://gre/modules/DomainFrontedRequests.sys.mjs",
 | 
| 10 | +  TorBridgeSource: "resource://gre/modules/TorSettings.sys.mjs",
 | |
| 11 | +  TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
 | |
| 12 | 12 |  });
 | 
| 13 | 13 | |
| 14 | 14 |  const TorLauncherPrefs = Object.freeze({
 | 
| ... | ... | @@ -211,13 +211,23 @@ export class MoatRPC { | 
| 211 | 211 |      };
 | 
| 212 | 212 |      switch (settings.bridges.source) {
 | 
| 213 | 213 |        case "builtin":
 | 
| 214 | -        retval.bridges.source = TorBridgeSource.BuiltIn;
 | |
| 214 | +        retval.bridges.source = lazy.TorBridgeSource.BuiltIn;
 | |
| 215 | 215 |          retval.bridges.builtin_type = settings.bridges.type;
 | 
| 216 | 216 |          // TorSettings will ignore strings for built-in bridges, and use the
 | 
| 217 | -        // ones it already knows, instead.
 | |
| 217 | +        // ones it already knows, instead. However, when we try these settings
 | |
| 218 | +        // in the connect assist, we skip TorSettings. Therefore, we set the
 | |
| 219 | +        // lines also here (the ones we already known, not the ones we receive
 | |
| 220 | +        // from Moat). This needs TorSettings to be initialized, which by now
 | |
| 221 | +        // should have already happened (this method is used only by TorConnect,
 | |
| 222 | +        // that needs TorSettings to be initialized).
 | |
| 223 | +        // In any case, getBuiltinBridges will throw if the data is not ready,
 | |
| 224 | +        // yet.
 | |
| 225 | +        retval.bridges.bridge_strings = lazy.TorSettings.getBuiltinBridges(
 | |
| 226 | +          settings.bridges.type
 | |
| 227 | +        );
 | |
| 218 | 228 |          break;
 | 
| 219 | 229 |        case "bridgedb":
 | 
| 220 | -        retval.bridges.source = TorBridgeSource.BridgeDB;
 | |
| 230 | +        retval.bridges.source = lazy.TorBridgeSource.BridgeDB;
 | |
| 221 | 231 |          if (settings.bridges.bridge_strings) {
 | 
| 222 | 232 |            retval.bridges.bridge_strings = settings.bridges.bridge_strings;
 | 
| 223 | 233 |          } else {
 | 
| ... | ... | @@ -150,7 +150,7 @@ class TorAndroidIntegrationImpl { | 
| 150 | 150 |              lazy.TorSettings.saveToPrefs();
 | 
| 151 | 151 |            }
 | 
| 152 | 152 |            if (data.apply) {
 | 
| 153 | -            lazy.TorSettings.applySettings();
 | |
| 153 | +            await lazy.TorSettings.applySettings();
 | |
| 154 | 154 |            }
 | 
| 155 | 155 |            break;
 | 
| 156 | 156 |          case ListenedEvents.settingsApply:
 | 
| ... | ... | @@ -2,14 +2,17 @@ | 
| 2 | 2 |   * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
| 3 | 3 |   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
| 4 | 4 | |
| 5 | +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | |
| 5 | 6 |  import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
 | 
| 6 | 7 | |
| 7 | 8 |  const lazy = {};
 | 
| 8 | 9 | |
| 9 | 10 |  ChromeUtils.defineESModuleGetters(lazy, {
 | 
| 11 | +  ConsoleAPI: "resource://gre/modules/Console.sys.mjs",
 | |
| 10 | 12 |    EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
 | 
| 11 | 13 |    MoatRPC: "resource://gre/modules/Moat.sys.mjs",
 | 
| 12 | 14 |    TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs",
 | 
| 15 | +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
 | |
| 13 | 16 |  });
 | 
| 14 | 17 | |
| 15 | 18 |  // TODO: Should we move this to the about:torconnect actor?
 | 
| ... | ... | @@ -20,10 +23,7 @@ ChromeUtils.defineModuleGetter( | 
| 20 | 23 |  );
 | 
| 21 | 24 | |
| 22 | 25 |  import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
 | 
| 23 | -import {
 | |
| 24 | -  TorSettings,
 | |
| 25 | -  TorSettingsTopics,
 | |
| 26 | -} from "resource://gre/modules/TorSettings.sys.mjs";
 | |
| 26 | +import { TorSettings } from "resource://gre/modules/TorSettings.sys.mjs";
 | |
| 27 | 27 | |
| 28 | 28 |  import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs";
 | 
| 29 | 29 | |
| ... | ... | @@ -40,6 +40,7 @@ const TorLauncherPrefs = Object.freeze({ | 
| 40 | 40 |  const TorConnectPrefs = Object.freeze({
 | 
| 41 | 41 |    censorship_level: "torbrowser.debug.censorship_level",
 | 
| 42 | 42 |    allow_internet_test: "torbrowser.bootstrap.allow_internet_test",
 | 
| 43 | +  log_level: "torbrowser.bootstrap.log_level",
 | |
| 43 | 44 |  });
 | 
| 44 | 45 | |
| 45 | 46 |  export const TorConnectState = Object.freeze({
 | 
| ... | ... | @@ -59,6 +60,17 @@ export const TorConnectState = Object.freeze({ | 
| 59 | 60 |    Disabled: "Disabled",
 | 
| 60 | 61 |  });
 | 
| 61 | 62 | |
| 63 | +XPCOMUtils.defineLazyGetter(
 | |
| 64 | +  lazy,
 | |
| 65 | +  "logger",
 | |
| 66 | +  () =>
 | |
| 67 | +    new lazy.ConsoleAPI({
 | |
| 68 | +      maxLogLevel: "info",
 | |
| 69 | +      maxLogLevelPref: TorConnectPrefs.log_level,
 | |
| 70 | +      prefix: "TorConnect",
 | |
| 71 | +    })
 | |
| 72 | +);
 | |
| 73 | + | |
| 62 | 74 |  /*
 | 
| 63 | 75 |                               TorConnect State Transitions
 | 
| 64 | 76 | |
| ... | ... | @@ -194,12 +206,12 @@ class StateCallback { | 
| 194 | 206 |    }
 | 
| 195 | 207 | |
| 196 | 208 |    async begin(...args) {
 | 
| 197 | -    console.log(`TorConnect: Entering ${this._state} state`);
 | |
| 209 | +    lazy.logger.trace(`Entering ${this._state} state`);
 | |
| 198 | 210 |      this._init();
 | 
| 199 | 211 |      try {
 | 
| 200 | 212 |        // this Promise will block until this StateCallback has completed its work
 | 
| 201 | 213 |        await Promise.resolve(this._callback.call(this._context, ...args));
 | 
| 202 | -      console.log(`TorConnect: Exited ${this._state} state`);
 | |
| 214 | +      lazy.logger.info(`Exited ${this._state} state`);
 | |
| 203 | 215 | |
| 204 | 216 |        // handled state transition
 | 
| 205 | 217 |        Services.obs.notifyObservers(
 | 
| ... | ... | @@ -267,16 +279,14 @@ class InternetTest { | 
| 267 | 279 |      this.cancel();
 | 
| 268 | 280 |      this._pending = true;
 | 
| 269 | 281 | |
| 270 | -    console.log("TorConnect: starting the Internet test");
 | |
| 282 | +    lazy.logger.info("Starting the Internet test");
 | |
| 271 | 283 |      this._testAsync()
 | 
| 272 | 284 |        .then(status => {
 | 
| 273 | 285 |          this._pending = false;
 | 
| 274 | 286 |          this._status = status.successful
 | 
| 275 | 287 |            ? InternetStatus.Online
 | 
| 276 | 288 |            : InternetStatus.Offline;
 | 
| 277 | -        console.log(
 | |
| 278 | -          `TorConnect: performed Internet test, outcome ${this._status}`
 | |
| 279 | -        );
 | |
| 289 | +        lazy.logger.info(`Performed Internet test, outcome ${this._status}`);
 | |
| 280 | 290 |          this.onResult(this.status, status.date);
 | 
| 281 | 291 |        })
 | 
| 282 | 292 |        .catch(error => {
 | 
| ... | ... | @@ -305,7 +315,7 @@ class InternetTest { | 
| 305 | 315 |        await mrpc.init();
 | 
| 306 | 316 |        status = await mrpc.testInternetConnection();
 | 
| 307 | 317 |      } catch (err) {
 | 
| 308 | -      console.error("Error while checking the Internet connection", err);
 | |
| 318 | +      lazy.logger.error("Error while checking the Internet connection", err);
 | |
| 309 | 319 |        error = err;
 | 
| 310 | 320 |      } finally {
 | 
| 311 | 321 |        mrpc.uninit();
 | 
| ... | ... | @@ -523,8 +533,8 @@ export const TorConnect = (() => { | 
| 523 | 533 |                    // get "Building circuits: Establishing a Tor circuit failed".
 | 
| 524 | 534 |                    // TODO: Maybe move this logic deeper in the process to know
 | 
| 525 | 535 |                    // when to filter out such errors triggered by cancelling.
 | 
| 526 | -                  console.log(
 | |
| 527 | -                    `TorConnect: Post-cancel error => ${message}; ${details}`
 | |
| 536 | +                  lazy.logger.warn(
 | |
| 537 | +                    `Post-cancel error => ${message}; ${details}`
 | |
| 528 | 538 |                    );
 | 
| 529 | 539 |                    return;
 | 
| 530 | 540 |                  }
 | 
| ... | ... | @@ -628,7 +638,7 @@ export const TorConnect = (() => { | 
| 628 | 638 |                        "vanilla",
 | 
| 629 | 639 |                      ]);
 | 
| 630 | 640 |                    } catch (err) {
 | 
| 631 | -                    console.error(
 | |
| 641 | +                    lazy.logger.error(
 | |
| 632 | 642 |                        "We did not get localized settings, and default settings failed as well",
 | 
| 633 | 643 |                        err
 | 
| 634 | 644 |                      );
 | 
| ... | ... | @@ -651,10 +661,19 @@ export const TorConnect = (() => { | 
| 651 | 661 |                    }
 | 
| 652 | 662 |                  }
 | 
| 653 | 663 | |
| 664 | +                const restoreOriginalSettings = async () => {
 | |
| 665 | +                  try {
 | |
| 666 | +                    await TorSettings.applySettings();
 | |
| 667 | +                  } catch (e) {
 | |
| 668 | +                    // We cannot do much if the original settings were bad or
 | |
| 669 | +                    // if the connection closed, so just report it in the
 | |
| 670 | +                    // console.
 | |
| 671 | +                    lazy.logger.warn("Failed to restore original settings.", e);
 | |
| 672 | +                  }
 | |
| 673 | +                };
 | |
| 674 | + | |
| 654 | 675 |                  // apply each of our settings and try to bootstrap with each
 | 
| 655 | 676 |                  try {
 | 
| 656 | -                  this.originalSettings = TorSettings.getSettings();
 | |
| 657 | - | |
| 658 | 677 |                    for (const [
 | 
| 659 | 678 |                      index,
 | 
| 660 | 679 |                      currentSetting,
 | 
| ... | ... | @@ -664,14 +683,32 @@ export const TorConnect = (() => { | 
| 664 | 683 |                        break;
 | 
| 665 | 684 |                      }
 | 
| 666 | 685 | |
| 667 | -                    console.log(
 | |
| 668 | -                      `TorConnect: Attempting Bootstrap with configuration ${
 | |
| 669 | -                        index + 1
 | |
| 670 | -                      }/${this.settings.length}`
 | |
| 686 | +                    lazy.logger.info(
 | |
| 687 | +                      `Attempting Bootstrap with configuration ${index + 1}/${
 | |
| 688 | +                        this.settings.length
 | |
| 689 | +                      }`
 | |
| 671 | 690 |                      );
 | 
| 672 | 691 | |
| 673 | -                    TorSettings.setSettings(currentSetting);
 | |
| 674 | -                    await TorSettings.applySettings();
 | |
| 692 | +                    // Send the new settings directly to the provider. We will
 | |
| 693 | +                    // save them only if the bootstrap succeeds.
 | |
| 694 | +                    // FIXME: We should somehow signal TorSettings users that we
 | |
| 695 | +                    // have set custom settings, and they should not apply
 | |
| 696 | +                    // theirs until we are done with trying ours.
 | |
| 697 | +                    // Otherwise, the new settings provided by the user while we
 | |
| 698 | +                    // were bootstrapping could be the ones that cause the
 | |
| 699 | +                    // bootstrap to succeed, but we overwrite them (unless we
 | |
| 700 | +                    // backup the original settings, and then save our new
 | |
| 701 | +                    // settings only if they have not changed).
 | |
| 702 | +                    // Another idea (maybe easier to implement) is to disable
 | |
| 703 | +                    // the settings UI while *any* bootstrap is going on.
 | |
| 704 | +                    // This is also documented in tor-browser#41921.
 | |
| 705 | +                    const provider = await lazy.TorProviderBuilder.build();
 | |
| 706 | +                    // We need to merge with old settings, in case the user is
 | |
| 707 | +                    // using a proxy or is behind a firewall.
 | |
| 708 | +                    await provider.writeSettings({
 | |
| 709 | +                      ...TorSettings.getSettings(),
 | |
| 710 | +                      ...currentSetting,
 | |
| 711 | +                    });
 | |
| 675 | 712 | |
| 676 | 713 |                      // build out our bootstrap request
 | 
| 677 | 714 |                      const tbr = new lazy.TorBootstrapRequest();
 | 
| ... | ... | @@ -679,8 +716,8 @@ export const TorConnect = (() => { | 
| 679 | 716 |                        TorConnect._updateBootstrapStatus(progress, status);
 | 
| 680 | 717 |                      };
 | 
| 681 | 718 |                      tbr.onbootstraperror = (message, details) => {
 | 
| 682 | -                      console.log(
 | |
| 683 | -                        `TorConnect: Auto-Bootstrap error => ${message}; ${details}`
 | |
| 719 | +                      lazy.logger.error(
 | |
| 720 | +                        `Auto-Bootstrap error => ${message}; ${details}`
 | |
| 684 | 721 |                        );
 | 
| 685 | 722 |                      };
 | 
| 686 | 723 | |
| ... | ... | @@ -688,6 +725,7 @@ export const TorConnect = (() => { | 
| 688 | 725 |                      this.on_transition = async nextState => {
 | 
| 689 | 726 |                        if (nextState === TorConnectState.Configuring) {
 | 
| 690 | 727 |                          await tbr.cancel();
 | 
| 728 | +                        await restoreOriginalSettings();
 | |
| 691 | 729 |                        }
 | 
| 692 | 730 |                        resolve();
 | 
| 693 | 731 |                      };
 | 
| ... | ... | @@ -695,23 +733,20 @@ export const TorConnect = (() => { | 
| 695 | 733 |                      // begin bootstrap
 | 
| 696 | 734 |                      if (await tbr.bootstrap()) {
 | 
| 697 | 735 |                        // persist the current settings to preferences
 | 
| 736 | +                      TorSettings.setSettings(currentSetting);
 | |
| 698 | 737 |                        TorSettings.saveToPrefs();
 | 
| 738 | +                      await TorSettings.applySettings();
 | |
| 699 | 739 |                        TorConnect._changeState(TorConnectState.Bootstrapped);
 | 
| 700 | 740 |                        return;
 | 
| 701 | 741 |                      }
 | 
| 702 | 742 |                    }
 | 
| 703 | 743 | |
| 704 | -                  // bootstrapped failed for all potential settings, so reset daemon to use original
 | |
| 705 | -                  TorSettings.setSettings(this.originalSettings);
 | |
| 706 | -                  // The original settings should be good, so we save them to
 | |
| 707 | -                  // preferences before trying to apply them, as it might fail
 | |
| 708 | -                  // if the actual problem is with the connection to the control
 | |
| 709 | -                  // port.
 | |
| 710 | -                  // FIXME: We should handle this case in a better way.
 | |
| 711 | -                  TorSettings.saveToPrefs();
 | |
| 712 | -                  await TorSettings.applySettings();
 | |
| 713 | - | |
| 714 | -                  // only explicitly change state here if something else has not transitioned us
 | |
| 744 | +                  // Bootstrap failed for all potential settings, so restore the
 | |
| 745 | +                  // original settings the provider.
 | |
| 746 | +                  await restoreOriginalSettings();
 | |
| 747 | + | |
| 748 | +                  // Only explicitly change state here if something else has not
 | |
| 749 | +                  // transitioned us.
 | |
| 715 | 750 |                    if (!this.transitioning) {
 | 
| 716 | 751 |                      throw_error(
 | 
| 717 | 752 |                        TorStrings.torConnect.autoBootstrappingFailed,
 | 
| ... | ... | @@ -720,18 +755,8 @@ export const TorConnect = (() => { | 
| 720 | 755 |                    }
 | 
| 721 | 756 |                    return;
 | 
| 722 | 757 |                  } catch (err) {
 | 
| 723 | -                  // restore original settings in case of error
 | |
| 724 | -                  try {
 | |
| 725 | -                    TorSettings.setSettings(this.originalSettings);
 | |
| 726 | -                    // As above
 | |
| 727 | -                    TorSettings.saveToPrefs();
 | |
| 728 | -                    await TorSettings.applySettings();
 | |
| 729 | -                  } catch (errRestore) {
 | |
| 730 | -                    console.log(
 | |
| 731 | -                      `TorConnect: Failed to restore original settings => ${errRestore}`
 | |
| 732 | -                    );
 | |
| 733 | -                  }
 | |
| 734 | -                  // throw to outer catch to transition us
 | |
| 758 | +                  await restoreOriginalSettings();
 | |
| 759 | +                  // throw to outer catch to transition us.
 | |
| 735 | 760 |                    throw err;
 | 
| 736 | 761 |                  }
 | 
| 737 | 762 |                } catch (err) {
 | 
| ... | ... | @@ -748,8 +773,8 @@ export const TorConnect = (() => { | 
| 748 | 773 |                      true
 | 
| 749 | 774 |                    );
 | 
| 750 | 775 |                  } else {
 | 
| 751 | -                  console.error(
 | |
| 752 | -                    "TorConnect: Received AutoBootstrapping error after transitioning",
 | |
| 776 | +                  lazy.logger.error(
 | |
| 777 | +                    "Received AutoBootstrapping error after transitioning",
 | |
| 753 | 778 |                      err
 | 
| 754 | 779 |                    );
 | 
| 755 | 780 |                  }
 | 
| ... | ... | @@ -793,8 +818,8 @@ export const TorConnect = (() => { | 
| 793 | 818 | |
| 794 | 819 |                TorConnect._errorMessage = errorMessage;
 | 
| 795 | 820 |                TorConnect._errorDetails = errorDetails;
 | 
| 796 | -              console.error(
 | |
| 797 | -                `[TorConnect] Entering error state (${errorMessage}, ${errorDetails})`
 | |
| 821 | +              lazy.logger.error(
 | |
| 822 | +                `Entering error state (${errorMessage}, ${errorDetails})`
 | |
| 798 | 823 |                );
 | 
| 799 | 824 | |
| 800 | 825 |                Services.obs.notifyObservers(
 | 
| ... | ... | @@ -835,9 +860,7 @@ export const TorConnect = (() => { | 
| 835 | 860 |          );
 | 
| 836 | 861 |        }
 | 
| 837 | 862 | |
| 838 | -      console.log(
 | |
| 839 | -        `TorConnect: Try transitioning from ${prevState} to ${newState}`
 | |
| 840 | -      );
 | |
| 863 | +      lazy.logger.trace(`Try transitioning from ${prevState} to ${newState}`);
 | |
| 841 | 864 | |
| 842 | 865 |        // set our new state first so that state transitions can themselves trigger
 | 
| 843 | 866 |        // a state transition
 | 
| ... | ... | @@ -851,8 +874,8 @@ export const TorConnect = (() => { | 
| 851 | 874 |        this._bootstrapProgress = progress;
 | 
| 852 | 875 |        this._bootstrapStatus = status;
 | 
| 853 | 876 | |
| 854 | -      console.log(
 | |
| 855 | -        `TorConnect: Bootstrapping ${this._bootstrapProgress}% complete (${this._bootstrapStatus})`
 | |
| 877 | +      lazy.logger.info(
 | |
| 878 | +        `Bootstrapping ${this._bootstrapProgress}% complete (${this._bootstrapStatus})`
 | |
| 856 | 879 |        );
 | 
| 857 | 880 |        Services.obs.notifyObservers(
 | 
| 858 | 881 |          {
 | 
| ... | ... | @@ -866,7 +889,7 @@ export const TorConnect = (() => { | 
| 866 | 889 | |
| 867 | 890 |      // init should be called by TorStartupService
 | 
| 868 | 891 |      init() {
 | 
| 869 | -      console.log("TorConnect: init()");
 | |
| 892 | +      lazy.logger.debug("TorConnect.init()");
 | |
| 870 | 893 |        this._callback(TorConnectState.Initial).begin();
 | 
| 871 | 894 | |
| 872 | 895 |        if (!this.enabled) {
 | 
| ... | ... | @@ -875,9 +898,17 @@ export const TorConnect = (() => { | 
| 875 | 898 |        } else {
 | 
| 876 | 899 |          let observeTopic = addTopic => {
 | 
| 877 | 900 |            Services.obs.addObserver(this, addTopic);
 | 
| 878 | -          console.log(`TorConnect: Observing topic '${addTopic}'`);
 | |
| 901 | +          lazy.logger.debug(`Observing topic '${addTopic}'`);
 | |
| 879 | 902 |          };
 | 
| 880 | 903 | |
| 904 | +        // Wait for TorSettings, as we will need it.
 | |
| 905 | +        // We will wait for a TorProvider only after TorSettings is ready,
 | |
| 906 | +        // because the TorProviderBuilder initialization might not have finished
 | |
| 907 | +        // at this point, and TorSettings initialization is a prerequisite for
 | |
| 908 | +        // having a provider.
 | |
| 909 | +        // So, we prefer initializing TorConnect as soon as possible, so that
 | |
| 910 | +        // the UI will be able to detect it is in the Initializing state and act
 | |
| 911 | +        // consequently.
 | |
| 881 | 912 |          TorSettings.initializedPromise.then(() => this._settingsInitialized());
 | 
| 882 | 913 | |
| 883 | 914 |          // register the Tor topics we always care about
 | 
| ... | ... | @@ -887,7 +918,7 @@ export const TorConnect = (() => { | 
| 887 | 918 |      },
 | 
| 888 | 919 | |
| 889 | 920 |      async observe(subject, topic, data) {
 | 
| 890 | -      console.log(`TorConnect: Observed ${topic}`);
 | |
| 921 | +      lazy.logger.debug(`Observed ${topic}`);
 | |
| 891 | 922 | |
| 892 | 923 |        switch (topic) {
 | 
| 893 | 924 |          case TorTopics.LogHasWarnOrErr: {
 | 
| ... | ... | @@ -919,19 +950,25 @@ export const TorConnect = (() => { | 
| 919 | 950 |        }
 | 
| 920 | 951 |      },
 | 
| 921 | 952 | |
| 922 | -    _settingsInitialized() {
 | |
| 953 | +    async _settingsInitialized() {
 | |
| 954 | +      // TODO: Handle failures here, instead of the prompt to restart the
 | |
| 955 | +      // daemon when it exits (tor-browser#21053, tor-browser#41921).
 | |
| 956 | +      await lazy.TorProviderBuilder.build();
 | |
| 957 | + | |
| 923 | 958 |        // tor-browser#41907: This is only a workaround to avoid users being
 | 
| 924 | 959 |        // bounced back to the initial panel without any explanation.
 | 
| 925 | 960 |        // Longer term we should disable the clickable elements, or find a UX
 | 
| 926 | 961 |        // to prevent this from happening (e.g., allow buttons to be clicked,
 | 
| 927 | 962 |        // but show an intermediate starting state, or a message that tor is
 | 
| 928 | 963 |        // starting while the butons are disabled, etc...).
 | 
| 964 | +      // See also tor-browser#41921.
 | |
| 929 | 965 |        if (this.state !== TorConnectState.Initial) {
 | 
| 930 | -        console.warn(
 | |
| 931 | -          "TorConnect: Seen the torsettings:ready after the state has already changed, ignoring the notification."
 | |
| 966 | +        lazy.logger.warn(
 | |
| 967 | +          "The TorProvider was built after the state had already changed."
 | |
| 932 | 968 |          );
 | 
| 933 | 969 |          return;
 | 
| 934 | 970 |        }
 | 
| 971 | +      lazy.logger.debug("The TorProvider is ready, changing state.");
 | |
| 935 | 972 |        if (this.shouldQuickStart) {
 | 
| 936 | 973 |          // Quickstart
 | 
| 937 | 974 |          this._changeState(TorConnectState.Bootstrapping);
 | 
| ... | ... | @@ -1074,17 +1111,17 @@ export const TorConnect = (() => { | 
| 1074 | 1111 |          */
 | 
| 1075 | 1112 | |
| 1076 | 1113 |      beginBootstrap() {
 | 
| 1077 | -      console.log("TorConnect: beginBootstrap()");
 | |
| 1114 | +      lazy.logger.debug("TorConnect.beginBootstrap()");
 | |
| 1078 | 1115 |        this._changeState(TorConnectState.Bootstrapping);
 | 
| 1079 | 1116 |      },
 | 
| 1080 | 1117 | |
| 1081 | 1118 |      cancelBootstrap() {
 | 
| 1082 | -      console.log("TorConnect: cancelBootstrap()");
 | |
| 1119 | +      lazy.logger.debug("TorConnect.cancelBootstrap()");
 | |
| 1083 | 1120 |        this._changeState(TorConnectState.Configuring);
 | 
| 1084 | 1121 |      },
 | 
| 1085 | 1122 | |
| 1086 | 1123 |      beginAutoBootstrap(countryCode) {
 | 
| 1087 | -      console.log("TorConnect: beginAutoBootstrap()");
 | |
| 1124 | +      lazy.logger.debug("TorConnect.beginAutoBootstrap()");
 | |
| 1088 | 1125 |        this._changeState(TorConnectState.AutoBootstrapping, countryCode);
 | 
| 1089 | 1126 |      },
 | 
| 1090 | 1127 | |
| ... | ... | @@ -1154,7 +1191,10 @@ export const TorConnect = (() => { | 
| 1154 | 1191 |          await mrpc.init();
 | 
| 1155 | 1192 |          this._countryCodes = await mrpc.circumvention_countries();
 | 
| 1156 | 1193 |        } catch (err) {
 | 
| 1157 | -        console.log("An error occurred while fetching country codes", err);
 | |
| 1194 | +        lazy.logger.error(
 | |
| 1195 | +          "An error occurred while fetching country codes",
 | |
| 1196 | +          err
 | |
| 1197 | +        );
 | |
| 1158 | 1198 |        } finally {
 | 
| 1159 | 1199 |          mrpc.uninit();
 | 
| 1160 | 1200 |        }
 | 
| ... | ... | @@ -1187,8 +1227,8 @@ export const TorConnect = (() => { | 
| 1187 | 1227 |          uriArray = uriVariant;
 | 
| 1188 | 1228 |        } else {
 | 
| 1189 | 1229 |          // about:tor as safe fallback
 | 
| 1190 | -        console.error(
 | |
| 1191 | -          `TorConnect: received unknown variant '${JSON.stringify(uriVariant)}'`
 | |
| 1230 | +        lazy.logger.error(
 | |
| 1231 | +          `Received unknown variant '${JSON.stringify(uriVariant)}'`
 | |
| 1192 | 1232 |          );
 | 
| 1193 | 1233 |          uriArray = ["about:tor"];
 | 
| 1194 | 1234 |        }
 | 
| ... | ... | @@ -1209,9 +1249,7 @@ export const TorConnect = (() => { | 
| 1209 | 1249 |      // which redirect after bootstrapping
 | 
| 1210 | 1250 |      getURIsToLoad(uriVariant) {
 | 
| 1211 | 1251 |        const uris = this.fixupURIs(uriVariant);
 | 
| 1212 | -      console.log(
 | |
| 1213 | -        `TorConnect: Will load after bootstrap => [${uris.join(", ")}]`
 | |
| 1214 | -      );
 | |
| 1252 | +      lazy.logger.debug(`Will load after bootstrap => [${uris.join(", ")}]`);
 | |
| 1215 | 1253 |        return uris.map(uri => this.getRedirectURL(uri));
 | 
| 1216 | 1254 |      },
 | 
| 1217 | 1255 |    };
 | 
| ... | ... | @@ -6,10 +6,9 @@ const lazy = {}; | 
| 6 | 6 | |
| 7 | 7 |  ChromeUtils.defineESModuleGetters(lazy, {
 | 
| 8 | 8 |    TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
 | 
| 9 | -  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
 | |
| 10 | -  TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
 | |
| 11 | 9 |    Lox: "resource://gre/modules/Lox.sys.mjs",
 | 
| 12 | 10 |    TorParsers: "resource://gre/modules/TorParsers.sys.mjs",
 | 
| 11 | +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
 | |
| 13 | 12 |  });
 | 
| 14 | 13 | |
| 15 | 14 |  ChromeUtils.defineLazyGetter(lazy, "logger", () => {
 | 
| ... | ... | @@ -71,20 +70,6 @@ const TorSettingsPrefs = Object.freeze({ | 
| 71 | 70 |    },
 | 
| 72 | 71 |  });
 | 
| 73 | 72 | |
| 74 | -/* Config Keys used to configure tor daemon */
 | |
| 75 | -const TorConfigKeys = Object.freeze({
 | |
| 76 | -  useBridges: "UseBridges",
 | |
| 77 | -  bridgeList: "Bridge",
 | |
| 78 | -  socks4Proxy: "Socks4Proxy",
 | |
| 79 | -  socks5Proxy: "Socks5Proxy",
 | |
| 80 | -  socks5ProxyUsername: "Socks5ProxyUsername",
 | |
| 81 | -  socks5ProxyPassword: "Socks5ProxyPassword",
 | |
| 82 | -  httpsProxy: "HTTPSProxy",
 | |
| 83 | -  httpsProxyAuthenticator: "HTTPSProxyAuthenticator",
 | |
| 84 | -  reachableAddresses: "ReachableAddresses",
 | |
| 85 | -  clientTransportPlugin: "ClientTransportPlugin",
 | |
| 86 | -});
 | |
| 87 | - | |
| 88 | 73 |  export const TorBridgeSource = Object.freeze({
 | 
| 89 | 74 |    Invalid: -1,
 | 
| 90 | 75 |    BuiltIn: 0,
 | 
| ... | ... | @@ -322,7 +307,7 @@ class TorSettingsImpl { | 
| 322 | 307 |            if (!val) {
 | 
| 323 | 308 |              return;
 | 
| 324 | 309 |            }
 | 
| 325 | -          const bridgeStrings = this.#getBuiltinBridges(val);
 | |
| 310 | +          const bridgeStrings = this.getBuiltinBridges(val);
 | |
| 326 | 311 |            if (bridgeStrings.length) {
 | 
| 327 | 312 |              this.bridges.bridge_strings = bridgeStrings;
 | 
| 328 | 313 |              return;
 | 
| ... | ... | @@ -659,14 +644,17 @@ class TorSettingsImpl { | 
| 659 | 644 |     * @param {string} pt The pluggable transport to return the lines for
 | 
| 660 | 645 |     * @returns {string[]} The bridge lines in random order
 | 
| 661 | 646 |     */
 | 
| 662 | -  #getBuiltinBridges(pt) {
 | |
| 647 | +  getBuiltinBridges(pt) {
 | |
| 648 | +    if (!this.#allowUninitialized) {
 | |
| 649 | +      this.#checkIfInitialized();
 | |
| 650 | +    }
 | |
| 663 | 651 |      // Shuffle so that Tor Browser users do not all try the built-in bridges in
 | 
| 664 | 652 |      // the same order.
 | 
| 665 | 653 |      return arrayShuffle(this.#builtinBridges[pt] ?? []);
 | 
| 666 | 654 |    }
 | 
| 667 | 655 | |
| 668 | 656 |    /**
 | 
| 669 | -   * Load or init our settings, and register observers.
 | |
| 657 | +   * Load or init our settings.
 | |
| 670 | 658 |     */
 | 
| 671 | 659 |    async init() {
 | 
| 672 | 660 |      if (this.#initialized) {
 | 
| ... | ... | @@ -677,6 +665,7 @@ class TorSettingsImpl { | 
| 677 | 665 |        await this.#initInternal();
 | 
| 678 | 666 |        this.#initialized = true;
 | 
| 679 | 667 |        this.#initComplete();
 | 
| 668 | +      Services.obs.notifyObservers(null, TorSettingsTopics.Ready);
 | |
| 680 | 669 |      } catch (e) {
 | 
| 681 | 670 |        this.#initFailed(e);
 | 
| 682 | 671 |        throw e;
 | 
| ... | ... | @@ -698,45 +687,35 @@ class TorSettingsImpl { | 
| 698 | 687 |        lazy.logger.error("Could not load the built-in PT config.", e);
 | 
| 699 | 688 |      }
 | 
| 700 | 689 | |
| 701 | -    // Initialize this before loading from prefs because we need Lox initialized before
 | |
| 702 | -    // any calls to Lox.getBridges()
 | |
| 690 | +    // Initialize this before loading from prefs because we need Lox initialized
 | |
| 691 | +    // before any calls to Lox.getBridges().
 | |
| 703 | 692 |      try {
 | 
| 704 | 693 |        await lazy.Lox.init();
 | 
| 705 | 694 |      } catch (e) {
 | 
| 706 | 695 |        lazy.logger.error("Could not initialize Lox.", e.type);
 | 
| 707 | 696 |      }
 | 
| 708 | 697 | |
| 709 | -    // TODO: We could use a shared promise, and wait for it to be fullfilled
 | |
| 710 | -    // instead of Service.obs.
 | |
| 711 | -    if (lazy.TorLauncherUtil.shouldStartAndOwnTor) {
 | |
| 712 | -      // if the settings branch exists, load settings from prefs
 | |
| 713 | -      if (Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)) {
 | |
| 714 | -        // Do not want notifications for initially loaded prefs.
 | |
| 715 | -        this.freezeNotifications();
 | |
| 716 | -        try {
 | |
| 717 | -          this.#allowUninitialized = true;
 | |
| 718 | -          this.#loadFromPrefs();
 | |
| 719 | -        } finally {
 | |
| 720 | -          this.#allowUninitialized = false;
 | |
| 721 | -          this.#notificationQueue.clear();
 | |
| 722 | -          this.thawNotifications();
 | |
| 723 | -        }
 | |
| 724 | -      }
 | |
| 698 | +    if (
 | |
| 699 | +      lazy.TorLauncherUtil.shouldStartAndOwnTor &&
 | |
| 700 | +      Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)
 | |
| 701 | +    ) {
 | |
| 702 | +      // Do not want notifications for initially loaded prefs.
 | |
| 703 | +      this.freezeNotifications();
 | |
| 725 | 704 |        try {
 | 
| 726 | -        const provider = await lazy.TorProviderBuilder.build();
 | |
| 727 | -        if (provider.isRunning) {
 | |
| 728 | -          this.#handleProcessReady();
 | |
| 729 | -          // No need to add an observer to call this again.
 | |
| 730 | -          return;
 | |
| 731 | -        }
 | |
| 732 | -      } catch {}
 | |
| 733 | - | |
| 734 | -      Services.obs.addObserver(this, lazy.TorProviderTopics.ProcessIsReady);
 | |
| 705 | +        this.#allowUninitialized = true;
 | |
| 706 | +        this.#loadFromPrefs();
 | |
| 707 | +      } finally {
 | |
| 708 | +        this.#allowUninitialized = false;
 | |
| 709 | +        this.#notificationQueue.clear();
 | |
| 710 | +        this.thawNotifications();
 | |
| 711 | +      }
 | |
| 735 | 712 |      }
 | 
| 713 | + | |
| 714 | +    lazy.logger.info("Ready");
 | |
| 736 | 715 |    }
 | 
| 737 | 716 | |
| 738 | 717 |    /**
 | 
| 739 | -   * Unload or uninit our settings, and unregister observers.
 | |
| 718 | +   * Unload or uninit our settings.
 | |
| 740 | 719 |     */
 | 
| 741 | 720 |    async uninit() {
 | 
| 742 | 721 |      await lazy.Lox.uninit();
 | 
| ... | ... | @@ -764,34 +743,6 @@ class TorSettingsImpl { | 
| 764 | 743 |      return this.#initialized;
 | 
| 765 | 744 |    }
 | 
| 766 | 745 | |
| 767 | -  /**
 | |
| 768 | -   * Wait for relevant life-cycle events to apply saved settings.
 | |
| 769 | -   */
 | |
| 770 | -  async observe(subject, topic, data) {
 | |
| 771 | -    lazy.logger.debug(`Observed ${topic}`);
 | |
| 772 | - | |
| 773 | -    switch (topic) {
 | |
| 774 | -      case lazy.TorProviderTopics.ProcessIsReady:
 | |
| 775 | -        Services.obs.removeObserver(
 | |
| 776 | -          this,
 | |
| 777 | -          lazy.TorProviderTopics.ProcessIsReady
 | |
| 778 | -        );
 | |
| 779 | -        await this.#handleProcessReady();
 | |
| 780 | -        break;
 | |
| 781 | -    }
 | |
| 782 | -  }
 | |
| 783 | - | |
| 784 | -  /**
 | |
| 785 | -   * Apply the settings once the tor provider is ready and notify any observer
 | |
| 786 | -   * that the settings can be used.
 | |
| 787 | -   */
 | |
| 788 | -  async #handleProcessReady() {
 | |
| 789 | -    // push down settings to tor
 | |
| 790 | -    await this.#applySettings(true);
 | |
| 791 | -    lazy.logger.info("Ready");
 | |
| 792 | -    Services.obs.notifyObservers(null, TorSettingsTopics.Ready);
 | |
| 793 | -  }
 | |
| 794 | - | |
| 795 | 746 |    /**
 | 
| 796 | 747 |     * Load our settings from prefs.
 | 
| 797 | 748 |     */
 | 
| ... | ... | @@ -972,85 +923,14 @@ class TorSettingsImpl { | 
| 972 | 923 | |
| 973 | 924 |    /**
 | 
| 974 | 925 |     * Push our settings down to the tor provider.
 | 
| 926 | +   *
 | |
| 927 | +   * Even though this introduces a circular depdency, it makes the API nicer for
 | |
| 928 | +   * frontend consumers.
 | |
| 975 | 929 |     */
 | 
| 976 | 930 |    async applySettings() {
 | 
| 977 | 931 |      this.#checkIfInitialized();
 | 
| 978 | -    return this.#applySettings(false);
 | |
| 979 | -  }
 | |
| 980 | - | |
| 981 | -  /**
 | |
| 982 | -   * Internal implementation of applySettings that does not check if we are
 | |
| 983 | -   * initialized.
 | |
| 984 | -   */
 | |
| 985 | -  async #applySettings(allowUninitialized) {
 | |
| 986 | -    lazy.logger.debug("#applySettings()");
 | |
| 987 | - | |
| 988 | -    this.#cleanupSettings();
 | |
| 989 | - | |
| 990 | -    const settingsMap = new Map();
 | |
| 991 | - | |
| 992 | -    // #applySettings can be called only when #allowUninitialized is false
 | |
| 993 | -    this.#allowUninitialized = allowUninitialized;
 | |
| 994 | - | |
| 995 | -    try {
 | |
| 996 | -      /* Bridges */
 | |
| 997 | -      const haveBridges =
 | |
| 998 | -        this.bridges.enabled && !!this.bridges.bridge_strings.length;
 | |
| 999 | -      settingsMap.set(TorConfigKeys.useBridges, haveBridges);
 | |
| 1000 | -      if (haveBridges) {
 | |
| 1001 | -        settingsMap.set(TorConfigKeys.bridgeList, this.bridges.bridge_strings);
 | |
| 1002 | -      } else {
 | |
| 1003 | -        settingsMap.set(TorConfigKeys.bridgeList, null);
 | |
| 1004 | -      }
 | |
| 1005 | - | |
| 1006 | -      /* Proxy */
 | |
| 1007 | -      settingsMap.set(TorConfigKeys.socks4Proxy, null);
 | |
| 1008 | -      settingsMap.set(TorConfigKeys.socks5Proxy, null);
 | |
| 1009 | -      settingsMap.set(TorConfigKeys.socks5ProxyUsername, null);
 | |
| 1010 | -      settingsMap.set(TorConfigKeys.socks5ProxyPassword, null);
 | |
| 1011 | -      settingsMap.set(TorConfigKeys.httpsProxy, null);
 | |
| 1012 | -      settingsMap.set(TorConfigKeys.httpsProxyAuthenticator, null);
 | |
| 1013 | -      if (this.proxy.enabled) {
 | |
| 1014 | -        const address = this.proxy.address;
 | |
| 1015 | -        const port = this.proxy.port;
 | |
| 1016 | -        const username = this.proxy.username;
 | |
| 1017 | -        const password = this.proxy.password;
 | |
| 1018 | - | |
| 1019 | -        switch (this.proxy.type) {
 | |
| 1020 | -          case TorProxyType.Socks4:
 | |
| 1021 | -            settingsMap.set(TorConfigKeys.socks4Proxy, `${address}:${port}`);
 | |
| 1022 | -            break;
 | |
| 1023 | -          case TorProxyType.Socks5:
 | |
| 1024 | -            settingsMap.set(TorConfigKeys.socks5Proxy, `${address}:${port}`);
 | |
| 1025 | -            settingsMap.set(TorConfigKeys.socks5ProxyUsername, username);
 | |
| 1026 | -            settingsMap.set(TorConfigKeys.socks5ProxyPassword, password);
 | |
| 1027 | -            break;
 | |
| 1028 | -          case TorProxyType.HTTPS:
 | |
| 1029 | -            settingsMap.set(TorConfigKeys.httpsProxy, `${address}:${port}`);
 | |
| 1030 | -            settingsMap.set(
 | |
| 1031 | -              TorConfigKeys.httpsProxyAuthenticator,
 | |
| 1032 | -              `${username}:${password}`
 | |
| 1033 | -            );
 | |
| 1034 | -            break;
 | |
| 1035 | -        }
 | |
| 1036 | -      }
 | |
| 1037 | - | |
| 1038 | -      /* Firewall */
 | |
| 1039 | -      if (this.firewall.enabled) {
 | |
| 1040 | -        const reachableAddresses = this.firewall.allowed_ports
 | |
| 1041 | -          .map(port => `*:${port}`)
 | |
| 1042 | -          .join(",");
 | |
| 1043 | -        settingsMap.set(TorConfigKeys.reachableAddresses, reachableAddresses);
 | |
| 1044 | -      } else {
 | |
| 1045 | -        settingsMap.set(TorConfigKeys.reachableAddresses, null);
 | |
| 1046 | -      }
 | |
| 1047 | -    } finally {
 | |
| 1048 | -      this.#allowUninitialized = false;
 | |
| 1049 | -    }
 | |
| 1050 | - | |
| 1051 | -    /* Push to Tor */
 | |
| 1052 | 932 |      const provider = await lazy.TorProviderBuilder.build();
 | 
| 1053 | -    await provider.writeSettings(settingsMap);
 | |
| 933 | +    await provider.writeSettings(this.getSettings());
 | |
| 1054 | 934 |    }
 | 
| 1055 | 935 | |
| 1056 | 936 |    /**
 |