| ... | ... | @@ -301,8 +301,12 @@ class TorSettingsImpl { | 
| 301 | 301 |            return this.#parsePort(val, false) ?? 0;
 | 
| 302 | 302 |          },
 | 
| 303 | 303 |        },
 | 
| 304 |  | -      username: {},
 | 
| 305 |  | -      password: {},
 | 
|  | 304 | +      username: {
 | 
|  | 305 | +        transform: val => val ?? "",
 | 
|  | 306 | +      },
 | 
|  | 307 | +      password: {
 | 
|  | 308 | +        transform: val => val ?? "",
 | 
|  | 309 | +      },
 | 
| 306 | 310 |        uri: {
 | 
| 307 | 311 |          getter: () => {
 | 
| 308 | 312 |            const { type, address, port, username, password } = this.proxy;
 | 
| ... | ... | @@ -910,7 +914,11 @@ class TorSettingsImpl { | 
| 910 | 914 |    }
 | 
| 911 | 915 |  
 | 
| 912 | 916 |    /**
 | 
| 913 |  | -   * Set all of our settings at once from a settings object.
 | 
|  | 917 | +   * Set blocks of settings at once from an object.
 | 
|  | 918 | +   *
 | 
|  | 919 | +   * It is possible to set all settings, or only some sections (e.g., only
 | 
|  | 920 | +   * bridges), but if a key is present, its settings must make sense (e.g., if
 | 
|  | 921 | +   * bridges are enabled, a valid source must be provided).
 | 
| 914 | 922 |     *
 | 
| 915 | 923 |     * @param {object} settings The settings object to set
 | 
| 916 | 924 |     */
 | 
| ... | ... | @@ -924,35 +932,59 @@ class TorSettingsImpl { | 
| 924 | 932 |      // Hold off on lots of notifications until all settings are changed.
 | 
| 925 | 933 |      this.freezeNotifications();
 | 
| 926 | 934 |      try {
 | 
| 927 |  | -      this.bridges.enabled = !!settings.bridges.enabled;
 | 
| 928 |  | -      this.bridges.source = settings.bridges.source;
 | 
| 929 |  | -      switch (settings.bridges.source) {
 | 
| 930 |  | -        case TorBridgeSource.BridgeDB:
 | 
| 931 |  | -        case TorBridgeSource.UserProvided:
 | 
| 932 |  | -          this.bridges.bridge_strings = settings.bridges.bridge_strings;
 | 
| 933 |  | -          break;
 | 
| 934 |  | -        case TorBridgeSource.BuiltIn: {
 | 
| 935 |  | -          this.bridges.builtin_type = settings.bridges.builtin_type;
 | 
| 936 |  | -          if (!this.bridges.bridge_strings.length) {
 | 
| 937 |  | -            // No bridges were found when setting the builtin_type.
 | 
| 938 |  | -            throw new Error(
 | 
| 939 |  | -              `No available builtin bridges of type ${settings.bridges.builtin_type}`
 | 
| 940 |  | -            );
 | 
| 941 |  | -          }
 | 
| 942 |  | -          break;
 | 
|  | 935 | +      if ("bridges" in settings) {
 | 
|  | 936 | +        this.bridges.enabled = !!settings.bridges.enabled;
 | 
|  | 937 | +        // Currently, disabling bridges in the UI does not remove the lines,
 | 
|  | 938 | +        // because we call only the `enabled` setter.
 | 
|  | 939 | +        // So, if the bridge source is undefined but bridges are disabled,
 | 
|  | 940 | +        // do not force Invalid. Instead, keep the current source.
 | 
|  | 941 | +        if (this.bridges.enabled || settings.bridges.source !== undefined) {
 | 
|  | 942 | +          this.bridges.source = settings.bridges.source;
 | 
| 943 | 943 |          }
 | 
| 944 |  | -        case TorBridgeSource.Invalid:
 | 
| 945 |  | -          break;
 | 
| 946 |  | -        default:
 | 
| 947 |  | -          if (settings.bridges.enabled) {
 | 
| 948 |  | -            throw new Error(
 | 
| 949 |  | -              `Bridge source '${settings.source}' is not a valid source`
 | 
| 950 |  | -            );
 | 
|  | 944 | +        switch (settings.bridges.source) {
 | 
|  | 945 | +          case TorBridgeSource.BridgeDB:
 | 
|  | 946 | +          case TorBridgeSource.UserProvided:
 | 
|  | 947 | +            this.bridges.bridge_strings = settings.bridges.bridge_strings;
 | 
|  | 948 | +            break;
 | 
|  | 949 | +          case TorBridgeSource.BuiltIn: {
 | 
|  | 950 | +            this.bridges.builtin_type = settings.bridges.builtin_type;
 | 
|  | 951 | +            if (!this.bridges.bridge_strings.length) {
 | 
|  | 952 | +              // No bridges were found when setting the builtin_type.
 | 
|  | 953 | +              throw new Error(
 | 
|  | 954 | +                `No available builtin bridges of type ${settings.bridges.builtin_type}`
 | 
|  | 955 | +              );
 | 
|  | 956 | +            }
 | 
|  | 957 | +            break;
 | 
| 951 | 958 |            }
 | 
| 952 |  | -          break;
 | 
|  | 959 | +          case TorBridgeSource.Invalid:
 | 
|  | 960 | +            break;
 | 
|  | 961 | +          default:
 | 
|  | 962 | +            if (settings.bridges.enabled) {
 | 
|  | 963 | +              throw new Error(
 | 
|  | 964 | +                `Bridge source '${settings.source}' is not a valid source`
 | 
|  | 965 | +              );
 | 
|  | 966 | +            }
 | 
|  | 967 | +            break;
 | 
|  | 968 | +        }
 | 
| 953 | 969 |        }
 | 
| 954 | 970 |  
 | 
| 955 |  | -      // TODO: proxy and firewall
 | 
|  | 971 | +      if ("proxy" in settings) {
 | 
|  | 972 | +        this.proxy.enabled = !!settings.proxy.enabled;
 | 
|  | 973 | +        if (this.proxy.enabled) {
 | 
|  | 974 | +          this.proxy.type = settings.proxy.type;
 | 
|  | 975 | +          this.proxy.address = settings.proxy.address;
 | 
|  | 976 | +          this.proxy.port = settings.proxy.port;
 | 
|  | 977 | +          this.proxy.username = settings.proxy.username;
 | 
|  | 978 | +          this.proxy.password = settings.proxy.password;
 | 
|  | 979 | +        }
 | 
|  | 980 | +      }
 | 
|  | 981 | +
 | 
|  | 982 | +      if ("firewall" in settings) {
 | 
|  | 983 | +        this.firewall.enabled = !!settings.firewall.enabled;
 | 
|  | 984 | +        if (this.firewall.enabled) {
 | 
|  | 985 | +          this.firewall.allowed_ports = settings.firewall.allowed_ports;
 | 
|  | 986 | +        }
 | 
|  | 987 | +      }
 | 
| 956 | 988 |      } catch (ex) {
 | 
| 957 | 989 |        // Restore the old settings without any new notifications generated from
 | 
| 958 | 990 |        // the above code.
 |