[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-150.0a1-16.0-2] 8 commits: fixup! TB 40933: Add tor-launcher functionality



Title: GitLab

henry pushed to branch tor-browser-150.0a1-16.0-2 at The Tor Project / Applications / Tor Browser

Commits:

  • b61277be
    by Henry Wilkes at 2026-04-28T12:46:37+01:00
    fixup! TB 40933: Add tor-launcher functionality
    
    TB 44796: Convert `TorProviders` types to strings.
    
    This allows them to be used in log messages.
    
  • 6a41ffd8
    by Henry Wilkes at 2026-04-28T12:53:36+01:00
    fixup! TB 40933: Add tor-launcher functionality
    
    TB 44796: Add `TorProviderState`, which tracks the state of a provider.
    
    We create a new `TorProviderBase` class which tracks this state, and the
    existing `TorProvider` now extends.
    
    `TorProviderBuilder.sys.mjs`:
    + We provide a callback to let the current provider instance let
      `TorProviderBuilder` know when this state has changed.
    + This will emit `TorProviderSateChanged` as a replacement for
      `TorProcessExited`. The `Stopped` state has the same role as the
      previous event, except it is more generic. This event also lets any
      consumers know when a provider has been just replaced (`Starting`) and
      successfully initialised (`Running`).
    
    `TorControlPort.sys.mjs`:
    + Add `onClosed` callback to signal to the `TorProvider` that the
      control port has closed, which will trigger `_stoppedInternal`.
    + Delay adding the event handler so the `TorProvider` only listens to a
      control port after it is successfully set up and adopted by the
      `TorProvider`.
    
    `TorProvider.sys.mjs`:
    + `TorProvider` no longer emits `TorProviderTopics.ProcessExited`.
    + Instead, `TorProvider` calls `_stoppedInternal` whenever the provider
      stops running. I.e. whenever the control port is closed, which
      includes when the process exits but also other scenarios where the
      control port might close unexpectedly.
    
  • 7167229f
    by Henry Wilkes at 2026-04-28T12:53:37+01:00
    fixup! TB 40933: Add tor-launcher functionality
    
    TB 44796: Convert the "none" provider into a `TorProviderNone` class, so
    it can be handled by the same logic as `TorProvider`.
    
  • adba934e
    by Henry Wilkes at 2026-04-28T12:53:39+01:00
    fixup! TB 40933: Add tor-launcher functionality
    
    TB 44796: Add the `settledState` method to allow `TorConnect` to wait
    for the first provider's state after it has settled. This is a way to
    get the current providers state prior to having listened to
    `TorProviderStateChange`.
    
    Add the `replace` method to allow `TorConnect` to request a new provider
    on the user's behalf.
    
  • e74c8d84
    by Henry Wilkes at 2026-04-28T12:53:40+01:00
    fixup! TB 40933: Add tor-launcher functionality
    
    TB 44796: Drop `#torExited` and internals of `firstWindowLoaded` and
    instead use promptProviderState which handles both. These are only
    temporary changes to be dropped in tor-browser#43570.
    
  • 615707ff
    by Henry Wilkes at 2026-04-28T12:53:41+01:00
    fixup! TB 40597: Implement TorSettings module
    
    TB 44796: Listen for ProviderStateChanged rather than ProcessStopped.
    
  • 48f5bfb2
    by Henry Wilkes at 2026-04-28T12:53:42+01:00
    fixup! TB 40933: Add tor-launcher functionality
    
    TB 44796: Drop `TorProvider.isRunning`.
    
  • 424f8e1a
    by Henry Wilkes at 2026-04-28T12:53:43+01:00
    fixup! TB 40597: Implement TorSettings module
    
    TB 44796: Use `TorProvider.state` instead of `isRunning`.
    

8 changed files:

