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

Commits:

4 changed files:

Changes:

  • toolkit/components/tor-launcher/TorProvider.sys.mjs
    ... ... @@ -227,7 +227,7 @@ export class TorProvider {
    227 227
         if (this.ownsTorDaemon) {
    
    228 228
           try {
    
    229 229
             await lazy.TorSettings.initializedPromise;
    
    230
    -        await this.writeSettings(lazy.TorSettings.getSettings());
    
    230
    +        await this.writeSettings();
    
    231 231
           } catch (e) {
    
    232 232
             logger.warn(
    
    233 233
               "Failed to initialize TorSettings or to write our initial settings. Continuing the initialization anyway.",
    
    ... ... @@ -269,11 +269,13 @@ export class TorProvider {
    269 269
       /**
    
    270 270
        * Send settings to the tor daemon.
    
    271 271
        *
    
    272
    -   * @param {object} settings A settings object, as returned by
    
    273
    -   * TorSettings.getSettings(). This allow to try settings without passing
    
    274
    -   * through TorSettings.
    
    272
    +   * This should only be called internally or by the TorSettings module.
    
    275 273
        */
    
    276
    -  async writeSettings(settings) {
    
    274
    +  async writeSettings() {
    
    275
    +    // Fetch the current settings.
    
    276
    +    // We set the useTemporary parameter since we want to apply temporary
    
    277
    +    // bridges if they are available.
    
    278
    +    const settings = lazy.TorSettings.getSettings(true);
    
    277 279
         logger.debug("TorProvider.writeSettings", settings);
    
    278 280
         const torSettings = new Map();
    
    279 281
     
    

  • toolkit/modules/Moat.sys.mjs
    ... ... @@ -13,7 +13,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
    13 13
       DomainFrontRequestBuilder:
    
    14 14
         "resource://gre/modules/DomainFrontedRequests.sys.mjs",
    
    15 15
       TorBridgeSource: "resource://gre/modules/TorSettings.sys.mjs",
    
    16
    -  TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
    
    17 16
     });
    
    18 17
     
    
    19 18
     const TorLauncherPrefs = Object.freeze({
    
    ... ... @@ -73,6 +72,25 @@ class InternetTestResponseListener {
    73 72
       }
    
    74 73
     }
    
    75 74
     
    
    75
    +/**
    
    76
    + * @typedef {Object} MoatBridges
    
    77
    + *
    
    78
    + * Bridge settings that can be passed to TorSettings.bridges.
    
    79
    + *
    
    80
    + * @property {number} source - The `TorBridgeSource` type.
    
    81
    + * @property {string} [builtin_type] - The built-in bridge type.
    
    82
    + * @property {string[]} [bridge_strings] - The bridge lines.
    
    83
    + */
    
    84
    +
    
    85
    +/**
    
    86
    + * @typedef {Object} MoatSettings
    
    87
    + *
    
    88
    + * The settings returned by Moat.
    
    89
    + *
    
    90
    + * @property {MoatBridges[]} bridgesList - The list of bridges found.
    
    91
    + * @property {string} [country] - The detected country (region).
    
    92
    + */
    
    93
    +
    
    76 94
     /**
    
    77 95
      * Constructs JSON objects and sends requests over Moat.
    
    78 96
      * The documentation about the JSON schemas to use are available at
    
    ... ... @@ -213,37 +231,31 @@ export class MoatRPC {
    213 231
         return { bridges, qrcode: qrcodeImg };
    
    214 232
       }
    
    215 233
     
    
    216
    -  // Convert received settings object to format used by TorSettings module.
    
    217
    -  #fixupSettings(settings) {
    
    234
    +  /**
    
    235
    +   * Extract bridges from the received Moat settings object.
    
    236
    +   *
    
    237
    +   * @param {Object} settings - The received settings.
    
    238
    +   * @return {MoatBridge} The extracted bridges.
    
    239
    +   */
    
    240
    +  #extractBridges(settings) {
    
    218 241
         if (!("bridges" in settings)) {
    
    219 242
           throw new Error("Expected to find `bridges` in the settings object.");
    
    220 243
         }
    
    221
    -    const retval = {
    
    222
    -      bridges: {
    
    223
    -        enabled: true,
    
    224
    -      },
    
    225
    -    };
    
    244
    +    const bridges = {};
    
    226 245
         switch (settings.bridges.source) {
    
    227 246
           case "builtin":
    
    228
    -        retval.bridges.source = lazy.TorBridgeSource.BuiltIn;
    
    229
    -        retval.bridges.builtin_type = settings.bridges.type;
    
    230
    -        // TorSettings will ignore strings for built-in bridges, and use the
    
    231
    -        // ones it already knows, instead. However, when we try these settings
    
    232
    -        // in the connect assist, we skip TorSettings. Therefore, we set the
    
    233
    -        // lines also here (the ones we already know, not the ones we receive
    
    234
    -        // from Moat). This needs TorSettings to be initialized, which by now
    
    235
    -        // should have already happened (this method is used only by TorConnect,
    
    236
    -        // that needs TorSettings to be initialized).
    
    237
    -        // In any case, getBuiltinBridges will throw if the data is not ready,
    
    238
    -        // yet.
    
    239
    -        retval.bridges.bridge_strings = lazy.TorSettings.getBuiltinBridges(
    
    240
    -          settings.bridges.type
    
    241
    -        );
    
    247
    +        bridges.source = lazy.TorBridgeSource.BuiltIn;
    
    248
    +        bridges.builtin_type = String(settings.bridges.type);
    
    249
    +        // Ignore the bridge_strings argument since we will use our built-in
    
    250
    +        // bridge strings instead.
    
    242 251
             break;
    
    243 252
           case "bridgedb":
    
    244
    -        retval.bridges.source = lazy.TorBridgeSource.BridgeDB;
    
    245
    -        if (settings.bridges.bridge_strings) {
    
    246
    -          retval.bridges.bridge_strings = settings.bridges.bridge_strings;
    
    253
    +        bridges.source = lazy.TorBridgeSource.BridgeDB;
    
    254
    +        if (settings.bridges.bridge_strings?.length) {
    
    255
    +          bridges.bridge_strings = Array.from(
    
    256
    +            settings.bridges.bridge_strings,
    
    257
    +            item => String(item)
    
    258
    +          );
    
    247 259
             } else {
    
    248 260
               throw new Error(
    
    249 261
                 "Received no bridge-strings for BridgeDB bridge source"
    
    ... ... @@ -255,37 +267,38 @@ export class MoatRPC {
    255 267
               `Unexpected bridge source '${settings.bridges.source}'`
    
    256 268
             );
    
    257 269
         }
    
    258
    -    return retval;
    
    270
    +    return bridges;
    
    259 271
       }
    
    260 272
     
    
    261
    -  // Converts a list of settings objects received from BridgeDB to a list of
    
    262
    -  // settings objects understood by the TorSettings module.
    
    263
    -  // In the event of error, returns an empty list.
    
    264
    -  #fixupSettingsList(settingsList) {
    
    265
    -    const retval = [];
    
    273
    +  /**
    
    274
    +   * Extract a list of bridges from the received Moat settings object.
    
    275
    +   *
    
    276
    +   * @param {Object} settings - The received settings.
    
    277
    +   * @return {MoatBridge[]} The list of extracted bridges.
    
    278
    +   */
    
    279
    +  #extractBridgesList(settingsList) {
    
    280
    +    const bridgesList = [];
    
    266 281
         for (const settings of settingsList) {
    
    267 282
           try {
    
    268
    -        retval.push(this.#fixupSettings(settings));
    
    283
    +        bridgesList.push(this.#extractBridges(settings));
    
    269 284
           } catch (ex) {
    
    270 285
             log.error(ex);
    
    271 286
           }
    
    272 287
         }
    
    273
    -    return retval;
    
    288
    +    return bridgesList;
    
    274 289
       }
    
    275 290
     
    
    276
    -  // Request tor settings for the user optionally based on their location
    
    277
    -  // (derived from their IP). Takes the following parameters:
    
    278
    -  // - transports: optional, an array of transports available to the client; if
    
    279
    -  //   empty (or not given) returns settings using all working transports known
    
    280
    -  //   to the server
    
    281
    -  // - country: optional, an ISO 3166-1 alpha-2 country code to request settings
    
    282
    -  //   for; if not provided the country is determined by the user's IP address
    
    283
    -  //
    
    284
    -  // Returns an object with the detected country code and an array of settings
    
    285
    -  // in a format that can be passed to the TorSettings module. This array might
    
    286
    -  // be empty if the country has no associated settings.
    
    287
    -  // If the server cannot determine the user's country (and no country code is
    
    288
    -  // provided), then null is returned instead of the object.
    
    291
    +  /**
    
    292
    +   * Request tor settings for the user optionally based on their location
    
    293
    +   * (derived from their IP). Takes the following parameters:
    
    294
    +   *
    
    295
    +   * @param {string[]} transports - A list of transports we support.
    
    296
    +   * @param {?string} country - The region to request bridges for, as an
    
    297
    +   *   ISO 3166-1 alpha-2 region code, or `null` to have the server
    
    298
    +   *   automatically determine the region.
    
    299
    +   * @returns {?MoatSettings} - The returned settings from the server, or `null`
    
    300
    +   *   if the region could not be determined by the server.
    
    301
    +   */
    
    289 302
       async circumvention_settings(transports, country) {
    
    290 303
         const args = {
    
    291 304
           transports: transports ? transports : [],
    
    ... ... @@ -306,7 +319,7 @@ export class MoatRPC {
    306 319
     
    
    307 320
           throw new Error(`MoatRPC: ${detail} (${code})`);
    
    308 321
         } else if ("settings" in response) {
    
    309
    -      settings.settings = this.#fixupSettingsList(response.settings);
    
    322
    +      settings.bridgesList = this.#extractBridgesList(response.settings);
    
    310 323
         }
    
    311 324
         if ("country" in response) {
    
    312 325
           settings.country = response.country;
    
    ... ... @@ -349,14 +362,12 @@ export class MoatRPC {
    349 362
         return map;
    
    350 363
       }
    
    351 364
     
    
    352
    -  // Request a copy of the defaul/fallback bridge settings, takes the following
    
    353
    -  // parameters:
    
    354
    -  // - transports: optional, an array of transports available to the client; if
    
    355
    -  //   empty (or not given) returns settings using all working transports known
    
    356
    -  //   to the server
    
    357
    -  //
    
    358
    -  // returns an array of settings objects in roughly the same format as the
    
    359
    -  // _settings object on the TorSettings module
    
    365
    +  /**
    
    366
    +   * Request a copy of the default/fallback bridge settings.
    
    367
    +   *
    
    368
    +   * @param {string[]} transports - A list of transports we support.
    
    369
    +   * @returns {MoatBridges[]} - The list of bridges found.
    
    370
    +   */
    
    360 371
       async circumvention_defaults(transports) {
    
    361 372
         const args = {
    
    362 373
           transports: transports ? transports : [],
    
    ... ... @@ -367,7 +378,7 @@ export class MoatRPC {
    367 378
           const detail = response.errors[0].detail;
    
    368 379
           throw new Error(`MoatRPC: ${detail} (${code})`);
    
    369 380
         } else if ("settings" in response) {
    
    370
    -      return this.#fixupSettingsList(response.settings);
    
    381
    +      return this.#extractBridgesList(response.settings);
    
    371 382
         }
    
    372 383
         return [];
    
    373 384
       }
    

  • toolkit/modules/TorConnect.sys.mjs
    ... ... @@ -103,11 +103,12 @@ export const TorConnectTopics = Object.freeze({
    103 103
      *   failing bootstrap.
    
    104 104
      * @property {integer} [options.simulateDelay] - The delay in microseconds to
    
    105 105
      *   apply to simulated bootstraps.
    
    106
    - * @property {object} [options.simulateMoatResponse] - Simulate a Moat response
    
    107
    - *   for circumvention settings. Should include a "settings" property, and
    
    108
    - *   optionally a "country" property. You may add a "simulateCensorship"
    
    109
    - *   property to some of the settings to make only their bootstrap attempts
    
    110
    - *   fail.
    
    106
    + * @property {MoatSettings} [options.simulateMoatResponse] - Simulate a Moat
    
    107
    + *   response for circumvention settings. Should include a "bridgesList"
    
    108
    + *   property, and optionally a "country" property. The "bridgesList" property
    
    109
    + *   should be an Array of MoatBridges objects that match the bridge settings
    
    110
    + *   accepted by TorSettings.bridges, plus you may add a "simulateCensorship"
    
    111
    + *   property to make only their bootstrap attempts fail.
    
    111 112
      * @property {boolean} [options.testInternet] - Whether to also test the
    
    112 113
      *   internet connection.
    
    113 114
      * @property {boolean} [options.simulateOffline] - Whether to simulate an
    
    ... ... @@ -214,6 +215,14 @@ class BootstrapAttempt {
    214 215
         return promise;
    
    215 216
       }
    
    216 217
     
    
    218
    +  /**
    
    219
    +   * Method to call just after the Bootstrapped stage is set in response to this
    
    220
    +   * bootstrap attempt.
    
    221
    +   */
    
    222
    +  postBootstrapped() {
    
    223
    +    // Nothing to do.
    
    224
    +  }
    
    225
    +
    
    217 226
       /**
    
    218 227
        * Run the attempt.
    
    219 228
        *
    
    ... ... @@ -395,17 +404,11 @@ class AutoBootstrapAttempt {
    395 404
        */
    
    396 405
       #cancelledPromise = null;
    
    397 406
       /**
    
    398
    -   * The found settings from Moat.
    
    399
    -   *
    
    400
    -   * @type {?object[]}
    
    401
    -   */
    
    402
    -  #settings = null;
    
    403
    -  /**
    
    404
    -   * The last settings that have been applied to the TorProvider, if any.
    
    407
    +   * The list of bridge configurations from Moat.
    
    405 408
        *
    
    406
    -   * @type {?object}
    
    409
    +   * @type {?MoatBridges[]}
    
    407 410
        */
    
    408
    -  #changedSetting = null;
    
    411
    +  #bridgesList = null;
    
    409 412
       /**
    
    410 413
        * The detected region code returned by Moat, if any.
    
    411 414
        *
    
    ... ... @@ -438,13 +441,8 @@ class AutoBootstrapAttempt {
    438 441
             // Run cleanup before we resolve the promise to ensure two instances
    
    439 442
             // of AutoBootstrapAttempt are not trying to change the settings at
    
    440 443
             // the same time.
    
    441
    -        if (this.#changedSetting) {
    
    442
    -          if (arg.result === "complete") {
    
    443
    -            // Persist the current settings to preferences.
    
    444
    -            lazy.TorSettings.setSettings(this.#changedSetting);
    
    445
    -            lazy.TorSettings.saveToPrefs();
    
    446
    -          } // else, applySettings will restore the current settings.
    
    447
    -          await lazy.TorSettings.applySettings();
    
    444
    +        if (arg.result !== "complete") {
    
    445
    +          await lazy.TorSettings.clearTemporaryBridges();
    
    448 446
             }
    
    449 447
           } catch (error) {
    
    450 448
             lazy.logger.error("Unexpected error in auto-bootstrap cleanup", error);
    
    ... ... @@ -466,6 +464,15 @@ class AutoBootstrapAttempt {
    466 464
         return promise;
    
    467 465
       }
    
    468 466
     
    
    467
    +  /**
    
    468
    +   * Method to call just after the Bootstrapped stage is set in response to this
    
    469
    +   * bootstrap attempt.
    
    470
    +   */
    
    471
    +  postBootstrapped() {
    
    472
    +    // Persist the current settings to preferences.
    
    473
    +    lazy.TorSettings.saveTemporaryBridges();
    
    474
    +  }
    
    475
    +
    
    469 476
       /**
    
    470 477
        * Run the attempt.
    
    471 478
        *
    
    ... ... @@ -477,12 +484,12 @@ class AutoBootstrapAttempt {
    477 484
        * @param {BootstrapOptions} options - Options to apply to the bootstrap.
    
    478 485
        */
    
    479 486
       async #runInternal(progressCallback, options) {
    
    480
    -    await this.#fetchSettings(options);
    
    487
    +    await this.#fetchBridges(options);
    
    481 488
         if (this.#cancelled || this.#resolved) {
    
    482 489
           return;
    
    483 490
         }
    
    484 491
     
    
    485
    -    if (!this.#settings?.length) {
    
    492
    +    if (!this.#bridgesList?.length) {
    
    486 493
           this.#resolveRun({
    
    487 494
             error: new TorConnectError(
    
    488 495
               options.regionCode === "automatic" && !this.detectedRegion
    
    ... ... @@ -493,14 +500,14 @@ class AutoBootstrapAttempt {
    493 500
         }
    
    494 501
     
    
    495 502
         // Apply each of our settings and try to bootstrap with each.
    
    496
    -    for (const [index, currentSetting] of this.#settings.entries()) {
    
    503
    +    for (const [index, bridges] of this.#bridgesList.entries()) {
    
    497 504
           lazy.logger.info(
    
    498 505
             `Attempting Bootstrap with configuration ${index + 1}/${
    
    499
    -          this.#settings.length
    
    506
    +          this.#bridgesList.length
    
    500 507
             }`
    
    501 508
           );
    
    502 509
     
    
    503
    -      await this.#trySetting(currentSetting, progressCallback, options);
    
    510
    +      await this.#tryBridges(bridges, progressCallback, options);
    
    504 511
     
    
    505 512
           if (this.#cancelled || this.#resolved) {
    
    506 513
             return;
    
    ... ... @@ -518,7 +525,7 @@ class AutoBootstrapAttempt {
    518 525
        *
    
    519 526
        * @param {BootstrapOptions} options - Options to apply to the bootstrap.
    
    520 527
        */
    
    521
    -  async #fetchSettings(options) {
    
    528
    +  async #fetchBridges(options) {
    
    522 529
         if (options.simulateMoatResponse) {
    
    523 530
           await Promise.race([
    
    524 531
             new Promise(res => setTimeout(res, options.simulateDelay || 0)),
    
    ... ... @@ -530,7 +537,7 @@ class AutoBootstrapAttempt {
    530 537
           }
    
    531 538
     
    
    532 539
           this.detectedRegion = options.simulateMoatResponse.country || null;
    
    533
    -      this.#settings = options.simulateMoatResponse.settings ?? null;
    
    540
    +      this.#bridgesList = options.simulateMoatResponse.bridgesList ?? null;
    
    534 541
     
    
    535 542
           return;
    
    536 543
         }
    
    ... ... @@ -564,16 +571,16 @@ class AutoBootstrapAttempt {
    564 571
     
    
    565 572
           this.detectedRegion = maybeSettings?.country || null;
    
    566 573
     
    
    567
    -      if (maybeSettings?.settings?.length) {
    
    568
    -        this.#settings = maybeSettings.settings;
    
    574
    +      if (maybeSettings?.bridgesList?.length) {
    
    575
    +        this.#bridgesList = maybeSettings.bridgesList;
    
    569 576
           } else {
    
    570 577
             // Keep consistency with the other call.
    
    571
    -        this.#settings = await Promise.race([
    
    578
    +        this.#bridgesList = await Promise.race([
    
    572 579
               moat.circumvention_defaults([
    
    573 580
                 ...lazy.TorSettings.builtinBridgeTypes,
    
    574 581
                 "vanilla",
    
    575 582
               ]),
    
    576
    -          // This might set this.#settings to undefined.
    
    583
    +          // This might set this.#bridgesList to undefined.
    
    577 584
               this.#cancelledPromise,
    
    578 585
             ]);
    
    579 586
           }
    
    ... ... @@ -586,21 +593,21 @@ class AutoBootstrapAttempt {
    586 593
       /**
    
    587 594
        * Try to apply the settings we fetched.
    
    588 595
        *
    
    589
    -   * @param {object} setting - The setting to try.
    
    596
    +   * @param {MoatBridges} bridges - The bridges to try.
    
    590 597
        * @param {ProgressCallback} progressCallback - The callback to invoke with
    
    591 598
        *   the bootstrap progress.
    
    592 599
        * @param {BootstrapOptions} options - Options to apply to the bootstrap.
    
    593 600
        */
    
    594
    -  async #trySetting(setting, progressCallback, options) {
    
    601
    +  async #tryBridges(bridges, progressCallback, options) {
    
    595 602
         if (this.#cancelled || this.#resolved) {
    
    596 603
           return;
    
    597 604
         }
    
    598 605
     
    
    599
    -    if (options.simulateMoatResponse && setting.simulateCensorship) {
    
    606
    +    if (options.simulateMoatResponse && bridges.simulateCensorship) {
    
    600 607
           // Move the simulateCensorship option to the options for the next
    
    601 608
           // BootstrapAttempt.
    
    602
    -      setting = structuredClone(setting);
    
    603
    -      delete setting.simulateCensorship;
    
    609
    +      bridges = structuredClone(bridges);
    
    610
    +      delete bridges.simulateCensorship;
    
    604 611
           options = { ...options, simulateCensorship: true };
    
    605 612
         }
    
    606 613
     
    
    ... ... @@ -616,14 +623,7 @@ class AutoBootstrapAttempt {
    616 623
         // Another idea (maybe easier to implement) is to disable the settings
    
    617 624
         // UI while *any* bootstrap is going on.
    
    618 625
         // This is also documented in tor-browser#41921.
    
    619
    -    const provider = await lazy.TorProviderBuilder.build();
    
    620
    -    this.#changedSetting = setting;
    
    621
    -    // We need to merge with old settings, in case the user is using a proxy
    
    622
    -    // or is behind a firewall.
    
    623
    -    await provider.writeSettings({
    
    624
    -      ...lazy.TorSettings.getSettings(),
    
    625
    -      ...setting,
    
    626
    -    });
    
    626
    +    await lazy.TorSettings.applyTemporaryBridges(bridges);
    
    627 627
     
    
    628 628
         if (this.#cancelled || this.#resolved) {
    
    629 629
           return;
    
    ... ... @@ -642,7 +642,7 @@ class AutoBootstrapAttempt {
    642 642
             error instanceof TorConnectError &&
    
    643 643
             error.code === TorConnectError.BootstrapError
    
    644 644
           ) {
    
    645
    -        lazy.logger.info("TorConnect setting failed", setting, error);
    
    645
    +        lazy.logger.info("TorConnect setting failed", bridges, error);
    
    646 646
             // Try with the next settings.
    
    647 647
             // NOTE: We do not restore the user settings in between these runs.
    
    648 648
             // Instead we wait for #resolveRun callback to do so.
    
    ... ... @@ -1459,6 +1459,12 @@ export const TorConnect = {
    1459 1459
           }
    
    1460 1460
           this._setStage(TorConnectStage.Bootstrapped);
    
    1461 1461
           Services.obs.notifyObservers(null, TorConnectTopics.BootstrapComplete);
    
    1462
    +
    
    1463
    +      // Now call the postBootstrapped method. We do this after changing the
    
    1464
    +      // stage to ensure that AutoBootstrapAttempt.postBootstrapped call to
    
    1465
    +      // saveTemporaryBridges does not trigger SettingsChanged too early and
    
    1466
    +      // cancel itself.
    
    1467
    +      bootstrapAttempt.postBootstrapped();
    
    1462 1468
           return;
    
    1463 1469
         }
    
    1464 1470
     
    

  • toolkit/modules/TorSettings.sys.mjs
    ... ... @@ -198,6 +198,14 @@ class TorSettingsImpl {
    198 198
           allowed_ports: [],
    
    199 199
         },
    
    200 200
       };
    
    201
    +
    
    202
    +  /**
    
    203
    +   * Temporary bridge settings to apply instead of #settings.bridges.
    
    204
    +   *
    
    205
    +   * @type {?Object}
    
    206
    +   */
    
    207
    +  #temporaryBridgeSettings = null;
    
    208
    +
    
    201 209
       /**
    
    202 210
        * Accumulated errors from trying to set settings.
    
    203 211
        *
    
    ... ... @@ -713,6 +721,8 @@ class TorSettingsImpl {
    713 721
           try {
    
    714 722
             this.#allowUninitialized = true;
    
    715 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.
    
    716 726
           } finally {
    
    717 727
             this.#allowUninitialized = false;
    
    718 728
             this.#notificationQueue.clear();
    
    ... ... @@ -971,7 +981,7 @@ class TorSettingsImpl {
    971 981
       async applySettings() {
    
    972 982
         this.#checkIfInitialized();
    
    973 983
         const provider = await lazy.TorProviderBuilder.build();
    
    974
    -    await provider.writeSettings(this.getSettings());
    
    984
    +    await provider.writeSettings();
    
    975 985
       }
    
    976 986
     
    
    977 987
       /**
    
    ... ... @@ -1073,12 +1083,20 @@ class TorSettingsImpl {
    1073 1083
       /**
    
    1074 1084
        * Get a copy of all our settings.
    
    1075 1085
        *
    
    1086
    +   * @param {boolean} [useTemporary=false] - Whether the returned settings
    
    1087
    +   *   should use the temporary bridge settings, if any, instead.
    
    1088
    +   *
    
    1076 1089
        * @returns {object} A copy of the settings object
    
    1077 1090
        */
    
    1078
    -  getSettings() {
    
    1091
    +  getSettings(useTemporary = false) {
    
    1079 1092
         lazy.logger.debug("getSettings()");
    
    1080 1093
         this.#checkIfInitialized();
    
    1081
    -    return structuredClone(this.#settings);
    
    1094
    +    const settings = structuredClone(this.#settings);
    
    1095
    +    if (useTemporary && this.#temporaryBridgeSettings) {
    
    1096
    +      // Override the bridge settings with our temporary ones.
    
    1097
    +      settings.bridges = structuredClone(this.#temporaryBridgeSettings);
    
    1098
    +    }
    
    1099
    +    return settings;
    
    1082 1100
       }
    
    1083 1101
     
    
    1084 1102
       /**
    
    ... ... @@ -1097,6 +1115,83 @@ class TorSettingsImpl {
    1097 1115
         }
    
    1098 1116
         return types;
    
    1099 1117
       }
    
    1118
    +
    
    1119
    +  /**
    
    1120
    +   * Apply some Moat bridges temporarily.
    
    1121
    +   *
    
    1122
    +   * These bridges will not yet be saved to settings.
    
    1123
    +   *
    
    1124
    +   * @param {MoatBridges} bridges - The bridges to apply.
    
    1125
    +   */
    
    1126
    +  async applyTemporaryBridges(bridges) {
    
    1127
    +    this.#checkIfInitialized();
    
    1128
    +
    
    1129
    +    if (
    
    1130
    +      bridges.source !== TorBridgeSource.BuiltIn &&
    
    1131
    +      bridges.source !== TorBridgeSource.BridgeDB
    
    1132
    +    ) {
    
    1133
    +      throw new Error(`Invalid bridge source ${bridges.source}`);
    
    1134
    +    }
    
    1135
    +
    
    1136
    +    const bridgeSettings = {
    
    1137
    +      enabled: true,
    
    1138
    +      source: bridges.source,
    
    1139
    +    };
    
    1140
    +
    
    1141
    +    if (bridges.source === TorBridgeSource.BuiltIn) {
    
    1142
    +      if (!bridges.builtin_type) {
    
    1143
    +        throw Error("Missing a built-in type");
    
    1144
    +      }
    
    1145
    +      bridgeSettings.builtin_type = String(bridges.builtin_type);
    
    1146
    +      const bridgeStrings = this.getBuiltinBridges(bridgeSettings.builtin_type);
    
    1147
    +      if (!bridgeStrings.length) {
    
    1148
    +        throw new Error(`No builtin bridges for type ${bridges.builtin_type}`);
    
    1149
    +      }
    
    1150
    +      bridgeSettings.bridge_strings = bridgeStrings;
    
    1151
    +    } else {
    
    1152
    +      // BridgeDB.
    
    1153
    +      if (!bridges.bridge_strings?.length) {
    
    1154
    +        throw new Error("Missing bridges strings");
    
    1155
    +      }
    
    1156
    +      // TODO: Can we safely verify the format of the bridge addresses sent from
    
    1157
    +      // Moat?
    
    1158
    +      bridgeSettings.bridge_strings = Array.from(bridges.bridge_strings, item =>
    
    1159
    +        String(item)
    
    1160
    +      );
    
    1161
    +    }
    
    1162
    +
    
    1163
    +    // After checks are complete, we commit them.
    
    1164
    +    this.#temporaryBridgeSettings = bridgeSettings;
    
    1165
    +    await this.applySettings();
    
    1166
    +  }
    
    1167
    +
    
    1168
    +  /**
    
    1169
    +   * Save to current temporary bridges to be permanent instead.
    
    1170
    +   */
    
    1171
    +  async saveTemporaryBridges() {
    
    1172
    +    this.#checkIfInitialized();
    
    1173
    +    if (!this.#temporaryBridgeSettings) {
    
    1174
    +      lazy.logger.warn("No temporary bridges to save");
    
    1175
    +      return;
    
    1176
    +    }
    
    1177
    +    this.setSettings({ bridges: this.#temporaryBridgeSettings });
    
    1178
    +    this.#temporaryBridgeSettings = null;
    
    1179
    +    this.saveToPrefs();
    
    1180
    +    await this.applySettings();
    
    1181
    +  }
    
    1182
    +
    
    1183
    +  /**
    
    1184
    +   * Clear the current temporary bridges.
    
    1185
    +   */
    
    1186
    +  async clearTemporaryBridges() {
    
    1187
    +    this.#checkIfInitialized();
    
    1188
    +    if (!this.#temporaryBridgeSettings) {
    
    1189
    +      lazy.logger.debug("No temporary bridges to clear");
    
    1190
    +      return;
    
    1191
    +    }
    
    1192
    +    this.#temporaryBridgeSettings = null;
    
    1193
    +    await this.applySettings();
    
    1194
    +  }
    
    1100 1195
     }
    
    1101 1196
     
    
    1102 1197
     export const TorSettings = new TorSettingsImpl();