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

[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-128.6.0esr-14.5-1] 2 commits: fixup! TB 40597: Implement TorSettings module



Title: GitLab

Pier Angelo Vendrame pushed to branch tor-browser-128.6.0esr-14.5-1 at The Tor Project / Applications / Tor Browser

Commits:

  • b64f4aad
    by Henry Wilkes at 2025-01-21T11:50:20+01:00
    fixup! TB 40597: Implement TorSettings module
    
    TB 41921: Make the TorSettings properties read-only outside of the
    TorSettings modules.
    
    Within TorSettings we make all post initialisation changes in
    changeSettings. This means we can simplify the class and move all the
    setter logic into one place.
    
  • 909f72ff
    by Henry Wilkes at 2025-01-21T11:50:24+01:00
    fixup! TB 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
    
    TB 41921: Pass in the BridgeDB bridge_strings as an Array to
    TorSettings.
    

3 changed files:

Changes:

  • browser/components/torpreferences/content/connectionPane.js
    ... ... @@ -2319,7 +2319,7 @@ const gBridgeSettings = {
    2319 2319
               bridges: {
    
    2320 2320
                 enabled: true,
    
    2321 2321
                 source: TorBridgeSource.BridgeDB,
    
    2322
    -            bridge_strings: result.bridges.join("\n"),
    
    2322
    +            bridge_strings: result.bridges,
    
    2323 2323
               },
    
    2324 2324
             });
    
    2325 2325
           }
    

  • toolkit/modules/DomainFrontedRequests.sys.mjs
    ... ... @@ -130,7 +130,7 @@ class MeekTransport {
    130 130
             TOR_PT_CLIENT_TRANSPORTS: meekTransport,
    
    131 131
           };
    
    132 132
           if (lazy.TorSettings.proxy.enabled) {
    
    133
    -        envAdditions.TOR_PT_PROXY = lazy.TorSettings.proxy.uri;
    
    133
    +        envAdditions.TOR_PT_PROXY = lazy.TorSettings.proxyUri;
    
    134 134
           }
    
    135 135
     
    
    136 136
           const opts = {
    

  • toolkit/modules/TorSettings.sys.mjs
    ... ... @@ -179,10 +179,34 @@ class TorSettingsImpl {
    179 179
           enabled: false,
    
    180 180
         },
    
    181 181
         bridges: {
    
    182
    +      /**
    
    183
    +       * Whether the bridges are enabled or not.
    
    184
    +       *
    
    185
    +       * @type {boolean}
    
    186
    +       */
    
    182 187
           enabled: false,
    
    183 188
           source: TorBridgeSource.Invalid,
    
    189
    +      /**
    
    190
    +       * The lox id is used with the Lox "source", and remains set with the
    
    191
    +       * stored value when other sources are used.
    
    192
    +       *
    
    193
    +       * @type {string}
    
    194
    +       */
    
    184 195
           lox_id: "",
    
    196
    +      /**
    
    197
    +       * The built-in type to use when using the BuiltIn "source", or empty when
    
    198
    +       * using any other source.
    
    199
    +       *
    
    200
    +       * @type {string}
    
    201
    +       */
    
    185 202
           builtin_type: "",
    
    203
    +      /**
    
    204
    +       * The current bridge strings.
    
    205
    +       *
    
    206
    +       * Can only be non-empty if the "source" is not Invalid.
    
    207
    +       *
    
    208
    +       * @type {Array<string>}
    
    209
    +       */
    
    186 210
           bridge_strings: [],
    
    187 211
         },
    
    188 212
         proxy: {
    
    ... ... @@ -206,15 +230,6 @@ class TorSettingsImpl {
    206 230
        */
    
    207 231
       #temporaryBridgeSettings = null;
    
    208 232
     
    
    209
    -  /**
    
    210
    -   * Accumulated errors from trying to set settings.
    
    211
    -   *
    
    212
    -   * Only added to if not null.
    
    213
    -   *
    
    214
    -   * @type {Array<Error>?}
    
    215
    -   */
    
    216
    -  #settingErrors = null;
    
    217
    -
    
    218 233
       /**
    
    219 234
        * The recommended pluggable transport.
    
    220 235
        *
    
    ... ... @@ -245,13 +260,6 @@ class TorSettingsImpl {
    245 260
        * @type {boolean}
    
    246 261
        */
    
    247 262
       #initialized = false;
    
    248
    -  /**
    
    249
    -   * During some phases of the initialization, allow calling setters and
    
    250
    -   * getters without throwing errors.
    
    251
    -   *
    
    252
    -   * @type {boolean}
    
    253
    -   */
    
    254
    -  #allowUninitialized = false;
    
    255 263
     
    
    256 264
       constructor() {
    
    257 265
         this.initializedPromise = new Promise((resolve, reject) => {
    
    ... ... @@ -259,347 +267,57 @@ class TorSettingsImpl {
    259 267
           this.#initFailed = reject;
    
    260 268
         });
    
    261 269
     
    
    262
    -    this.#addProperties("quickstart", {
    
    263
    -      enabled: {},
    
    264
    -    });
    
    265
    -    this.#addProperties("bridges", {
    
    266
    -      /**
    
    267
    -       * Whether the bridges are enabled or not.
    
    268
    -       *
    
    269
    -       * @type {boolean}
    
    270
    -       */
    
    271
    -      enabled: {},
    
    272
    -      /**
    
    273
    -       * The current bridge source.
    
    274
    -       *
    
    275
    -       * @type {integer}
    
    276
    -       */
    
    277
    -      source: {
    
    278
    -        transform: (val, addError) => {
    
    279
    -          if (Object.values(TorBridgeSource).includes(val)) {
    
    280
    -            return val;
    
    281
    -          }
    
    282
    -          addError(`Not a valid bridge source: "${val}"`);
    
    283
    -          return TorBridgeSource.Invalid;
    
    284
    -        },
    
    285
    -      },
    
    286
    -      /**
    
    287
    -       * The current bridge strings.
    
    288
    -       *
    
    289
    -       * Can only be non-empty if the "source" is not Invalid.
    
    290
    -       *
    
    291
    -       * @type {Array<string>}
    
    292
    -       */
    
    293
    -      bridge_strings: {
    
    294
    -        transform: val => {
    
    295
    -          if (Array.isArray(val)) {
    
    296
    -            return [...val];
    
    297
    -          }
    
    298
    -          // Split the bridge strings, discarding empty.
    
    299
    -          return splitBridgeLines(val).filter(val => val);
    
    300
    -        },
    
    301
    -        copy: val => [...val],
    
    302
    -        equal: (val1, val2) => this.#arrayEqual(val1, val2),
    
    303
    -      },
    
    304
    -      /**
    
    305
    -       * The built-in type to use when using the BuiltIn "source", or empty when
    
    306
    -       * using any other source.
    
    307
    -       *
    
    308
    -       * @type {string}
    
    309
    -       */
    
    310
    -      builtin_type: {
    
    311
    -        callback: (val, addError) => {
    
    312
    -          if (!val) {
    
    313
    -            return;
    
    314
    -          }
    
    315
    -          const bridgeStrings = this.getBuiltinBridges(val);
    
    316
    -          if (bridgeStrings.length) {
    
    317
    -            this.bridges.bridge_strings = bridgeStrings;
    
    318
    -            return;
    
    319
    -          }
    
    320
    -
    
    321
    -          addError(`No built-in ${val} bridges found`);
    
    322
    -          // Set as invalid, which will make the builtin_type "" and set the
    
    323
    -          // bridge_strings to be empty at the next #cleanupSettings.
    
    324
    -          this.bridges.source = TorBridgeSource.Invalid;
    
    325
    -        },
    
    326
    -      },
    
    327
    -      /**
    
    328
    -       * The lox id is used with the Lox "source", and remains set with the stored value when
    
    329
    -       * other sources are used.
    
    330
    -       *
    
    331
    -       * @type {string}
    
    332
    -       */
    
    333
    -      lox_id: {
    
    334
    -        callback: (val, addError) => {
    
    335
    -          if (!val) {
    
    336
    -            return;
    
    337
    -          }
    
    338
    -          let bridgeStrings;
    
    339
    -          try {
    
    340
    -            bridgeStrings = lazy.Lox.getBridges(val);
    
    341
    -          } catch (error) {
    
    342
    -            addError(`No bridges for lox_id ${val}: ${error?.message}`);
    
    343
    -            // Set as invalid, which will make the builtin_type "" and set the
    
    344
    -            // bridge_strings to be empty at the next #cleanupSettings.
    
    345
    -            this.bridges.source = TorBridgeSource.Invalid;
    
    346
    -            return;
    
    347
    -          }
    
    348
    -          this.bridges.bridge_strings = bridgeStrings;
    
    349
    -        },
    
    350
    -      },
    
    351
    -    });
    
    352
    -    this.#addProperties("proxy", {
    
    353
    -      enabled: {},
    
    354
    -      type: {
    
    355
    -        transform: (val, addError) => {
    
    356
    -          if (Object.values(TorProxyType).includes(val)) {
    
    357
    -            return val;
    
    358
    -          }
    
    359
    -          addError(`Not a valid proxy type: "${val}"`);
    
    360
    -          return TorProxyType.Invalid;
    
    361
    -        },
    
    362
    -      },
    
    363
    -      address: {},
    
    364
    -      port: {
    
    365
    -        transform: (val, addError) => {
    
    366
    -          if (val === 0) {
    
    367
    -            // This is a valid value that "unsets" the port.
    
    368
    -            // Keep this value without giving a warning.
    
    369
    -            // NOTE: In contrast, "0" is not valid.
    
    370
    -            return 0;
    
    371
    -          }
    
    372
    -          // Unset to 0 if invalid null is returned.
    
    373
    -          return this.#parsePort(val, false, addError) ?? 0;
    
    374
    -        },
    
    375
    -      },
    
    376
    -      username: {},
    
    377
    -      password: {},
    
    378
    -      uri: {
    
    379
    -        getter: () => {
    
    380
    -          const { type, address, port, username, password } = this.proxy;
    
    381
    -          switch (type) {
    
    382
    -            case TorProxyType.Socks4:
    
    383
    -              return `socks4a://${address}:${port}`;
    
    384
    -            case TorProxyType.Socks5:
    
    385
    -              if (username) {
    
    386
    -                return `socks5://${username}:${password}@${address}:${port}`;
    
    387
    -              }
    
    388
    -              return `socks5://${address}:${port}`;
    
    389
    -            case TorProxyType.HTTPS:
    
    390
    -              if (username) {
    
    391
    -                return `http://${username}:${password}@${address}:${port}`;
    
    392
    -              }
    
    393
    -              return `http://${address}:${port}`;
    
    394
    -          }
    
    395
    -          return null;
    
    396
    -        },
    
    397
    -      },
    
    398
    -    });
    
    399
    -    this.#addProperties("firewall", {
    
    400
    -      enabled: {},
    
    401
    -      allowed_ports: {
    
    402
    -        transform: (val, addError) => {
    
    403
    -          if (!Array.isArray(val)) {
    
    404
    -            val = val === "" ? [] : val.split(",");
    
    405
    -          }
    
    406
    -          // parse and remove duplicates
    
    407
    -          const portSet = new Set(
    
    408
    -            val.map(p => this.#parsePort(p, true, addError))
    
    409
    -          );
    
    410
    -          // parsePort returns null for failed parses, so remove it.
    
    411
    -          portSet.delete(null);
    
    412
    -          return [...portSet];
    
    413
    -        },
    
    414
    -        copy: val => [...val],
    
    415
    -        equal: (val1, val2) => this.#arrayEqual(val1, val2),
    
    416
    -      },
    
    417
    -    });
    
    418
    -  }
    
    419
    -
    
    420
    -  /**
    
    421
    -   * Clean the setting values after making some changes, so that the values do
    
    422
    -   * not contradict each other.
    
    423
    -   */
    
    424
    -  #cleanupSettings() {
    
    425
    -    this.#freezeNotifications();
    
    426
    -    try {
    
    427
    -      if (this.bridges.source === TorBridgeSource.Invalid) {
    
    428
    -        this.bridges.enabled = false;
    
    429
    -        this.bridges.bridge_strings = [];
    
    270
    +    // Add some read-only getters for the #settings object.
    
    271
    +    // E.g. TorSetting.#settings.bridges.source is exposed publicly as
    
    272
    +    // TorSettings.bridges.source.
    
    273
    +    for (const groupname in this.#settings) {
    
    274
    +      const publicGroup = {};
    
    275
    +      for (const name in this.#settings[groupname]) {
    
    276
    +        // Public group only has a getter for the property.
    
    277
    +        Object.defineProperty(publicGroup, name, {
    
    278
    +          get: () => {
    
    279
    +            this.#checkIfInitialized();
    
    280
    +            return structuredClone(this.#settings[groupname][name]);
    
    281
    +          },
    
    282
    +          set: () => {
    
    283
    +            throw new Error(
    
    284
    +              `TorSettings.${groupname}.${name} cannot be set directly`
    
    285
    +            );
    
    286
    +          },
    
    287
    +        });
    
    430 288
           }
    
    431
    -      if (!this.bridges.bridge_strings.length) {
    
    432
    -        this.bridges.enabled = false;
    
    433
    -        this.bridges.source = TorBridgeSource.Invalid;
    
    434
    -      }
    
    435
    -      if (this.bridges.source !== TorBridgeSource.BuiltIn) {
    
    436
    -        this.bridges.builtin_type = "";
    
    437
    -      }
    
    438
    -      if (this.bridges.source !== TorBridgeSource.Lox) {
    
    439
    -        this.bridges.lox_id = "";
    
    440
    -      }
    
    441
    -      if (!this.proxy.enabled) {
    
    442
    -        this.proxy.type = TorProxyType.Invalid;
    
    443
    -        this.proxy.address = "";
    
    444
    -        this.proxy.port = 0;
    
    445
    -        this.proxy.username = "";
    
    446
    -        this.proxy.password = "";
    
    447
    -      }
    
    448
    -      if (!this.firewall.enabled) {
    
    449
    -        this.firewall.allowed_ports = [];
    
    450
    -      }
    
    451
    -    } finally {
    
    452
    -      this.#thawNotifications();
    
    289
    +      // The group object itself should not be writable.
    
    290
    +      Object.preventExtensions(publicGroup);
    
    291
    +      Object.defineProperty(this, groupname, {
    
    292
    +        writable: false,
    
    293
    +        value: publicGroup,
    
    294
    +      });
    
    453 295
         }
    
    454 296
       }
    
    455 297
     
    
    456 298
       /**
    
    457
    -   * The current number of freezes applied to the notifications.
    
    458
    -   *
    
    459
    -   * @type {integer}
    
    460
    -   */
    
    461
    -  #freezeNotificationsCount = 0;
    
    462
    -  /**
    
    463
    -   * The queue for settings that have changed. To be broadcast in the
    
    464
    -   * notification when not frozen.
    
    465
    -   *
    
    466
    -   * @type {Set<string>}
    
    467
    -   */
    
    468
    -  #notificationQueue = new Set();
    
    469
    -  /**
    
    470
    -   * Send a notification if we have any queued and we are not frozen.
    
    471
    -   */
    
    472
    -  #tryNotification() {
    
    473
    -    if (this.#freezeNotificationsCount || !this.#notificationQueue.size) {
    
    474
    -      return;
    
    475
    -    }
    
    476
    -    Services.obs.notifyObservers(
    
    477
    -      { changes: [...this.#notificationQueue] },
    
    478
    -      TorSettingsTopics.SettingsChanged
    
    479
    -    );
    
    480
    -    this.#notificationQueue.clear();
    
    481
    -  }
    
    482
    -  /**
    
    483
    -   * Pause notifications for changes in setting values. This is useful if you
    
    484
    -   * need to make batch changes to settings.
    
    485
    -   *
    
    486
    -   * This should always be paired with a call to thawNotifications once
    
    487
    -   * notifications should be released. Usually you should wrap whatever
    
    488
    -   * changes you make with a `try` block and call thawNotifications in the
    
    489
    -   * `finally` block.
    
    490
    -   */
    
    491
    -  #freezeNotifications() {
    
    492
    -    this.#freezeNotificationsCount++;
    
    493
    -  }
    
    494
    -  /**
    
    495
    -   * Release the hold on notifications so they may be sent out.
    
    496
    -   *
    
    497
    -   * Note, if some other method has also frozen the notifications, this will
    
    498
    -   * only release them once it has also called this method.
    
    499
    -   */
    
    500
    -  #thawNotifications() {
    
    501
    -    this.#freezeNotificationsCount--;
    
    502
    -    this.#tryNotification();
    
    503
    -  }
    
    504
    -  /**
    
    505
    -   * @typedef {object} TorSettingProperty
    
    299
    +   * The proxy URI for the current settings, or `null` if no proxy is
    
    300
    +   * configured.
    
    506 301
        *
    
    507
    -   * @property {function} [getter] - A getter for the property. If this is
    
    508
    -   *   given, the property cannot be set.
    
    509
    -   * @property {function} [transform] - Called in the setter for the property,
    
    510
    -   *   with the new value given. Should transform the given value into the
    
    511
    -   *   right type.
    
    512
    -   * @property {function} [equal] - Test whether two values for the property
    
    513
    -   *   are considered equal. Otherwise uses `===`.
    
    514
    -   * @property {function} [callback] - Called whenever the property value
    
    515
    -   *   changes, with the new value given. Should be used to trigger any other
    
    516
    -   *   required changes for the new value.
    
    517
    -   * @property {function} [copy] - Called whenever the property is read, with
    
    518
    -   *   the stored value given. Should return a copy of the value. Otherwise
    
    519
    -   *   returns the stored value.
    
    302
    +   * @type {?string}
    
    520 303
        */
    
    521
    -  /**
    
    522
    -   * Add properties to the TorSettings instance, to be read or set.
    
    523
    -   *
    
    524
    -   * @param {string} groupname - The name of the setting group. The given
    
    525
    -   *   settings will be accessible from the TorSettings property of the same
    
    526
    -   *   name.
    
    527
    -   * @param {object.<string, TorSettingProperty>} propParams - An object that
    
    528
    -   *   defines the settings to add to this group. The object property names
    
    529
    -   *   will be mapped to properties of TorSettings under the given groupname
    
    530
    -   *   property. Details about the setting should be described in the
    
    531
    -   *   TorSettingProperty property value.
    
    532
    -   */
    
    533
    -  #addProperties(groupname, propParams) {
    
    534
    -    // Create a new object to hold all these settings.
    
    535
    -    const group = {};
    
    536
    -    for (const name in propParams) {
    
    537
    -      const { getter, transform, callback, copy, equal } = propParams[name];
    
    538
    -      // Method for adding setting errors.
    
    539
    -      const addError = message => {
    
    540
    -        message = `TorSettings.${groupname}.${name}: ${message}`;
    
    541
    -        lazy.logger.error(message);
    
    542
    -        // Only add to #settingErrors if it is not null.
    
    543
    -        this.#settingErrors?.push(message);
    
    544
    -      };
    
    545
    -      Object.defineProperty(group, name, {
    
    546
    -        get: getter
    
    547
    -          ? () => {
    
    548
    -              // Allow getting in loadFromPrefs before we are initialized.
    
    549
    -              if (!this.#allowUninitialized) {
    
    550
    -                this.#checkIfInitialized();
    
    551
    -              }
    
    552
    -              return getter();
    
    553
    -            }
    
    554
    -          : () => {
    
    555
    -              // Allow getting in loadFromPrefs before we are initialized.
    
    556
    -              if (!this.#allowUninitialized) {
    
    557
    -                this.#checkIfInitialized();
    
    558
    -              }
    
    559
    -              let val = this.#settings[groupname][name];
    
    560
    -              if (copy) {
    
    561
    -                val = copy(val);
    
    562
    -              }
    
    563
    -              // Assume string or number value.
    
    564
    -              return val;
    
    565
    -            },
    
    566
    -        set: getter
    
    567
    -          ? undefined
    
    568
    -          : val => {
    
    569
    -              // Allow setting in loadFromPrefs before we are initialized.
    
    570
    -              if (!this.#allowUninitialized) {
    
    571
    -                this.#checkIfInitialized();
    
    572
    -              }
    
    573
    -              const prevVal = this.#settings[groupname][name];
    
    574
    -              this.#freezeNotifications();
    
    575
    -              try {
    
    576
    -                if (transform) {
    
    577
    -                  val = transform(val, addError);
    
    578
    -                }
    
    579
    -                const isEqual = equal ? equal(val, prevVal) : val === prevVal;
    
    580
    -                if (!isEqual) {
    
    581
    -                  // Set before the callback.
    
    582
    -                  this.#settings[groupname][name] = val;
    
    583
    -                  this.#notificationQueue.add(`${groupname}.${name}`);
    
    584
    -
    
    585
    -                  if (callback) {
    
    586
    -                    callback(val, addError);
    
    587
    -                  }
    
    588
    -                }
    
    589
    -              } catch (e) {
    
    590
    -                addError(e.message);
    
    591
    -              } finally {
    
    592
    -                this.#thawNotifications();
    
    593
    -              }
    
    594
    -            },
    
    595
    -      });
    
    304
    +  get proxyUri() {
    
    305
    +    const { type, address, port, username, password } = this.#settings.proxy;
    
    306
    +    switch (type) {
    
    307
    +      case TorProxyType.Socks4:
    
    308
    +        return `socks4a://${address}:${port}`;
    
    309
    +      case TorProxyType.Socks5:
    
    310
    +        if (username) {
    
    311
    +          return `socks5://${username}:${password}@${address}:${port}`;
    
    312
    +        }
    
    313
    +        return `socks5://${address}:${port}`;
    
    314
    +      case TorProxyType.HTTPS:
    
    315
    +        if (username) {
    
    316
    +          return `http://${username}:${password}@${address}:${port}`;
    
    317
    +        }
    
    318
    +        return `http://${address}:${port}`;
    
    596 319
         }
    
    597
    -    // The group object itself should not be writable.
    
    598
    -    Object.preventExtensions(group);
    
    599
    -    Object.defineProperty(this, groupname, {
    
    600
    -      writable: false,
    
    601
    -      value: group,
    
    602
    -    });
    
    320
    +    return null;
    
    603 321
       }
    
    604 322
     
    
    605 323
       /**
    
    ... ... @@ -614,12 +332,11 @@ class TorSettingsImpl {
    614 332
        * @param {string|integer} val - The value to parse.
    
    615 333
        * @param {boolean} trim - Whether a string value can be stripped of
    
    616 334
        *   whitespace before parsing.
    
    617
    -   * @param {function} addError - Callback to add error messages to.
    
    618 335
        *
    
    619 336
        * @return {integer?} - The port number, or null if the given value was not
    
    620 337
        *   valid.
    
    621 338
        */
    
    622
    -  #parsePort(val, trim, addError) {
    
    339
    +  #parsePort(val, trim) {
    
    623 340
         if (typeof val === "string") {
    
    624 341
           if (trim) {
    
    625 342
             val = val.trim();
    
    ... ... @@ -628,13 +345,11 @@ class TorSettingsImpl {
    628 345
           if (this.#portRegex.test(val)) {
    
    629 346
             val = Number.parseInt(val, 10);
    
    630 347
           } else {
    
    631
    -        addError(`Invalid port string "${val}"`);
    
    632
    -        return null;
    
    348
    +        throw new Error(`Invalid port string "${val}"`);
    
    633 349
           }
    
    634 350
         }
    
    635 351
         if (!Number.isInteger(val) || val < 1 || val > 65535) {
    
    636
    -      addError(`Port out of range: ${val}`);
    
    637
    -      return null;
    
    352
    +      throw new Error(`Port out of range: ${val}`);
    
    638 353
         }
    
    639 354
         return val;
    
    640 355
       }
    
    ... ... @@ -659,13 +374,8 @@ class TorSettingsImpl {
    659 374
        * @param {string} pt The pluggable transport to return the lines for
    
    660 375
        * @returns {string[]} The bridge lines in random order
    
    661 376
        */
    
    662
    -  getBuiltinBridges(pt) {
    
    663
    -    if (!this.#allowUninitialized) {
    
    664
    -      this.#checkIfInitialized();
    
    665
    -    }
    
    666
    -    // Shuffle so that Tor Browser users do not all try the built-in bridges in
    
    667
    -    // the same order.
    
    668
    -    return arrayShuffle(this.#builtinBridges[pt] ?? []);
    
    377
    +  #getBuiltinBridges(pt) {
    
    378
    +    return this.#builtinBridges[pt] ?? [];
    
    669 379
       }
    
    670 380
     
    
    671 381
       /**
    
    ... ... @@ -698,6 +408,13 @@ class TorSettingsImpl {
    698 408
           lazy.logger.debug("Loaded pt_config.json", config);
    
    699 409
           this.#recommendedPT = config.recommendedDefault;
    
    700 410
           this.#builtinBridges = config.bridges;
    
    411
    +      for (const type in this.#builtinBridges) {
    
    412
    +        // Shuffle so that Tor Browser users do not all try the built-in bridges
    
    413
    +        // in the same order.
    
    414
    +        // Only do this once per session. In particular, we don't re-shuffle if
    
    415
    +        // changeSettings is called with the same bridges.builtin_type value.
    
    416
    +        this.#builtinBridges[type] = arrayShuffle(this.#builtinBridges[type]);
    
    417
    +      }
    
    701 418
         } catch (e) {
    
    702 419
           lazy.logger.error("Could not load the built-in PT config.", e);
    
    703 420
         }
    
    ... ... @@ -716,18 +433,9 @@ class TorSettingsImpl {
    716 433
           lazy.TorLauncherUtil.shouldStartAndOwnTor &&
    
    717 434
           Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)
    
    718 435
         ) {
    
    719
    -      // Do not want notifications for initially loaded prefs.
    
    720
    -      this.#freezeNotifications();
    
    721
    -      try {
    
    722
    -        this.#allowUninitialized = true;
    
    723
    -        this.#loadFromPrefs();
    
    724
    -        // We do not pass on the loaded settings to the TorProvider yet. Instead
    
    725
    -        // TorProvider will ask for these once it has initialised.
    
    726
    -      } finally {
    
    727
    -        this.#allowUninitialized = false;
    
    728
    -        this.#notificationQueue.clear();
    
    729
    -        this.#thawNotifications();
    
    730
    -      }
    
    436
    +      this.#loadFromPrefs();
    
    437
    +      // We do not pass on the loaded settings to the TorProvider yet. Instead
    
    438
    +      // TorProvider will ask for these once it has initialised.
    
    731 439
         }
    
    732 440
     
    
    733 441
         Services.obs.addObserver(this, lazy.LoxTopics.UpdateBridges);
    
    ... ... @@ -746,16 +454,19 @@ class TorSettingsImpl {
    746 454
       observe(subject, topic) {
    
    747 455
         switch (topic) {
    
    748 456
           case lazy.LoxTopics.UpdateBridges:
    
    749
    -        if (this.bridges.lox_id) {
    
    750
    -          // Fetch the newest bridges.
    
    751
    -          this.bridges.bridge_strings = lazy.Lox.getBridges(
    
    752
    -            this.bridges.lox_id
    
    753
    -          );
    
    754
    -          // No need to save to prefs since bridge_strings is not stored for Lox
    
    755
    -          // source. But we do pass on the changes to TorProvider.
    
    457
    +        if (
    
    458
    +          this.#settings.bridges.lox_id &&
    
    459
    +          this.#settings.bridges.source === TorBridgeSource.Lox
    
    460
    +        ) {
    
    461
    +          // Re-trigger the call to lazy.Lox.getBridges.
    
    756 462
               // FIXME: This can compete with TorConnect to reach TorProvider.
    
    757 463
               // tor-browser#42316
    
    758
    -          this.#applySettings();
    
    464
    +          this.changeSettings({
    
    465
    +            bridges: {
    
    466
    +              source: TorBridgeSource.Lox,
    
    467
    +              lox_id: this.#settings.bridges.lox_id,
    
    468
    +            },
    
    469
    +          });
    
    759 470
             }
    
    760 471
             break;
    
    761 472
         }
    
    ... ... @@ -790,23 +501,24 @@ class TorSettingsImpl {
    790 501
         lazy.logger.debug("loadFromPrefs()");
    
    791 502
     
    
    792 503
         /* Quickstart */
    
    793
    -    this.quickstart.enabled = Services.prefs.getBoolPref(
    
    504
    +    this.#settings.quickstart.enabled = Services.prefs.getBoolPref(
    
    794 505
           TorSettingsPrefs.quickstart.enabled,
    
    795 506
           false
    
    796 507
         );
    
    797 508
         /* Bridges */
    
    798
    -    this.bridges.enabled = Services.prefs.getBoolPref(
    
    509
    +    const bridges = {};
    
    510
    +    bridges.enabled = Services.prefs.getBoolPref(
    
    799 511
           TorSettingsPrefs.bridges.enabled,
    
    800 512
           false
    
    801 513
         );
    
    802
    -    this.bridges.source = Services.prefs.getIntPref(
    
    514
    +    bridges.source = Services.prefs.getIntPref(
    
    803 515
           TorSettingsPrefs.bridges.source,
    
    804 516
           TorBridgeSource.Invalid
    
    805 517
         );
    
    806
    -    switch (this.bridges.source) {
    
    518
    +    switch (bridges.source) {
    
    807 519
           case TorBridgeSource.BridgeDB:
    
    808 520
           case TorBridgeSource.UserProvided:
    
    809
    -        this.bridges.bridge_strings = Services.prefs
    
    521
    +        bridges.bridge_strings = Services.prefs
    
    810 522
               .getBranch(TorSettingsPrefs.bridges.bridge_strings)
    
    811 523
               .getChildList("")
    
    812 524
               .map(pref =>
    
    ... ... @@ -817,60 +529,79 @@ class TorSettingsImpl {
    817 529
             break;
    
    818 530
           case TorBridgeSource.BuiltIn:
    
    819 531
             // bridge_strings is set via builtin_type.
    
    820
    -        this.bridges.builtin_type = Services.prefs.getStringPref(
    
    532
    +        bridges.builtin_type = Services.prefs.getStringPref(
    
    821 533
               TorSettingsPrefs.bridges.builtin_type,
    
    822 534
               ""
    
    823 535
             );
    
    824 536
             break;
    
    825 537
           case TorBridgeSource.Lox:
    
    826 538
             // bridge_strings is set via lox id.
    
    827
    -        this.bridges.lox_id = Services.prefs.getStringPref(
    
    539
    +        bridges.lox_id = Services.prefs.getStringPref(
    
    828 540
               TorSettingsPrefs.bridges.lox_id,
    
    829 541
               ""
    
    830 542
             );
    
    831 543
             break;
    
    832 544
         }
    
    545
    +    try {
    
    546
    +      this.#fixupBridgeSettings(bridges);
    
    547
    +      this.#settings.bridges = bridges;
    
    548
    +    } catch (error) {
    
    549
    +      lazy.logger.error("Loaded bridge preferences failed", error);
    
    550
    +      // Keep the default #settings.bridges.
    
    551
    +    }
    
    552
    +
    
    833 553
         /* Proxy */
    
    834
    -    this.proxy.enabled = Services.prefs.getBoolPref(
    
    554
    +    const proxy = {};
    
    555
    +    proxy.enabled = Services.prefs.getBoolPref(
    
    835 556
           TorSettingsPrefs.proxy.enabled,
    
    836 557
           false
    
    837 558
         );
    
    838
    -    if (this.proxy.enabled) {
    
    839
    -      this.proxy.type = Services.prefs.getIntPref(
    
    559
    +    if (proxy.enabled) {
    
    560
    +      proxy.type = Services.prefs.getIntPref(
    
    840 561
             TorSettingsPrefs.proxy.type,
    
    841 562
             TorProxyType.Invalid
    
    842 563
           );
    
    843
    -      this.proxy.address = Services.prefs.getStringPref(
    
    564
    +      proxy.address = Services.prefs.getStringPref(
    
    844 565
             TorSettingsPrefs.proxy.address,
    
    845 566
             ""
    
    846 567
           );
    
    847
    -      this.proxy.port = Services.prefs.getIntPref(
    
    848
    -        TorSettingsPrefs.proxy.port,
    
    849
    -        0
    
    850
    -      );
    
    851
    -      this.proxy.username = Services.prefs.getStringPref(
    
    568
    +      proxy.port = Services.prefs.getIntPref(TorSettingsPrefs.proxy.port, 0);
    
    569
    +      proxy.username = Services.prefs.getStringPref(
    
    852 570
             TorSettingsPrefs.proxy.username,
    
    853 571
             ""
    
    854 572
           );
    
    855
    -      this.proxy.password = Services.prefs.getStringPref(
    
    573
    +      proxy.password = Services.prefs.getStringPref(
    
    856 574
             TorSettingsPrefs.proxy.password,
    
    857 575
             ""
    
    858 576
           );
    
    859 577
         }
    
    578
    +    try {
    
    579
    +      this.#fixupProxySettings(proxy);
    
    580
    +      this.#settings.proxy = proxy;
    
    581
    +    } catch (error) {
    
    582
    +      lazy.logger.error("Loaded proxy preferences failed", error);
    
    583
    +      // Keep the default #settings.proxy.
    
    584
    +    }
    
    860 585
     
    
    861 586
         /* Firewall */
    
    862
    -    this.firewall.enabled = Services.prefs.getBoolPref(
    
    587
    +    const firewall = {};
    
    588
    +    firewall.enabled = Services.prefs.getBoolPref(
    
    863 589
           TorSettingsPrefs.firewall.enabled,
    
    864 590
           false
    
    865 591
         );
    
    866
    -    if (this.firewall.enabled) {
    
    867
    -      this.firewall.allowed_ports = Services.prefs.getStringPref(
    
    592
    +    if (firewall.enabled) {
    
    593
    +      firewall.allowed_ports = Services.prefs.getStringPref(
    
    868 594
             TorSettingsPrefs.firewall.allowed_ports,
    
    869 595
             ""
    
    870 596
           );
    
    871 597
         }
    
    872
    -
    
    873
    -    this.#cleanupSettings();
    
    598
    +    try {
    
    599
    +      this.#fixupFirewallSettings(firewall);
    
    600
    +      this.#settings.firewall = firewall;
    
    601
    +    } catch (error) {
    
    602
    +      lazy.logger.error("Loaded firewall preferences failed", error);
    
    603
    +      // Keep the default #settings.firewall.
    
    604
    +    }
    
    874 605
       }
    
    875 606
     
    
    876 607
       /**
    
    ... ... @@ -880,29 +611,28 @@ class TorSettingsImpl {
    880 611
         lazy.logger.debug("saveToPrefs()");
    
    881 612
     
    
    882 613
         this.#checkIfInitialized();
    
    883
    -    this.#cleanupSettings();
    
    884 614
     
    
    885 615
         /* Quickstart */
    
    886 616
         Services.prefs.setBoolPref(
    
    887 617
           TorSettingsPrefs.quickstart.enabled,
    
    888
    -      this.quickstart.enabled
    
    618
    +      this.#settings.quickstart.enabled
    
    889 619
         );
    
    890 620
         /* Bridges */
    
    891 621
         Services.prefs.setBoolPref(
    
    892 622
           TorSettingsPrefs.bridges.enabled,
    
    893
    -      this.bridges.enabled
    
    623
    +      this.#settings.bridges.enabled
    
    894 624
         );
    
    895 625
         Services.prefs.setIntPref(
    
    896 626
           TorSettingsPrefs.bridges.source,
    
    897
    -      this.bridges.source
    
    627
    +      this.#settings.bridges.source
    
    898 628
         );
    
    899 629
         Services.prefs.setStringPref(
    
    900 630
           TorSettingsPrefs.bridges.builtin_type,
    
    901
    -      this.bridges.builtin_type
    
    631
    +      this.#settings.bridges.builtin_type
    
    902 632
         );
    
    903 633
         Services.prefs.setStringPref(
    
    904 634
           TorSettingsPrefs.bridges.lox_id,
    
    905
    -      this.bridges.lox_id
    
    635
    +      this.#settings.bridges.lox_id
    
    906 636
         );
    
    907 637
         // erase existing bridge strings
    
    908 638
         const bridgeBranchPrefs = Services.prefs
    
    ... ... @@ -915,10 +645,10 @@ class TorSettingsImpl {
    915 645
         });
    
    916 646
         // write new ones
    
    917 647
         if (
    
    918
    -      this.bridges.source !== TorBridgeSource.Lox &&
    
    919
    -      this.bridges.source !== TorBridgeSource.BuiltIn
    
    648
    +      this.#settings.bridges.source !== TorBridgeSource.Lox &&
    
    649
    +      this.#settings.bridges.source !== TorBridgeSource.BuiltIn
    
    920 650
         ) {
    
    921
    -      this.bridges.bridge_strings.forEach((string, index) => {
    
    651
    +      this.#settings.bridges.bridge_strings.forEach((string, index) => {
    
    922 652
             Services.prefs.setStringPref(
    
    923 653
               `${TorSettingsPrefs.bridges.bridge_strings}.${index}`,
    
    924 654
               string
    
    ... ... @@ -928,22 +658,28 @@ class TorSettingsImpl {
    928 658
         /* Proxy */
    
    929 659
         Services.prefs.setBoolPref(
    
    930 660
           TorSettingsPrefs.proxy.enabled,
    
    931
    -      this.proxy.enabled
    
    661
    +      this.#settings.proxy.enabled
    
    932 662
         );
    
    933
    -    if (this.proxy.enabled) {
    
    934
    -      Services.prefs.setIntPref(TorSettingsPrefs.proxy.type, this.proxy.type);
    
    663
    +    if (this.#settings.proxy.enabled) {
    
    664
    +      Services.prefs.setIntPref(
    
    665
    +        TorSettingsPrefs.proxy.type,
    
    666
    +        this.#settings.proxy.type
    
    667
    +      );
    
    935 668
           Services.prefs.setStringPref(
    
    936 669
             TorSettingsPrefs.proxy.address,
    
    937
    -        this.proxy.address
    
    670
    +        this.#settings.proxy.address
    
    671
    +      );
    
    672
    +      Services.prefs.setIntPref(
    
    673
    +        TorSettingsPrefs.proxy.port,
    
    674
    +        this.#settings.proxy.port
    
    938 675
           );
    
    939
    -      Services.prefs.setIntPref(TorSettingsPrefs.proxy.port, this.proxy.port);
    
    940 676
           Services.prefs.setStringPref(
    
    941 677
             TorSettingsPrefs.proxy.username,
    
    942
    -        this.proxy.username
    
    678
    +        this.#settings.proxy.username
    
    943 679
           );
    
    944 680
           Services.prefs.setStringPref(
    
    945 681
             TorSettingsPrefs.proxy.password,
    
    946
    -        this.proxy.password
    
    682
    +        this.#settings.proxy.password
    
    947 683
           );
    
    948 684
         } else {
    
    949 685
           Services.prefs.clearUserPref(TorSettingsPrefs.proxy.type);
    
    ... ... @@ -955,12 +691,12 @@ class TorSettingsImpl {
    955 691
         /* Firewall */
    
    956 692
         Services.prefs.setBoolPref(
    
    957 693
           TorSettingsPrefs.firewall.enabled,
    
    958
    -      this.firewall.enabled
    
    694
    +      this.#settings.firewall.enabled
    
    959 695
         );
    
    960
    -    if (this.firewall.enabled) {
    
    696
    +    if (this.#settings.firewall.enabled) {
    
    961 697
           Services.prefs.setStringPref(
    
    962 698
             TorSettingsPrefs.firewall.allowed_ports,
    
    963
    -        this.firewall.allowed_ports.join(",")
    
    699
    +        this.#settings.firewall.allowed_ports.join(",")
    
    964 700
           );
    
    965 701
         } else {
    
    966 702
           Services.prefs.clearUserPref(TorSettingsPrefs.firewall.allowed_ports);
    
    ... ... @@ -977,11 +713,135 @@ class TorSettingsImpl {
    977 713
        * frontend consumers.
    
    978 714
        */
    
    979 715
       async #applySettings() {
    
    980
    -    this.#checkIfInitialized();
    
    981 716
         const provider = await lazy.TorProviderBuilder.build();
    
    982 717
         await provider.writeSettings();
    
    983 718
       }
    
    984 719
     
    
    720
    +  /**
    
    721
    +   * Fixup the given bridges settings to fill in details, establish the correct
    
    722
    +   * types and clean up.
    
    723
    +   *
    
    724
    +   * May throw if there is an error in the given values.
    
    725
    +   *
    
    726
    +   * @param {Object} bridges - The bridges settings to fix up.
    
    727
    +   */
    
    728
    +  #fixupBridgeSettings(bridges) {
    
    729
    +    if (!Object.values(TorBridgeSource).includes(bridges.source)) {
    
    730
    +      throw new Error(`Not a valid bridge source: "${bridges.source}"`);
    
    731
    +    }
    
    732
    +
    
    733
    +    if ("enabled" in bridges) {
    
    734
    +      bridges.enabled = Boolean(bridges.enabled);
    
    735
    +    }
    
    736
    +
    
    737
    +    // Set bridge_strings
    
    738
    +    switch (bridges.source) {
    
    739
    +      case TorBridgeSource.UserProvided:
    
    740
    +      case TorBridgeSource.BridgeDB:
    
    741
    +        // Only accept an Array for UserProvided and BridgeDB bridge_strings.
    
    742
    +        break;
    
    743
    +      case TorBridgeSource.BuiltIn:
    
    744
    +        bridges.builtin_type = String(bridges.builtin_type);
    
    745
    +        bridges.bridge_strings = this.#getBuiltinBridges(bridges.builtin_type);
    
    746
    +        break;
    
    747
    +      case TorBridgeSource.Lox:
    
    748
    +        bridges.lox_id = String(bridges.lox_id);
    
    749
    +        bridges.bridge_strings = lazy.Lox.getBridges(bridges.lox_id);
    
    750
    +        break;
    
    751
    +      case TorBridgeSource.Invalid:
    
    752
    +        bridges.bridge_strings = [];
    
    753
    +        break;
    
    754
    +    }
    
    755
    +
    
    756
    +    if (
    
    757
    +      !Array.isArray(bridges.bridge_strings) ||
    
    758
    +      bridges.bridge_strings.some(str => typeof str !== "string")
    
    759
    +    ) {
    
    760
    +      throw new Error("bridge_strings should be an Array of strings");
    
    761
    +    }
    
    762
    +
    
    763
    +    if (
    
    764
    +      bridges.source !== TorBridgeSource.Invalid &&
    
    765
    +      !bridges.bridge_strings?.length
    
    766
    +    ) {
    
    767
    +      throw new Error(
    
    768
    +        `Missing bridge_strings for bridge source ${bridges.source}`
    
    769
    +      );
    
    770
    +    }
    
    771
    +
    
    772
    +    if (bridges.source !== TorBridgeSource.BuiltIn) {
    
    773
    +      bridges.builtin_type = "";
    
    774
    +    }
    
    775
    +    if (bridges.source !== TorBridgeSource.Lox) {
    
    776
    +      bridges.lox_id = "";
    
    777
    +    }
    
    778
    +
    
    779
    +    if (bridges.source === TorBridgeSource.Invalid) {
    
    780
    +      bridges.enabled = false;
    
    781
    +    }
    
    782
    +  }
    
    783
    +
    
    784
    +  /**
    
    785
    +   * Fixup the given proxy settings to fill in details, establish the correct
    
    786
    +   * types and clean up.
    
    787
    +   *
    
    788
    +   * May throw if there is an error in the given values.
    
    789
    +   *
    
    790
    +   * @param {Object} proxy - The proxy settings to fix up.
    
    791
    +   */
    
    792
    +  #fixupProxySettings(proxy) {
    
    793
    +    proxy.enabled = Boolean(proxy.enabled);
    
    794
    +    if (!proxy.enabled) {
    
    795
    +      proxy.type = TorProxyType.Invalid;
    
    796
    +      proxy.address = "";
    
    797
    +      proxy.port = 0;
    
    798
    +      proxy.username = "";
    
    799
    +      proxy.password = "";
    
    800
    +      return;
    
    801
    +    }
    
    802
    +
    
    803
    +    if (!Object.values(TorProxyType).includes(proxy.type)) {
    
    804
    +      throw new Error(`Invalid proxy type: ${proxy.type}`);
    
    805
    +    }
    
    806
    +    proxy.port = this.#parsePort(proxy.port, false);
    
    807
    +    proxy.address = String(proxy.address);
    
    808
    +    proxy.username = String(proxy.username);
    
    809
    +    proxy.password = String(proxy.password);
    
    810
    +  }
    
    811
    +
    
    812
    +  /**
    
    813
    +   * Fixup the given firewall settings to fill in details, establish the correct
    
    814
    +   * types and clean up.
    
    815
    +   *
    
    816
    +   * May throw if there is an error in the given values.
    
    817
    +   *
    
    818
    +   * @param {Object} firewall - The proxy settings to fix up.
    
    819
    +   */
    
    820
    +  #fixupFirewallSettings(firewall) {
    
    821
    +    firewall.enabled = Boolean(firewall.enabled);
    
    822
    +    if (!firewall.enabled) {
    
    823
    +      firewall.allowed_ports = [];
    
    824
    +      return;
    
    825
    +    }
    
    826
    +
    
    827
    +    let allowed_ports = firewall.allowed_ports;
    
    828
    +    if (!Array.isArray(allowed_ports)) {
    
    829
    +      allowed_ports = allowed_ports === "" ? [] : allowed_ports.split(",");
    
    830
    +    }
    
    831
    +    // parse and remove duplicates
    
    832
    +    const portSet = new Set();
    
    833
    +
    
    834
    +    for (const port of allowed_ports) {
    
    835
    +      try {
    
    836
    +        portSet.add(this.#parsePort(port, true));
    
    837
    +      } catch (e) {
    
    838
    +        // Do not throw for individual ports.
    
    839
    +        lazy.logger.error(`Failed to parse the port ${port}. Ignoring.`, e);
    
    840
    +      }
    
    841
    +    }
    
    842
    +    firewall.allowed_ports = [...portSet];
    
    843
    +  }
    
    844
    +
    
    985 845
       /**
    
    986 846
        * Change the Tor settings in use.
    
    987 847
        *
    
    ... ... @@ -994,101 +854,128 @@ class TorSettingsImpl {
    994 854
        * + proxy settings can be set as a group.
    
    995 855
        * + firewall settings can be set a group.
    
    996 856
        *
    
    997
    -   * @param {object} settings - The settings object to set.
    
    857
    +   * @param {object} newValues - The new setting values, a subset of the
    
    858
    +   *   complete settings that should be changed.
    
    998 859
        */
    
    999
    -  async changeSettings(settings) {
    
    1000
    -    lazy.logger.debug("changeSettings()", settings);
    
    860
    +  async changeSettings(newValues) {
    
    861
    +    lazy.logger.debug("changeSettings()", newValues);
    
    1001 862
         this.#checkIfInitialized();
    
    1002 863
     
    
    1003
    -    const backup = this.getSettings();
    
    1004
    -    const backupNotifications = [...this.#notificationQueue];
    
    1005
    -    // Start collecting errors.
    
    1006
    -    this.#settingErrors = [];
    
    1007
    -
    
    1008
    -    // Hold off on lots of notifications until all settings are changed.
    
    1009
    -    this.#freezeNotifications();
    
    1010
    -    try {
    
    1011
    -      if ("quickstart" in settings && "enabled" in settings.quickstart) {
    
    1012
    -        this.quickstart.enabled = !!settings.quickstart.enabled;
    
    864
    +    // Make a structured clone since we change the object and may adopt some of
    
    865
    +    // the Array values.
    
    866
    +    newValues = structuredClone(newValues);
    
    867
    +
    
    868
    +    const completeSettings = structuredClone(this.#settings);
    
    869
    +    const changes = [];
    
    870
    +
    
    871
    +    /**
    
    872
    +     * Change the given setting to a new value. Does nothing if the new value
    
    873
    +     * equals the old one, otherwise the change will be recorded in `changes`.
    
    874
    +     *
    
    875
    +     * @param {string} group - The group name for the property.
    
    876
    +     * @param {string} prop - The property name within the group.
    
    877
    +     * @param {any} value - The value to set.
    
    878
    +     * @param [Function?] equal - A method to test equality between the old and
    
    879
    +     *   new value. Otherwise uses `===` to check equality.
    
    880
    +     */
    
    881
    +    const changeSetting = (group, prop, value, equal = null) => {
    
    882
    +      const currentValue = this.#settings[group][prop];
    
    883
    +      if (equal ? equal(currentValue, value) : currentValue === value) {
    
    884
    +        return;
    
    1013 885
           }
    
    886
    +      completeSettings[group][prop] = value;
    
    887
    +      changes.push(`${group}.${prop}`);
    
    888
    +    };
    
    1014 889
     
    
    1015
    -      if ("bridges" in settings) {
    
    1016
    -        if ("enabled" in settings.bridges) {
    
    1017
    -          this.bridges.enabled = !!settings.bridges.enabled;
    
    1018
    -        }
    
    1019
    -        if ("source" in settings.bridges) {
    
    1020
    -          this.bridges.source = settings.bridges.source;
    
    1021
    -          switch (settings.bridges.source) {
    
    1022
    -            case TorBridgeSource.BridgeDB:
    
    1023
    -            case TorBridgeSource.UserProvided:
    
    1024
    -              this.bridges.bridge_strings = settings.bridges.bridge_strings;
    
    1025
    -              break;
    
    1026
    -            case TorBridgeSource.BuiltIn:
    
    1027
    -              this.bridges.builtin_type = settings.bridges.builtin_type;
    
    1028
    -              break;
    
    1029
    -            case TorBridgeSource.Lox:
    
    1030
    -              this.bridges.lox_id = settings.bridges.lox_id;
    
    1031
    -              break;
    
    1032
    -            case TorBridgeSource.Invalid:
    
    1033
    -              break;
    
    1034
    -            case undefined:
    
    1035
    -              break;
    
    1036
    -          }
    
    1037
    -        }
    
    1038
    -      }
    
    890
    +    if ("quickstart" in newValues && "enabled" in newValues.quickstart) {
    
    891
    +      changeSetting(
    
    892
    +        "quickstart",
    
    893
    +        "enabled",
    
    894
    +        Boolean(newValues.quickstart.enabled)
    
    895
    +      );
    
    896
    +    }
    
    1039 897
     
    
    1040
    -      if ("proxy" in settings) {
    
    1041
    -        // proxy settings have to be set as a group.
    
    1042
    -        this.proxy.enabled = !!settings.proxy.enabled;
    
    1043
    -        if (this.proxy.enabled) {
    
    1044
    -          this.proxy.type = settings.proxy.type;
    
    1045
    -          this.proxy.address = settings.proxy.address;
    
    1046
    -          this.proxy.port = settings.proxy.port;
    
    1047
    -          this.proxy.username = settings.proxy.username;
    
    1048
    -          this.proxy.password = settings.proxy.password;
    
    898
    +    if ("bridges" in newValues) {
    
    899
    +      if ("source" in newValues.bridges) {
    
    900
    +        this.#fixupBridgeSettings(newValues.bridges);
    
    901
    +        changeSetting("bridges", "source", newValues.bridges.source);
    
    902
    +        changeSetting(
    
    903
    +          "bridges",
    
    904
    +          "bridge_strings",
    
    905
    +          newValues.bridges.bridge_strings,
    
    906
    +          this.#arrayEqual
    
    907
    +        );
    
    908
    +        changeSetting("bridges", "lox_id", newValues.bridges.lox_id);
    
    909
    +        changeSetting(
    
    910
    +          "bridges",
    
    911
    +          "builtin_type",
    
    912
    +          newValues.bridges.builtin_type
    
    913
    +        );
    
    914
    +      } else if ("enabled" in newValues.bridges) {
    
    915
    +        // Don't need to fixup all the settings, just need to ensure that the
    
    916
    +        // enabled value is compatible with the current source.
    
    917
    +        newValues.bridges.enabled = Boolean(newValues.bridges.enabled);
    
    918
    +        if (
    
    919
    +          newValues.bridges.enabled &&
    
    920
    +          completeSettings.bridges.source === TorBridgeSource.Invalid
    
    921
    +        ) {
    
    922
    +          throw new Error("Cannot enable bridges without a bridge source.");
    
    1049 923
             }
    
    1050 924
           }
    
    1051
    -
    
    1052
    -      if ("firewall" in settings) {
    
    1053
    -        // firewall settings have to be set as a group.
    
    1054
    -        this.firewall.enabled = !!settings.firewall.enabled;
    
    1055
    -        if (this.firewall.enabled) {
    
    1056
    -          this.firewall.allowed_ports = settings.firewall.allowed_ports;
    
    1057
    -        }
    
    925
    +      if ("enabled" in newValues.bridges) {
    
    926
    +        changeSetting("bridges", "enabled", newValues.bridges.enabled);
    
    1058 927
           }
    
    928
    +    }
    
    1059 929
     
    
    1060
    -      this.#cleanupSettings();
    
    930
    +    if ("proxy" in newValues) {
    
    931
    +      // proxy settings have to be set as a group.
    
    932
    +      this.#fixupProxySettings(newValues.proxy);
    
    933
    +      changeSetting("proxy", "enabled", Boolean(newValues.proxy.enabled));
    
    934
    +      changeSetting("proxy", "type", newValues.proxy.type);
    
    935
    +      changeSetting("proxy", "address", newValues.proxy.address);
    
    936
    +      changeSetting("proxy", "port", newValues.proxy.port);
    
    937
    +      changeSetting("proxy", "username", newValues.proxy.username);
    
    938
    +      changeSetting("proxy", "password", newValues.proxy.password);
    
    939
    +    }
    
    1061 940
     
    
    1062
    -      if (this.#settingErrors.length) {
    
    1063
    -        throw Error(this.#settingErrors.join("; "));
    
    1064
    -      }
    
    1065
    -      this.#saveToPrefs();
    
    1066
    -    } catch (ex) {
    
    1067
    -      // Restore the old settings without any new notifications generated from
    
    1068
    -      // the above code.
    
    1069
    -      // NOTE: Since the code that changes #settings is not async, it should not
    
    1070
    -      // be possible for some other call to TorSettings to change anything
    
    1071
    -      // whilst we are in this context (other than lower down in this call
    
    1072
    -      // stack), so it is safe to discard all changes to settings and
    
    1073
    -      // notifications.
    
    1074
    -      this.#settings = backup;
    
    1075
    -      this.#notificationQueue.clear();
    
    1076
    -      for (const notification of backupNotifications) {
    
    1077
    -        this.#notificationQueue.add(notification);
    
    1078
    -      }
    
    941
    +    if ("firewall" in newValues) {
    
    942
    +      // firewall settings have to be set as a group.
    
    943
    +      this.#fixupFirewallSettings(newValues.firewall);
    
    944
    +      changeSetting("firewall", "enabled", Boolean(newValues.firewall.enabled));
    
    945
    +      changeSetting(
    
    946
    +        "firewall",
    
    947
    +        "allowed_ports",
    
    948
    +        newValues.firewall.allowed_ports,
    
    949
    +        this.#arrayEqual
    
    950
    +      );
    
    951
    +    }
    
    952
    +
    
    953
    +    // No errors so far, so save and commit.
    
    954
    +    this.#settings = completeSettings;
    
    955
    +    this.#saveToPrefs();
    
    1079 956
     
    
    1080
    -      throw ex;
    
    1081
    -    } finally {
    
    1082
    -      this.#thawNotifications();
    
    1083
    -      // Stop collecting errors.
    
    1084
    -      this.#settingErrors = null;
    
    957
    +    if (changes.length) {
    
    958
    +      Services.obs.notifyObservers(
    
    959
    +        { changes },
    
    960
    +        TorSettingsTopics.SettingsChanged
    
    961
    +      );
    
    1085 962
         }
    
    1086 963
     
    
    1087
    -    lazy.logger.debug("setSettings result", this.#settings);
    
    964
    +    lazy.logger.debug("setSettings result", this.#settings, changes);
    
    1088 965
     
    
    1089 966
         // After we have sent out the notifications for the changed settings and
    
    1090 967
         // saved the preferences we send the new settings to TorProvider.
    
    1091
    -    await this.#applySettings();
    
    968
    +    // Some properties are unread by TorProvider. So if only these values change
    
    969
    +    // there is no need to re-apply the settings.
    
    970
    +    const unreadProps = [
    
    971
    +      "quickstart.enabled",
    
    972
    +      "bridges.builtin_type",
    
    973
    +      "bridges.lox_id",
    
    974
    +    ];
    
    975
    +    const shouldApply = changes.some(prop => !unreadProps.includes(prop));
    
    976
    +    if (shouldApply) {
    
    977
    +      await this.#applySettings();
    
    978
    +    }
    
    1092 979
       }
    
    1093 980
     
    
    1094 981
       /**
    
    ... ... @@ -1147,29 +1034,11 @@ class TorSettingsImpl {
    1147 1034
         const bridgeSettings = {
    
    1148 1035
           enabled: true,
    
    1149 1036
           source: bridges.source,
    
    1037
    +      builtin_type: String(bridges.builtin_type),
    
    1038
    +      bridge_strings: structuredClone(bridges.bridge_strings),
    
    1150 1039
         };
    
    1151 1040
     
    
    1152
    -    if (bridges.source === TorBridgeSource.BuiltIn) {
    
    1153
    -      if (!bridges.builtin_type) {
    
    1154
    -        throw Error("Missing a built-in type");
    
    1155
    -      }
    
    1156
    -      bridgeSettings.builtin_type = String(bridges.builtin_type);
    
    1157
    -      const bridgeStrings = this.getBuiltinBridges(bridgeSettings.builtin_type);
    
    1158
    -      if (!bridgeStrings.length) {
    
    1159
    -        throw new Error(`No builtin bridges for type ${bridges.builtin_type}`);
    
    1160
    -      }
    
    1161
    -      bridgeSettings.bridge_strings = bridgeStrings;
    
    1162
    -    } else {
    
    1163
    -      // BridgeDB.
    
    1164
    -      if (!bridges.bridge_strings?.length) {
    
    1165
    -        throw new Error("Missing bridges strings");
    
    1166
    -      }
    
    1167
    -      // TODO: Can we safely verify the format of the bridge addresses sent from
    
    1168
    -      // Moat?
    
    1169
    -      bridgeSettings.bridge_strings = Array.from(bridges.bridge_strings, item =>
    
    1170
    -        String(item)
    
    1171
    -      );
    
    1172
    -    }
    
    1041
    +    this.#fixupBridgeSettings(bridgeSettings);
    
    1173 1042
     
    
    1174 1043
         // After checks are complete, we commit them.
    
    1175 1044
         this.#temporaryBridgeSettings = bridgeSettings;
    

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