Changes:

  • toolkit/components/tor-launcher/TorControlPort.sys.mjs
    ... ... @@ -384,21 +384,19 @@ export class TorController {
    384 384
       /**
    
    385 385
        * The event handler.
    
    386 386
        *
    
    387
    -   * @type {TorEventHandler}
    
    387
    +   * @type {?TorEventHandler}
    
    388 388
        */
    
    389
    -  #eventHandler;
    
    389
    +  #eventHandler = null;
    
    390 390
     
    
    391 391
       /**
    
    392 392
        * Connect to a control port over a Unix socket.
    
    393 393
        * Not available on Windows.
    
    394 394
        *
    
    395 395
        * @param {nsIFile} ipcFile The path to the Unix socket to connect to
    
    396
    -   * @param {TorEventHandler} eventHandler The event handler to use for
    
    397
    -   * asynchronous notifications
    
    398 396
        * @returns {TorController}
    
    399 397
        */
    
    400
    -  static fromIpcFile(ipcFile, eventHandler) {
    
    401
    -    return new TorController(AsyncSocket.fromIpcFile(ipcFile), eventHandler);
    
    398
    +  static fromIpcFile(ipcFile) {
    
    399
    +    return new TorController(AsyncSocket.fromIpcFile(ipcFile));
    
    402 400
       }
    
    403 401
     
    
    404 402
       /**
    
    ... ... @@ -406,15 +404,10 @@ export class TorController {
    406 404
        *
    
    407 405
        * @param {string} host The hostname to connect to
    
    408 406
        * @param {number} port The port to connect the to
    
    409
    -   * @param {TorEventHandler} eventHandler The event handler to use for
    
    410
    -   * asynchronous notifications
    
    411 407
        * @returns {TorController}
    
    412 408
        */
    
    413
    -  static fromSocketAddress(host, port, eventHandler) {
    
    414
    -    return new TorController(
    
    415
    -      AsyncSocket.fromSocketAddress(host, port),
    
    416
    -      eventHandler
    
    417
    -    );
    
    409
    +  static fromSocketAddress(host, port) {
    
    410
    +    return new TorController(AsyncSocket.fromSocketAddress(host, port));
    
    418 411
       }
    
    419 412
     
    
    420 413
       /**
    
    ... ... @@ -425,15 +418,22 @@ export class TorController {
    425 418
        *
    
    426 419
        * @private
    
    427 420
        * @param {AsyncSocket} socket The socket to use
    
    428
    -   * @param {TorEventHandler} eventHandler The event handler to use for
    
    429
    -   * asynchronous notifications
    
    430 421
        */
    
    431
    -  constructor(socket, eventHandler) {
    
    422
    +  constructor(socket) {
    
    432 423
         this.#socket = socket;
    
    433
    -    this.#eventHandler = eventHandler;
    
    434 424
         this.#startMessagePump();
    
    435 425
       }
    
    436 426
     
    
    427
    +  /**
    
    428
    +   * Set an event handler for this instance.
    
    429
    +   *
    
    430
    +   * @param {TorEventHandler} eventHandler The event handler to use for
    
    431
    +   *   asynchronous notifications
    
    432
    +   */
    
    433
    +  setEventHandler(eventHandler) {
    
    434
    +    this.#eventHandler = eventHandler;
    
    435
    +  }
    
    436
    +
    
    437 437
       // Socket and communication handling
    
    438 438
     
    
    439 439
       /**
    
    ... ... @@ -649,6 +649,7 @@ export class TorController {
    649 649
           this.#socket?.close();
    
    650 650
         } finally {
    
    651 651
           this.#socket = null;
    
    652
    +      this.#eventHandler?.onClosed();
    
    652 653
         }
    
    653 654
       }
    
    654 655
     
    
    ... ... @@ -1324,6 +1325,7 @@ export class TorController {
    1324 1325
      * The controller owner can implement this methods to receive asynchronous
    
    1325 1326
      * notifications from the controller.
    
    1326 1327
      *
    
    1328
    + * @property {OnClosed} onClosed Called when the socket is closed.
    
    1327 1329
      * @property {OnBootstrapStatus} onBootstrapStatus Called when a bootstrap
    
    1328 1330
      * status is received (i.e., a STATUS_CLIENT event with a BOOTSTRAP action)
    
    1329 1331
      * @property {OnLogMessage} onLogMessage Called when a log message is received
    
    ... ... @@ -1336,6 +1338,9 @@ export class TorController {
    1336 1338
      * a connect cell along a circuit (i.e., a STREAM event with a SENTCONNECT
    
    1337 1339
      * status)
    
    1338 1340
      */
    
    1341
    +/**
    
    1342
    + * @callback OnClosed
    
    1343
    + */
    
    1339 1344
     /**
    
    1340 1345
      * @callback OnBootstrapStatus
    
    1341 1346
      *
    

  • toolkit/components/tor-launcher/TorProvider.sys.mjs
    ... ... @@ -6,6 +6,7 @@ import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs";
    6 6
     
    
    7 7
     import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
    
    8 8
     import { TorParsers } from "resource://gre/modules/TorParsers.sys.mjs";
    
    9
    +import { TorProviderBase } from "resource://gre/modules/TorProviderBase.sys.mjs";
    
    9 10
     import {
    
    10 11
       TorBootstrapError,
    
    11 12
       TorProviderTopics,
    
    ... ... @@ -88,7 +89,7 @@ const TorConfigKeys = Object.freeze({
    88 89
      * It can start a new tor instance, or connect to an existing one.
    
    89 90
      * In the former case, it also takes its ownership by default.
    
    90 91
      */
    
    91
    -export class TorProvider {
    
    92
    +export class TorProvider extends TorProviderBase {
    
    92 93
       /**
    
    93 94
        * The control port settings.
    
    94 95
        *
    
    ... ... @@ -184,7 +185,7 @@ export class TorProvider {
    184 185
        * Starts a new tor process and connect to its control port, or connect to the
    
    185 186
        * control port of an existing tor daemon.
    
    186 187
        */
    
    187
    -  async init() {
    
    188
    +  async _initInternal() {
    
    188 189
         logger.debug("Initializing the Tor provider.");
    
    189 190
     
    
    190 191
         // These settings might be customized in the following steps.
    
    ... ... @@ -259,7 +260,7 @@ export class TorProvider {
    259 260
        * control connection is closed. Therefore, as a matter of facts, calling this
    
    260 261
        * function also makes the child Tor instance stop.
    
    261 262
        */
    
    262
    -  uninit() {
    
    263
    +  async _uninitInternal() {
    
    263 264
         logger.debug("Uninitializing the Tor provider.");
    
    264 265
     
    
    265 266
         if (this.#torProcess) {
    
    ... ... @@ -509,17 +510,6 @@ export class TorProvider {
    509 510
         return TorLauncherUtil.shouldStartAndOwnTor;
    
    510 511
       }
    
    511 512
     
    
    512
    -  /**
    
    513
    -   * TODO: Rename to isReady once we remove finish the migration.
    
    514
    -   *
    
    515
    -   * @returns {boolean} true if we currently have a connection to the control
    
    516
    -   * port. We take for granted that if we have one, we authenticated to it, and
    
    517
    -   * so we have already verified we can send and receive data.
    
    518
    -   */
    
    519
    -  get isRunning() {
    
    520
    -    return this.#controlConnection?.isOpen ?? false;
    
    521
    -  }
    
    522
    -
    
    523 513
       /**
    
    524 514
        * Return the data about the current bridge, if any, or null.
    
    525 515
        * We can detect bridge only when the configured bridge lines include the
    
    ... ... @@ -740,6 +730,11 @@ export class TorProvider {
    740 730
           };
    
    741 731
           tryConnect();
    
    742 732
         });
    
    733
    +    // The previous TorControlPort instances may have failed, in which case we
    
    734
    +    // want to ignore their events (such as `onClose`). This instance has just
    
    735
    +    // succeeded and will be owned by this TorProvider instance, therefore we
    
    736
    +    // want to start listening to its events now.
    
    737
    +    this.#controlConnection.setEventHandler(this);
    
    743 738
     
    
    744 739
         // The following code will never throw, but we still want to wait for it
    
    745 740
         // before marking the provider as initialized.
    
    ... ... @@ -751,7 +746,6 @@ export class TorProvider {
    751 746
           this.#torProcess.onExit = exitCode => {
    
    752 747
             logger.info(`The tor process exited with code ${exitCode}`);
    
    753 748
             this.#closeConnection("The tor process exited suddenly");
    
    754
    -        Services.obs.notifyObservers(null, TorProviderTopics.ProcessExited);
    
    755 749
           };
    
    756 750
           if (!TorLauncherUtil.shouldOnlyConfigureTor) {
    
    757 751
             await this.#takeOwnership();
    
    ... ... @@ -823,14 +817,12 @@ export class TorProvider {
    823 817
         let controlPort;
    
    824 818
         if (this.#controlPortSettings.ipcFile) {
    
    825 819
           controlPort = lazy.TorController.fromIpcFile(
    
    826
    -        this.#controlPortSettings.ipcFile,
    
    827
    -        this
    
    820
    +        this.#controlPortSettings.ipcFile
    
    828 821
           );
    
    829 822
         } else {
    
    830 823
           controlPort = lazy.TorController.fromSocketAddress(
    
    831 824
             this.#controlPortSettings.host,
    
    832
    -        this.#controlPortSettings.port,
    
    833
    -        this
    
    825
    +        this.#controlPortSettings.port
    
    834 826
           );
    
    835 827
         }
    
    836 828
         try {
    
    ... ... @@ -864,21 +856,27 @@ export class TorProvider {
    864 856
        * attempt)
    
    865 857
        */
    
    866 858
       #closeConnection(reason) {
    
    867
    -    this.#cancelConnection(reason);
    
    868
    -    if (this.#controlConnection) {
    
    869
    -      logger.info("Closing the control connection", reason);
    
    870
    -      try {
    
    871
    -        this.#controlConnection.close();
    
    872
    -      } catch (e) {
    
    873
    -        logger.error("Failed to close the control port connection", e);
    
    859
    +    try {
    
    860
    +      this.#cancelConnection(reason);
    
    861
    +      if (this.#controlConnection) {
    
    862
    +        logger.info("Closing the control connection", reason);
    
    863
    +        try {
    
    864
    +          this.#controlConnection.close();
    
    865
    +        } catch (e) {
    
    866
    +          logger.error("Failed to close the control port connection", e);
    
    867
    +        }
    
    868
    +        this.#controlConnection = null;
    
    869
    +      } else {
    
    870
    +        logger.trace(
    
    871
    +          "Requested to close an already closed control port connection"
    
    872
    +        );
    
    874 873
           }
    
    875
    -      this.#controlConnection = null;
    
    876
    -    } else {
    
    877
    -      logger.trace(
    
    878
    -        "Requested to close an already closed control port connection"
    
    879
    -      );
    
    874
    +    } finally {
    
    875
    +      this.#lastWarning = {};
    
    876
    +      // #controlConnection.close() may already trigger onClosed, but we
    
    877
    +      // call it once more for error cases as well.
    
    878
    +      this._stoppedInternal();
    
    880 879
         }
    
    881
    -    this.#lastWarning = {};
    
    882 880
       }
    
    883 881
     
    
    884 882
       // Authentication
    
    ... ... @@ -975,6 +973,13 @@ export class TorProvider {
    975 973
     
    
    976 974
       // Notification handlers
    
    977 975
     
    
    976
    +  /**
    
    977
    +   * Notification that the control port has closed.
    
    978
    +   */
    
    979
    +  onClosed() {
    
    980
    +    this._stoppedInternal();
    
    981
    +  }
    
    982
    +
    
    978 983
       /**
    
    979 984
        * Receive and process a notification with the bootstrap status.
    
    980 985
        *
    

  • toolkit/components/tor-launcher/TorProviderBase.sys.mjs
    1
    +import {
    
    2
    +  TorProviderState,
    
    3
    +  TorProviderInitError,
    
    4
    +} from "resource://gre/modules/TorProviderBuilder.sys.mjs";
    
    5
    +
    
    6
    +/**
    
    7
    + * @callback StateChangedCallback
    
    8
    + */
    
    9
    +
    
    10
    +/**
    
    11
    + * The base class for tor providers.
    
    12
    + */
    
    13
    +export class TorProviderBase {
    
    14
    +  /**
    
    15
    +   * The current provider state.
    
    16
    +   *
    
    17
    +   * @type {string}
    
    18
    +   */
    
    19
    +  #state = TorProviderState.Starting;
    
    20
    +
    
    21
    +  /**
    
    22
    +   * The callback for when our state changes. `null` once the `uninit` method is
    
    23
    +   * called.
    
    24
    +   *
    
    25
    +   * @type {?StateChangedCallback}
    
    26
    +   */
    
    27
    +  #stateChangedCallback;
    
    28
    +
    
    29
    +  /**
    
    30
    +   * The promise to return from the `init` method.
    
    31
    +   *
    
    32
    +   * @type {?Promise<undefined>}
    
    33
    +   */
    
    34
    +  #initPromise = null;
    
    35
    +
    
    36
    +  /**
    
    37
    +   * The promise to return from the `uninit` method.
    
    38
    +   *
    
    39
    +   * @type {?Promise<undefined>}
    
    40
    +   */
    
    41
    +  #uninitPromise = null;
    
    42
    +
    
    43
    +  /**
    
    44
    +   * Create a new provider.
    
    45
    +   *
    
    46
    +   * @param {StateChangedCallback} stateChangedCallback - A callback to let an
    
    47
    +   *   owner know that a provider's state has changed.
    
    48
    +   */
    
    49
    +  // NOTE: This should *not* be overridden by implementations.
    
    50
    +  constructor(stateChangedCallback) {
    
    51
    +    this.#stateChangedCallback = stateChangedCallback;
    
    52
    +  }
    
    53
    +
    
    54
    +  /**
    
    55
    +   * Initialize the provider and wait for it to be `Running`.
    
    56
    +   *
    
    57
    +   * This is safe to call multiple times, with each call waiting for the
    
    58
    +   * provider to be ready.
    
    59
    +   */
    
    60
    +  // NOTE: This should *not* be overridden by implementations.
    
    61
    +  async init() {
    
    62
    +    if (!this.#initPromise) {
    
    63
    +      this.#initPromise = this._initInternal().then(
    
    64
    +        () => {
    
    65
    +          this.#setState(TorProviderState.Running);
    
    66
    +        },
    
    67
    +        error => {
    
    68
    +          this.#setState(TorProviderState.Stopped);
    
    69
    +          // Wrap the error in `TorProviderInitError` to let callers know that
    
    70
    +          // this error is an initialization error.
    
    71
    +          throw new TorProviderInitError(error);
    
    72
    +        }
    
    73
    +      );
    
    74
    +    }
    
    75
    +    return this.#initPromise;
    
    76
    +  }
    
    77
    +
    
    78
    +  /**
    
    79
    +   * Uninitialize the provider and wait for it to be cleaned up.
    
    80
    +   *
    
    81
    +   * This is safe to call multiple times, with each call waiting for the
    
    82
    +   * clean up.
    
    83
    +   */
    
    84
    +  // NOTE: This should *not* be overridden by implementations.
    
    85
    +  async uninit() {
    
    86
    +    if (!this.#uninitPromise) {
    
    87
    +      this.#setState(TorProviderState.Stopped);
    
    88
    +      this.#stateChangedCallback = null;
    
    89
    +      this.#uninitPromise = this._uninitInternal();
    
    90
    +    }
    
    91
    +    return this.#uninitPromise;
    
    92
    +  }
    
    93
    +
    
    94
    +  /**
    
    95
    +   * The current `TorProviderState` state of the provider.
    
    96
    +   *
    
    97
    +   * @type {string}
    
    98
    +   */
    
    99
    +  // NOTE: This should *not* be overridden by implementations.
    
    100
    +  get state() {
    
    101
    +    return this.#state;
    
    102
    +  }
    
    103
    +
    
    104
    +  /**
    
    105
    +   * Set the state of the provider. Announcing this via a callback.
    
    106
    +   *
    
    107
    +   * @param {string} state - The new `TorProviderState`.
    
    108
    +   */
    
    109
    +  #setState(state) {
    
    110
    +    if (state === this.#state) {
    
    111
    +      return;
    
    112
    +    }
    
    113
    +    if (this.#state === TorProviderState.Stopped) {
    
    114
    +      // Ignore any changes away from the `Stopped` state, which is unexpected
    
    115
    +      // since implementations can only trigger the `Stopped` state, and
    
    116
    +      // everything else is handled by `TorProviderBase`.
    
    117
    +      return;
    
    118
    +    }
    
    119
    +    this.#state = state;
    
    120
    +    this.#stateChangedCallback?.();
    
    121
    +  }
    
    122
    +
    
    123
    +  /**
    
    124
    +   * An internal method to be called by implementations when they stop working.
    
    125
    +   *
    
    126
    +   * Optional and safe to call as part of `_uninitInternal`.
    
    127
    +   */
    
    128
    +  // NOTE: This should *not* be overridden by implementations.
    
    129
    +  _stoppedInternal() {
    
    130
    +    this.#setState(TorProviderState.Stopped);
    
    131
    +  }
    
    132
    +
    
    133
    +  // Implementation methods.
    
    134
    +
    
    135
    +  /**
    
    136
    +   * An internal initialization method for provider instances to implement.
    
    137
    +   */
    
    138
    +  async _initInternal() {
    
    139
    +    throw new Error("_initInternal not implemented.");
    
    140
    +  }
    
    141
    +
    
    142
    +  /**
    
    143
    +   * An internal uninitialization method for provider instances to implement.
    
    144
    +   */
    
    145
    +  async _uninitInternal() {
    
    146
    +    throw new Error("_uninitInternal not implemented.");
    
    147
    +  }
    
    148
    +
    
    149
    +  async writeBridgeSettings(_bridges) {
    
    150
    +    throw new Error("writeBridgeSettings not implemented.");
    
    151
    +  }
    
    152
    +
    
    153
    +  async writeProxySettings(_proxy) {
    
    154
    +    throw new Error("writeProxySettings not implemented.");
    
    155
    +  }
    
    156
    +
    
    157
    +  async writeFirewallSettings(_firewall) {
    
    158
    +    throw new Error("writeFirewallSettings not implemented.");
    
    159
    +  }
    
    160
    +
    
    161
    +  async flushSettings() {
    
    162
    +    throw new Error("flushSettings not implemented.");
    
    163
    +  }
    
    164
    +
    
    165
    +  async connect() {
    
    166
    +    throw new Error("connect not implemented.");
    
    167
    +  }
    
    168
    +
    
    169
    +  async stopBootstrap() {
    
    170
    +    throw new Error("stopBootstrap not implemented.");
    
    171
    +  }
    
    172
    +
    
    173
    +  async newnym() {
    
    174
    +    throw new Error("newnym not implemented.");
    
    175
    +  }
    
    176
    +
    
    177
    +  async getBridges() {
    
    178
    +    throw new Error("getBridges not implemented.");
    
    179
    +  }
    
    180
    +
    
    181
    +  async getPluggableTransports() {
    
    182
    +    throw new Error("getPluggableTransports not implemented.");
    
    183
    +  }
    
    184
    +
    
    185
    +  async onionAuthAdd(_address, _b64PrivateKey, _isPermanent) {
    
    186
    +    throw new Error("onionAuthAdd not implemented.");
    
    187
    +  }
    
    188
    +
    
    189
    +  async onionAuthRemove(_address) {
    
    190
    +    throw new Error("onionAuthRemove not implemented.");
    
    191
    +  }
    
    192
    +
    
    193
    +  async onionAuthViewKeys() {
    
    194
    +    throw new Error("onionAuthViewKeys not implemented.");
    
    195
    +  }
    
    196
    +
    
    197
    +  get currentBridge() {
    
    198
    +    throw new Error("currentBridge not implemented.");
    
    199
    +  }
    
    200
    +}

  • toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
    ... ... @@ -6,10 +6,19 @@ const lazy = {};
    6 6
     ChromeUtils.defineESModuleGetters(lazy, {
    
    7 7
       TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
    
    8 8
       TorProvider: "resource://gre/modules/TorProvider.sys.mjs",
    
    9
    +  TorProviderNone: "resource://gre/modules/TorProviderNone.sys.mjs",
    
    10
    +});
    
    11
    +
    
    12
    +ChromeUtils.defineLazyGetter(lazy, "logger", () => {
    
    13
    +  return console.createInstance({
    
    14
    +    // Share preference with TorProvider.
    
    15
    +    maxLogLevelPref: "browser.tor_provider.log_level",
    
    16
    +    prefix: "TorProviderBuilder",
    
    17
    +  });
    
    9 18
     });
    
    10 19
     
    
    11 20
     export const TorProviderTopics = Object.freeze({
    
    12
    -  ProcessExited: "TorProcessExited",
    
    21
    +  ProviderStateChanged: "TorProviderStateChanged",
    
    13 22
       BootstrapStatus: "TorBootstrapStatus",
    
    14 23
       BootstrapError: "TorBootstrapError",
    
    15 24
       TorLog: "TorLog",
    
    ... ... @@ -18,6 +27,15 @@ export const TorProviderTopics = Object.freeze({
    18 27
       CircuitCredentialsMatched: "TorCircuitCredentialsMatched",
    
    19 28
     });
    
    20 29
     
    
    30
    +/**
    
    31
    + * The tracked state a provider might be in.
    
    32
    + */
    
    33
    +export const TorProviderState = Object.freeze({
    
    34
    +  Starting: "Starting",
    
    35
    +  Running: "Running",
    
    36
    +  Stopped: "Stopped",
    
    37
    +});
    
    38
    +
    
    21 39
     /**
    
    22 40
      * Wrapper error class for errors raised during TorProvider.init.
    
    23 41
      */
    
    ... ... @@ -54,8 +72,8 @@ export class TorBootstrapError extends Error {
    54 72
     }
    
    55 73
     
    
    56 74
     export const TorProviders = Object.freeze({
    
    57
    -  none: 0,
    
    58
    -  tor: 1,
    
    75
    +  none: "none",
    
    76
    +  tor: "tor",
    
    59 77
     });
    
    60 78
     
    
    61 79
     /**
    
    ... ... @@ -65,6 +83,17 @@ export const TorProviders = Object.freeze({
    65 83
      * @property {string} msg The message
    
    66 84
      */
    
    67 85
     
    
    86
    +/**
    
    87
    + * @typedef {object} TorProviderData
    
    88
    + *
    
    89
    + * The data associated with a tor provider.
    
    90
    + *
    
    91
    + * @property {TorProviderBase} provider - The provider instance.
    
    92
    + * @property {Promise<undefined>} initPromise - A promise that fulfils after the
    
    93
    + *   previous provider is cleaned up and the new provider's initialization
    
    94
    + *   completes or throws an error.
    
    95
    + */
    
    96
    +
    
    68 97
     /**
    
    69 98
      * The factory to get a Tor provider.
    
    70 99
      * Currently we support only TorProvider, i.e., the one that interacts with
    
    ... ... @@ -72,14 +101,35 @@ export const TorProviders = Object.freeze({
    72 101
      */
    
    73 102
     export class TorProviderBuilder {
    
    74 103
       /**
    
    75
    -   * A promise with the instance of the provider that we are using.
    
    104
    +   * Data about the current provider instance.
    
    76 105
        *
    
    77
    -   * @type {Promise<TorProvider>?}
    
    106
    +   * @type {?TorProviderData}
    
    78 107
        */
    
    79
    -  static #provider = null;
    
    108
    +  static #providerData = null;
    
    80 109
     
    
    81 110
       /**
    
    82
    -   * A record of the log messages from all TorProvider instances.
    
    111
    +   * Whether the `uninit` method has been called.
    
    112
    +   *
    
    113
    +   * @type {boolean}
    
    114
    +   */
    
    115
    +  static #uninitialized = false;
    
    116
    +
    
    117
    +  /**
    
    118
    +   * Check that we are active before a public call.
    
    119
    +   *
    
    120
    +   * @throws {Error} Throws if we are not active.
    
    121
    +   */
    
    122
    +  static #checkActive() {
    
    123
    +    if (this.#uninitialized) {
    
    124
    +      throw new Error("TorProviderBuilder has already been uninitialized.");
    
    125
    +    }
    
    126
    +    if (!this.#providerData) {
    
    127
    +      throw new Error("TorProviderBuilder has not been initialized.");
    
    128
    +    }
    
    129
    +  }
    
    130
    +
    
    131
    +  /**
    
    132
    +   * A record of the log messages from all provider instances.
    
    83 133
        *
    
    84 134
        * @type {LogEntry[]}
    
    85 135
        */
    
    ... ... @@ -120,22 +170,6 @@ export class TorProviderBuilder {
    120 170
         this.#log.push(logEntry);
    
    121 171
       }
    
    122 172
     
    
    123
    -  /**
    
    124
    -   * The observer that checks when the tor process exits, and reinitializes the
    
    125
    -   * provider.
    
    126
    -   *
    
    127
    -   * @type {Function}
    
    128
    -   */
    
    129
    -  static #exitObserver = null;
    
    130
    -
    
    131
    -  /**
    
    132
    -   * Tell whether the browser UI is ready.
    
    133
    -   * We ignore any errors until it is because we cannot show them.
    
    134
    -   *
    
    135
    -   * @type {boolean}
    
    136
    -   */
    
    137
    -  static #uiReady = false;
    
    138
    -
    
    139 173
       /**
    
    140 174
        * Initialize the provider of choice.
    
    141 175
        */
    
    ... ... @@ -149,115 +183,214 @@ export class TorProviderBuilder {
    149 183
         };
    
    150 184
         Services.obs.addObserver(this.#logObserver, TorProviderTopics.TorLog);
    
    151 185
     
    
    186
    +    // Even though initialization of the initial provider is asynchronous, we do
    
    187
    +    // not expect the caller to await it. The reason is that any call to build()
    
    188
    +    // will wait the initialization anyway (and re-throw any initialization
    
    189
    +    // error).
    
    190
    +    this.#replaceProvider();
    
    191
    +  }
    
    192
    +
    
    193
    +  /**
    
    194
    +   * Replace the provider with a new instance.
    
    195
    +   */
    
    196
    +  static #replaceProvider() {
    
    197
    +    // NOTE: We need to ensure that the #providerData is set as soon as
    
    198
    +    // TorProviderBuilder.init is called.
    
    199
    +    // I.e. it should be safe to call
    
    200
    +    //   TorProviderBuilder.init();
    
    201
    +    //   TorProviderBuilder.build();
    
    202
    +    //   TorProviderBuilder.settledState();
    
    203
    +    //   // etc
    
    204
    +    // without any await.
    
    205
    +    //
    
    206
    +    // In particular, this is needed by `TorConnect.init`, which will call
    
    207
    +    // `settledState`. It will also call `build` immediately if quickstart is
    
    208
    +    // set. See tor-browser#41921.
    
    209
    +    if (this.#providerData) {
    
    210
    +      lazy.logger.info(
    
    211
    +        `Replacing the provider with a "${this.providerType}" provider.`
    
    212
    +      );
    
    213
    +    } else {
    
    214
    +      lazy.logger.info(`Creating the initial "${this.providerType}" provider.`);
    
    215
    +    }
    
    216
    +
    
    217
    +    let providerClass;
    
    152 218
         switch (this.providerType) {
    
    153 219
           case TorProviders.tor:
    
    154
    -        // Even though initialization of the initial TorProvider is
    
    155
    -        // asynchronous, we do not expect the caller to await it. The reason is
    
    156
    -        // that any call to build() will wait the initialization anyway (and
    
    157
    -        // re-throw any initialization error).
    
    158
    -        this.#initTorProvider();
    
    220
    +        providerClass = lazy.TorProvider;
    
    159 221
             break;
    
    160 222
           case TorProviders.none:
    
    161
    -        lazy.TorLauncherUtil.setProxyConfiguration(
    
    162
    -          lazy.TorLauncherUtil.getPreferredSocksConfiguration()
    
    163
    -        );
    
    223
    +        providerClass = lazy.TorProviderNone;
    
    164 224
             break;
    
    165 225
           default:
    
    166
    -        console.error(`Unknown tor provider ${this.providerType}.`);
    
    226
    +        lazy.logger.error(`Unknown tor provider ${this.providerType}.`);
    
    167 227
             break;
    
    168 228
         }
    
    229
    +    // NOTE: It should be safe to create another provider instance whilst the
    
    230
    +    // existing one is still active. However, we will wait until the other is
    
    231
    +    // uninitialized before we initialize the new one.
    
    232
    +    const provider = new providerClass(() => {
    
    233
    +      this.#notifyStateChanged(provider);
    
    234
    +    });
    
    235
    +    const prevProviderData = this.#providerData;
    
    236
    +    // NOTE: We want `#providerData` to be set prior to our call to
    
    237
    +    // `provider.init`, so we create the `initPromise` prior to setting it.
    
    238
    +    const { promise: initPromise, resolve, reject } = Promise.withResolvers();
    
    239
    +    this.#providerData = { provider, initPromise };
    
    240
    +    // Let observers know we are restarting the provider.
    
    241
    +    this.#notifyStateChanged(provider);
    
    242
    +
    
    243
    +    // Run the rest of the init in an async operation that will cause
    
    244
    +    // `initPromise` to settle.
    
    245
    +    // NOTE: `#cleanupProviderData` should not throw, unlike `provider.init()`,
    
    246
    +    // which may throw.
    
    247
    +    // NOTE: We wait for `#cleanupProviderData` to complete before calling
    
    248
    +    // `provider.init()` in case the implementation relies on this.
    
    249
    +    this.#cleanupProviderData(prevProviderData).finally(() => {
    
    250
    +      provider.init().then(resolve, reject);
    
    251
    +    });
    
    169 252
       }
    
    170 253
     
    
    171 254
       /**
    
    172
    -   * Replace #provider with a new instance.
    
    255
    +   * Notify any listeners that the state of the current provider has changed.
    
    173 256
        *
    
    174
    -   * @returns {Promise<TorProvider>} The new instance.
    
    257
    +   * @param {TorProviderBase} provider - The provider who's state has changed.
    
    175 258
        */
    
    176
    -  static #initTorProvider() {
    
    177
    -    if (!this.#exitObserver) {
    
    178
    -      this.#exitObserver = this.#torExited.bind(this);
    
    179
    -      Services.obs.addObserver(
    
    180
    -        this.#exitObserver,
    
    181
    -        TorProviderTopics.ProcessExited
    
    182
    -      );
    
    259
    +  static #notifyStateChanged(provider) {
    
    260
    +    if (this.#uninitialized) {
    
    261
    +      // Do not signal the final state changes when we uninitialize.
    
    262
    +      return;
    
    263
    +    }
    
    264
    +    if (provider !== this.#providerData.provider) {
    
    265
    +      // Delayed call from an old provider. Ignore.
    
    266
    +      return;
    
    183 267
         }
    
    184 268
     
    
    185
    -    // NOTE: We need to ensure that the #provider is set as soon
    
    186
    -    // TorProviderBuilder.init is called.
    
    187
    -    // I.e. it should be safe to call
    
    188
    -    //   TorProviderBuilder.init();
    
    189
    -    //   TorProviderBuilder.build();
    
    190
    -    // without any await.
    
    191
    -    //
    
    192
    -    // Therefore, we await the oldProvider within the Promise rather than make
    
    193
    -    // #initTorProvider async.
    
    194
    -    //
    
    195
    -    // In particular, this is needed by TorConnect when the user has selected
    
    196
    -    // quickstart, in which case `TorConnect.init` will immediately request the
    
    197
    -    // provider. See tor-browser#41921.
    
    198
    -    this.#provider = this.#replaceTorProvider(this.#provider);
    
    199
    -    return this.#provider;
    
    269
    +    Services.obs.notifyObservers(
    
    270
    +      null,
    
    271
    +      TorProviderTopics.ProviderStateChanged,
    
    272
    +      provider.state
    
    273
    +    );
    
    274
    +
    
    275
    +    this.#promptProviderState(false);
    
    200 276
       }
    
    201 277
     
    
    202 278
       /**
    
    203
    -   * Replace a TorProvider instance. Resolves once the TorProvider is
    
    204
    -   * initialised.
    
    279
    +   * Check the given provider's state.
    
    280
    +   *
    
    281
    +   * If the provider is no longer the current one, it is considered to be
    
    282
    +   * "Stopped".
    
    283
    +   *
    
    284
    +   * If it is still the current provider, the state will be updated and
    
    285
    +   * returned.
    
    205 286
        *
    
    206
    -   * @param {Promise<TorProvider>?} oldProvider - The previous's provider's
    
    207
    -   *   promise, if any.
    
    208
    -   * @returns {TorProvider} The new TorProvider instance.
    
    287
    +   * @param {TorProviderBase} provider - The provider to check.
    
    288
    +   * @returns {string} - The `TorProviderState` state for the provider.
    
    209 289
        */
    
    210
    -  static async #replaceTorProvider(oldProvider) {
    
    290
    +  static #checkProviderState(provider) {
    
    291
    +    if (this.#providerData?.provider !== provider) {
    
    292
    +      // Replaced.
    
    293
    +      lazy.logger.debug("The checked provider has been replaced.");
    
    294
    +      return TorProviderState.Stopped;
    
    295
    +    }
    
    296
    +    return this.#providerData.provider.state;
    
    297
    +  }
    
    298
    +
    
    299
    +  /**
    
    300
    +   * Cleanup the given provider data.
    
    301
    +   *
    
    302
    +   * @param {?TorProviderData} providerData - The data to clean up.
    
    303
    +   */
    
    304
    +  static async #cleanupProviderData(providerData) {
    
    305
    +    if (!providerData) {
    
    306
    +      return;
    
    307
    +    }
    
    211 308
         try {
    
    212
    -      // Uninitialise the old TorProvider, if there is any.
    
    213
    -      (await oldProvider)?.uninit();
    
    309
    +      await providerData.initPromise;
    
    214 310
         } catch {}
    
    215
    -    const provider = new lazy.TorProvider();
    
    311
    +
    
    312
    +    // Call `uninit` to clean up, even if `init` threw.
    
    313
    +    // Should be safe to call more than once (via `uninit`).
    
    216 314
         try {
    
    217
    -      await provider.init();
    
    315
    +      await providerData.provider.uninit();
    
    218 316
         } catch (error) {
    
    219
    -      // Wrap in an error type for callers to know whether the error comes from
    
    220
    -      // initialisation or something else.
    
    221
    -      throw new TorProviderInitError(error);
    
    317
    +      lazy.logger.error("Error in uninitializing provider", error);
    
    222 318
         }
    
    223
    -    return provider;
    
    224 319
       }
    
    225 320
     
    
    226 321
       static uninit() {
    
    227
    -    this.#provider?.then(provider => {
    
    228
    -      provider.uninit();
    
    229
    -      this.#provider = null;
    
    230
    -    });
    
    231
    -    if (this.#exitObserver) {
    
    232
    -      Services.obs.removeObserver(
    
    233
    -        this.#exitObserver,
    
    234
    -        TorProviderTopics.ProcessExited
    
    235
    -      );
    
    236
    -      this.#exitObserver = null;
    
    237
    -    }
    
    322
    +    this.#uninitialized = true;
    
    323
    +
    
    324
    +    // NOTE: `uninit` should not be followed by any further calls to public
    
    325
    +    // methods. So we can clear the `#providerData` without keeping it for any
    
    326
    +    // future provider instances to wait on.
    
    327
    +    const providerData = this.#providerData;
    
    328
    +    this.#providerData = null;
    
    329
    +    this.#cleanupProviderData(providerData);
    
    330
    +
    
    238 331
         Services.obs.removeObserver(this.#logObserver, TorProviderTopics.TorLog);
    
    239 332
       }
    
    240 333
     
    
    241 334
       /**
    
    242
    -   * Build a provider.
    
    243
    -   * This method will wait for the system to be initialized, and allows you to
    
    244
    -   * catch also any initialization errors.
    
    335
    +   * Request the current instance of the Tor provider.
    
    336
    +   *
    
    337
    +   * This method will wait for the system to be initialized before returning the
    
    338
    +   * provider.
    
    339
    +   *
    
    340
    +   * This will throw any initialization errors of the provider, if it had any.
    
    341
    +   * This will also throw if the provider is no longer active.
    
    245 342
        *
    
    246 343
        * @returns {TorProvider} A TorProvider instance
    
    247 344
        */
    
    248 345
       static async build() {
    
    249
    -    if (!this.#provider && this.providerType === TorProviders.none) {
    
    346
    +    this.#checkActive();
    
    347
    +    if (this.#providerData.provider instanceof lazy.TorProviderNone) {
    
    250 348
           throw new Error(
    
    251 349
             "Tor Browser has been configured to use only the proxy functionalities."
    
    252 350
           );
    
    253
    -    } else if (!this.#provider) {
    
    254
    -      throw new Error(
    
    255
    -        "The provider has not been initialized or already uninitialized."
    
    351
    +    }
    
    352
    +
    
    353
    +    const { provider, initPromise } = this.#providerData;
    
    354
    +    // initPromise may throw.
    
    355
    +    await initPromise;
    
    356
    +    if (this.#checkProviderState(provider) !== TorProviderState.Running) {
    
    357
    +      lazy.logger.warn("Request was made for a provider that has stopped.");
    
    358
    +      // TODO: Wait for the new instance instead?
    
    359
    +      throw new TorProviderInitError(
    
    360
    +        new Error("Provider is no longer active.")
    
    256 361
           );
    
    257 362
         }
    
    258
    -    return this.#provider;
    
    363
    +    return provider;
    
    364
    +  }
    
    365
    +
    
    366
    +  /**
    
    367
    +   * Get the state of the current provider instance. Waits until the provider
    
    368
    +   * has finished initialisation first.
    
    369
    +   *
    
    370
    +   * If the provider has been replaced, the Stopped state will be returned.
    
    371
    +   *
    
    372
    +   * @returns {string} - The `TorProviderState` state for the provider that
    
    373
    +   *   existed when this method was called.
    
    374
    +   */
    
    375
    +  static async settledState() {
    
    376
    +    this.#checkActive();
    
    377
    +    const { provider, initPromise } = this.#providerData;
    
    378
    +    try {
    
    379
    +      await initPromise;
    
    380
    +    } catch {}
    
    381
    +    return this.#checkProviderState(provider);
    
    259 382
       }
    
    260 383
     
    
    384
    +  /**
    
    385
    +   * Replace the current provider instance with a new provider.
    
    386
    +   */
    
    387
    +  static replace() {
    
    388
    +    this.#checkActive();
    
    389
    +    this.#replaceProvider();
    
    390
    +  }
    
    391
    +
    
    392
    +  // TODO: Remove firstWindowLoaded, #uiReady, #prompting, #promptProviderState
    
    393
    +  // and use TorConnect instead. tor-browser#43570.
    
    261 394
       /**
    
    262 395
        * Check if the provider has been succesfully initialized when the first
    
    263 396
        * browser window is shown.
    
    ... ... @@ -266,50 +399,72 @@ export class TorProviderBuilder {
    266 399
        * but we should modify TorConnect and about:torconnect to handle this case
    
    267 400
        * there with a better UX.
    
    268 401
        */
    
    269
    -  static async firstWindowLoaded() {
    
    270
    -    // FIXME: Just integrate this with the about:torconnect or about:tor UI.
    
    271
    -    if (
    
    272
    -      !lazy.TorLauncherUtil.shouldStartAndOwnTor ||
    
    273
    -      this.providerType !== TorProviders.tor
    
    274
    -    ) {
    
    275
    -      // If we are not managing the Tor daemon we cannot restart it, so just
    
    276
    -      // early return.
    
    277
    -      return;
    
    278
    -    }
    
    279
    -    let running = false;
    
    280
    -    try {
    
    281
    -      const provider = await this.#provider;
    
    282
    -      // The initialization might have succeeded, but so far we have ignored any
    
    283
    -      // error notification. So, check that the process has not exited after the
    
    284
    -      // provider has been initialized successfully, but the UI was not ready
    
    285
    -      // yet.
    
    286
    -      running = provider.isRunning;
    
    287
    -    } catch {
    
    288
    -      // Not even initialized, running is already false.
    
    289
    -    }
    
    290
    -    while (!running && lazy.TorLauncherUtil.showRestartPrompt(true)) {
    
    291
    -      try {
    
    292
    -        await this.#initTorProvider();
    
    293
    -        running = true;
    
    294
    -      } catch {}
    
    295
    -    }
    
    296
    -    // The user might have canceled the restart, but at this point the UI is
    
    297
    -    // ready in any case.
    
    298
    -    this.#uiReady = true;
    
    402
    +  static firstWindowLoaded() {
    
    403
    +    this.#promptProviderState(true);
    
    299 404
       }
    
    300 405
     
    
    301
    -  static async #torExited() {
    
    406
    +  /**
    
    407
    +   * Tell whether the browser UI is ready.
    
    408
    +   * We ignore any errors until it is because we cannot show them.
    
    409
    +   *
    
    410
    +   * @type {boolean}
    
    411
    +   */
    
    412
    +  static #uiReady = false;
    
    413
    +
    
    414
    +  /**
    
    415
    +   * Whether we are prompting the user for a restart of the provider.
    
    416
    +   *
    
    417
    +   * @type {boolean}
    
    418
    +   */
    
    419
    +  static #prompting = false;
    
    420
    +
    
    421
    +  /**
    
    422
    +   * Prompt the user to restart the provider, if this is necessary.
    
    423
    +   *
    
    424
    +   * @param {boolean} uiReady - Whether this is being called for the first time
    
    425
    +   *   when the UI is ready.
    
    426
    +   */
    
    427
    +  static async #promptProviderState(uiReady) {
    
    428
    +    if (uiReady) {
    
    429
    +      this.#uiReady = true;
    
    430
    +    }
    
    431
    +    if (this.#providerData.provider.state === TorProviderState.Running) {
    
    432
    +      // Nothing to wait for.
    
    433
    +      return;
    
    434
    +    }
    
    302 435
         if (!this.#uiReady) {
    
    303
    -      console.warn(
    
    304
    -        `Seen ${TorProviderTopics.ProcessExited}, but not doing anything because the UI is not ready yet.`
    
    436
    +      lazy.logger.warn(
    
    437
    +        "Seen exit, but not doing anything because the UI is not ready yet."
    
    305 438
           );
    
    306 439
           return;
    
    307 440
         }
    
    308
    -    while (lazy.TorLauncherUtil.showRestartPrompt(false)) {
    
    309
    -      try {
    
    310
    -        await this.#initTorProvider();
    
    311
    -        break;
    
    312
    -      } catch {}
    
    441
    +    if (this.#prompting) {
    
    442
    +      // Already prompting, so don't duplicate.
    
    443
    +      return;
    
    444
    +    }
    
    445
    +
    
    446
    +    this.#prompting = true;
    
    447
    +    let waitForInit = uiReady;
    
    448
    +    let retry = true;
    
    449
    +    try {
    
    450
    +      while (retry) {
    
    451
    +        if (waitForInit) {
    
    452
    +          try {
    
    453
    +            await this.#providerData.initPromise;
    
    454
    +          } catch {}
    
    455
    +        }
    
    456
    +        if (
    
    457
    +          this.#providerData.provider.state === TorProviderState.Stopped &&
    
    458
    +          lazy.TorLauncherUtil.showRestartPrompt(uiReady)
    
    459
    +        ) {
    
    460
    +          waitForInit = true;
    
    461
    +          this.replace();
    
    462
    +        } else {
    
    463
    +          retry = false;
    
    464
    +        }
    
    465
    +      }
    
    466
    +    } finally {
    
    467
    +      this.#prompting = false;
    
    313 468
         }
    
    314 469
       }
    
    315 470
     
    
    ... ... @@ -320,7 +475,7 @@ export class TorProviderBuilder {
    320 475
        * Otherwise, if it is not valid, the C tor implementation is chosen as the
    
    321 476
        * default one.
    
    322 477
        *
    
    323
    -   * @returns {number} An entry from TorProviders
    
    478
    +   * @returns {string} An entry from TorProviders
    
    324 479
        */
    
    325 480
       static get providerType() {
    
    326 481
         // TODO: Add a preference to permanently save this without and avoid always
    

  • toolkit/components/tor-launcher/TorProviderNone.sys.mjs
    1
    +import { TorProviderBase } from "resource://gre/modules/TorProviderBase.sys.mjs";
    
    2
    +
    
    3
    +const lazy = {};
    
    4
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    5
    +  TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
    
    6
    +});
    
    7
    +
    
    8
    +/**
    
    9
    + * A provider that only sets the proxy settings.
    
    10
    + */
    
    11
    +export class TorProviderNone extends TorProviderBase {
    
    12
    +  async _initInternal() {
    
    13
    +    lazy.TorLauncherUtil.setProxyConfiguration(
    
    14
    +      lazy.TorLauncherUtil.getPreferredSocksConfiguration()
    
    15
    +    );
    
    16
    +  }
    
    17
    +
    
    18
    +  async _uninitInternal() {}
    
    19
    +}

  • toolkit/components/tor-launcher/moz.build
    ... ... @@ -7,7 +7,9 @@ EXTRA_JS_MODULES += [
    7 7
         "TorProcess.sys.mjs",
    
    8 8
         "TorProcessAndroid.sys.mjs",
    
    9 9
         "TorProvider.sys.mjs",
    
    10
    +    "TorProviderBase.sys.mjs",
    
    10 11
         "TorProviderBuilder.sys.mjs",
    
    12
    +    "TorProviderNone.sys.mjs",
    
    11 13
         "TorStartupService.sys.mjs",
    
    12 14
     ]
    
    13 15
     
    

  • toolkit/modules/TorConnect.sys.mjs
    ... ... @@ -9,6 +9,7 @@ const lazy = {};
    9 9
     ChromeUtils.defineESModuleGetters(lazy, {
    
    10 10
       MoatRPC: "resource://gre/modules/Moat.sys.mjs",
    
    11 11
       TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs",
    
    12
    +  TorProviderState: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    12 13
       TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    13 14
       TorBootstrapError: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    14 15
       TorProviderInitError: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    ... ... @@ -913,7 +914,7 @@ export const TorConnect = {
    913 914
         };
    
    914 915
     
    
    915 916
         // register the Tor topics we always care about
    
    916
    -    observeTopic(lazy.TorProviderTopics.ProcessExited);
    
    917
    +    observeTopic(lazy.TorProviderTopics.ProviderStateChanged);
    
    917 918
         observeTopic(lazy.TorProviderTopics.HasWarnOrErr);
    
    918 919
         observeTopic(lazy.TorSettingsTopics.SettingsChanged);
    
    919 920
         observeTopic(NETWORK_LINK_TOPIC);
    
    ... ... @@ -933,7 +934,7 @@ export const TorConnect = {
    933 934
         }
    
    934 935
       },
    
    935 936
     
    
    936
    -  async observe(subject, topic) {
    
    937
    +  async observe(subject, topic, data) {
    
    937 938
         lazy.logger.debug(`Observed ${topic}`);
    
    938 939
     
    
    939 940
         switch (topic) {
    
    ... ... @@ -947,7 +948,10 @@ export const TorConnect = {
    947 948
               this._notifyBootstrapProgress();
    
    948 949
             }
    
    949 950
             break;
    
    950
    -      case lazy.TorProviderTopics.ProcessExited:
    
    951
    +      case lazy.TorProviderTopics.ProviderStateChanged:
    
    952
    +        if (data !== lazy.TorProviderState.Stopped) {
    
    953
    +          break;
    
    954
    +        }
    
    951 955
             lazy.logger.info("Starting again since the tor process exited");
    
    952 956
             // Treat a failure as a possibly broken configuration.
    
    953 957
             // So, prevent quickstart at the next start.
    
    ... ... @@ -1341,7 +1345,7 @@ export const TorConnect = {
    1341 1345
           this._tryAgain = true;
    
    1342 1346
     
    
    1343 1347
           if (error instanceof lazy.TorProviderInitError) {
    
    1344
    -        // Treat like TorProviderTopics.ProcessExited. We expect a user
    
    1348
    +        // Treat like TorProviderTopics.ProviderStateChanged. We expect a user
    
    1345 1349
             // notification when this happens.
    
    1346 1350
             // Treat a failure as a possibly broken configuration.
    
    1347 1351
             // So, prevent quickstart at the next start.
    

  • toolkit/modules/TorSettings.sys.mjs
    ... ... @@ -9,6 +9,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
    9 9
       Lox: "resource://gre/modules/Lox.sys.mjs",
    
    10 10
       LoxTopics: "resource://gre/modules/Lox.sys.mjs",
    
    11 11
       TorParsers: "resource://gre/modules/TorParsers.sys.mjs",
    
    12
    +  TorProviderState: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    12 13
     });
    
    13 14
     
    
    14 15
     ChromeUtils.defineLazyGetter(lazy, "logger", () => {
    
    ... ... @@ -940,7 +941,10 @@ class TorSettingsImpl {
    940 941
     
    
    941 942
           // Test whether the provider is no longer running or has been replaced.
    
    942 943
           const providerRunning = () => {
    
    943
    -        return providerRef === this.#providerRef && provider.isRunning;
    
    944
    +        return (
    
    945
    +          providerRef === this.#providerRef &&
    
    946
    +          provider.state !== lazy.TorProviderState.Stopped
    
    947
    +        );
    
    944 948
           };
    
    945 949
     
    
    946 950
           lazy.logger.debug("Passing on settings to the provider", apply, details);
    

  • _______________________________________________
    tor-commits mailing list -- tor-commits@xxxxxxxxxxxxxxxxxxxx
    To unsubscribe send an email to tor-commits-leave@xxxxxxxxxxxxxxxxxxxx