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

Commits:

7 changed files:

Changes:

  • browser/components/torpreferences/content/connectionPane.js
    ... ... @@ -2545,17 +2545,14 @@ const gConnectionPane = (function () {
    2545 2545
               }
    
    2546 2546
               return item;
    
    2547 2547
             };
    
    2548
    +
    
    2549
    +        // TODO: Re-fetch when intl:app-locales-changed is fired, if we keep
    
    2550
    +        // this after tor-browser#42477.
    
    2551
    +        const regionNames = TorConnect.getRegionNames();
    
    2548 2552
             const addLocations = codes => {
    
    2549 2553
               const items = [];
    
    2550 2554
               for (const code of codes) {
    
    2551
    -            items.push(
    
    2552
    -              createItem(
    
    2553
    -                code,
    
    2554
    -                TorConnect.countryNames[code]
    
    2555
    -                  ? TorConnect.countryNames[code]
    
    2556
    -                  : code
    
    2557
    -              )
    
    2558
    -            );
    
    2555
    +            items.push(createItem(code, regionNames[code] || code));
    
    2559 2556
               }
    
    2560 2557
               items.sort((left, right) => left.label.localeCompare(right.label));
    
    2561 2558
               locationEntries.append(...items);
    
    ... ... @@ -2573,7 +2570,7 @@ const gConnectionPane = (function () {
    2573 2570
               locationEntries.append(
    
    2574 2571
                 createItem("", TorStrings.settings.bridgeLocationOther, true)
    
    2575 2572
               );
    
    2576
    -          addLocations(Object.keys(TorConnect.countryNames));
    
    2573
    +          addLocations(Object.keys(regionNames));
    
    2577 2574
             });
    
    2578 2575
             this._showAutoconfiguration = () => {
    
    2579 2576
               locationGroup.hidden =
    

  • toolkit/components/torconnect/TorConnectParent.sys.mjs
    ... ... @@ -53,6 +53,9 @@ export class TorConnectParent extends JSWindowActorParent {
    53 53
                   TorConnect.quickstart
    
    54 54
                 );
    
    55 55
                 break;
    
    56
    +          case TorConnectTopics.RegionNamesChange:
    
    57
    +            self.sendAsyncMessage("torconnect:region-names-change");
    
    58
    +            break;
    
    56 59
             }
    
    57 60
           },
    
    58 61
         };
    
    ... ... @@ -69,6 +72,10 @@ export class TorConnectParent extends JSWindowActorParent {
    69 72
           this.torConnectObserver,
    
    70 73
           TorConnectTopics.QuickstartChange
    
    71 74
         );
    
    75
    +    Services.obs.addObserver(
    
    76
    +      this.torConnectObserver,
    
    77
    +      TorConnectTopics.RegionNamesChange
    
    78
    +    );
    
    72 79
       }
    
    73 80
     
    
    74 81
       didDestroy() {
    
    ... ... @@ -84,6 +91,10 @@ export class TorConnectParent extends JSWindowActorParent {
    84 91
           this.torConnectObserver,
    
    85 92
           TorConnectTopics.QuickstartChange
    
    86 93
         );
    
    94
    +    Services.obs.removeObserver(
    
    95
    +      this.torConnectObserver,
    
    96
    +      TorConnectTopics.RegionNamesChange
    
    97
    +    );
    
    87 98
       }
    
    88 99
     
    
    89 100
       async receiveMessage(message) {
    
    ... ... @@ -134,7 +145,6 @@ export class TorConnectParent extends JSWindowActorParent {
    134 145
             return {
    
    135 146
               TorStrings,
    
    136 147
               Direction: Services.locale.isAppLocaleRTL ? "rtl" : "ltr",
    
    137
    -          CountryNames: TorConnect.countryNames,
    
    138 148
               stage: TorConnect.stage,
    
    139 149
               userHasEverClickedConnect: Services.prefs.getBoolPref(
    
    140 150
                 userHasEverClickedConnectPref,
    
    ... ... @@ -142,8 +152,10 @@ export class TorConnectParent extends JSWindowActorParent {
    142 152
               ),
    
    143 153
               quickstartEnabled: TorConnect.quickstart,
    
    144 154
             };
    
    145
    -      case "torconnect:get-frequent-regions":
    
    146
    -        return TorConnect.getFrequentRegions();
    
    155
    +      case "torconnect:get-regions":
    
    156
    +        return TorConnect.getFrequentRegions().then(frequent => {
    
    157
    +          return { names: TorConnect.getRegionNames(), frequent };
    
    158
    +        });
    
    147 159
         }
    
    148 160
         return undefined;
    
    149 161
       }
    

  • toolkit/components/torconnect/content/aboutTorConnect.html
    ... ... @@ -68,7 +68,11 @@
    68 68
             <div class="button-container">
    
    69 69
               <label id="locationDropdownLabel" for="countries"></label>
    
    70 70
               <form id="locationDropdown" hidden="true">
    
    71
    -            <select id="countries"></select>
    
    71
    +            <select id="regions-select">
    
    72
    +              <option id="first-region-option"></option>
    
    73
    +              <optgroup id="frequent-regions-option-group"></optgroup>
    
    74
    +              <optgroup id="full-regions-option-group"></optgroup>
    
    75
    +            </select>
    
    72 76
               </form>
    
    73 77
               <span id="buttonPadding"></span>
    
    74 78
               <span id="connectButtonContainer">
    

  • toolkit/components/torconnect/content/aboutTorConnect.js
    ... ... @@ -70,7 +70,7 @@ class AboutTorConnect {
    70 70
           tryBridge: "button#tryBridgeButton",
    
    71 71
           locationDropdownLabel: "#locationDropdownLabel",
    
    72 72
           locationDropdown: "#locationDropdown",
    
    73
    -      locationDropdownSelect: "#locationDropdown select",
    
    73
    +      locationDropdownSelect: "#regions-select",
    
    74 74
         },
    
    75 75
       });
    
    76 76
     
    
    ... ... @@ -129,13 +129,38 @@ class AboutTorConnect {
    129 129
         locationDropdownSelect: document.querySelector(
    
    130 130
           this.selectors.buttons.locationDropdownSelect
    
    131 131
         ),
    
    132
    +    firstRegionOption: document.getElementById("first-region-option"),
    
    133
    +    frequentRegionsOptionGroup: document.getElementById(
    
    134
    +      "frequent-regions-option-group"
    
    135
    +    ),
    
    136
    +    fullRegionsOptionGroup: document.getElementById(
    
    137
    +      "full-regions-option-group"
    
    138
    +    ),
    
    132 139
         tryBridgeButton: document.querySelector(this.selectors.buttons.tryBridge),
    
    133 140
       });
    
    134 141
     
    
    135
    -  selectedLocation;
    
    142
    +  /**
    
    143
    +   * The currently shown stage, or `null` if the page in uninitialised.
    
    144
    +   *
    
    145
    +   * @type {?string}
    
    146
    +   */
    
    136 147
       shownStage = null;
    
    137 148
     
    
    138
    -  locations = {};
    
    149
    +  /**
    
    150
    +   * A promise that resolves to a list of region names and frequent regions, or
    
    151
    +   * `null` if this needs to be re-fetched from the TorConnectParent.
    
    152
    +   *
    
    153
    +   * @type {?Promise<object>}
    
    154
    +   */
    
    155
    +  regions = null;
    
    156
    +
    
    157
    +  /**
    
    158
    +   * The option value that *should* be selected when the list of regions is
    
    159
    +   * populated.
    
    160
    +   *
    
    161
    +   * @type {string}
    
    162
    +   */
    
    163
    +  selectedRegion = "";
    
    139 164
     
    
    140 165
       /**
    
    141 166
        * Whether the user requested a cancellation of the bootstrap from *this*
    
    ... ... @@ -201,89 +226,6 @@ class AboutTorConnect {
    201 226
         this.hide(this.elements.tryBridgeButton);
    
    202 227
       }
    
    203 228
     
    
    204
    -  populateLocations() {
    
    205
    -    const selectCountryRegion = document.createElement("option");
    
    206
    -    selectCountryRegion.textContent = TorStrings.torConnect.selectCountryRegion;
    
    207
    -    selectCountryRegion.value = "";
    
    208
    -
    
    209
    -    // get all codes and names from TorStrings
    
    210
    -    const locationNodes = [];
    
    211
    -    for (const [code, name] of Object.entries(this.locations)) {
    
    212
    -      let option = document.createElement("option");
    
    213
    -      option.value = code;
    
    214
    -      option.textContent = name;
    
    215
    -      locationNodes.push(option);
    
    216
    -    }
    
    217
    -    // locale sort by name
    
    218
    -    locationNodes.sort((left, right) =>
    
    219
    -      left.textContent.localeCompare(right.textContent)
    
    220
    -    );
    
    221
    -    this.elements.locationDropdownSelect.append(
    
    222
    -      selectCountryRegion,
    
    223
    -      ...locationNodes
    
    224
    -    );
    
    225
    -  }
    
    226
    -
    
    227
    -  populateFrequentLocations(locations) {
    
    228
    -    this.removeFrequentLocations();
    
    229
    -    if (!locations || !locations.length) {
    
    230
    -      return;
    
    231
    -    }
    
    232
    -
    
    233
    -    const locationNodes = [];
    
    234
    -    for (const code of locations) {
    
    235
    -      const option = document.createElement("option");
    
    236
    -      option.value = code;
    
    237
    -      option.className = "frequent-location";
    
    238
    -      // codes (partially) come from rdsys service, so make sure we have a
    
    239
    -      // string defined for it
    
    240
    -      let name = this.locations[code];
    
    241
    -      if (!name) {
    
    242
    -        name = code;
    
    243
    -      }
    
    244
    -      option.textContent = name;
    
    245
    -      locationNodes.push(option);
    
    246
    -    }
    
    247
    -    // locale sort by name
    
    248
    -    locationNodes.sort((left, right) =>
    
    249
    -      left.textContent.localeCompare(right.textContent)
    
    250
    -    );
    
    251
    -
    
    252
    -    const frequentGroup = document.createElement("optgroup");
    
    253
    -    frequentGroup.setAttribute(
    
    254
    -      "label",
    
    255
    -      TorStrings.torConnect.frequentLocations
    
    256
    -    );
    
    257
    -    frequentGroup.className = "frequent-location";
    
    258
    -    const locationGroup = document.createElement("optgroup");
    
    259
    -    locationGroup.setAttribute("label", TorStrings.torConnect.otherLocations);
    
    260
    -    locationGroup.className = "frequent-location";
    
    261
    -    // options[0] is either "Select Country or Region" or "Automatic"
    
    262
    -    this.elements.locationDropdownSelect.options[0].after(
    
    263
    -      frequentGroup,
    
    264
    -      ...locationNodes,
    
    265
    -      locationGroup
    
    266
    -    );
    
    267
    -  }
    
    268
    -
    
    269
    -  removeFrequentLocations() {
    
    270
    -    const select = this.elements.locationDropdownSelect;
    
    271
    -    for (const option of select.querySelectorAll(".frequent-location")) {
    
    272
    -      option.remove();
    
    273
    -    }
    
    274
    -  }
    
    275
    -
    
    276
    -  validateLocation() {
    
    277
    -    const selectedIndex = this.elements.locationDropdownSelect.selectedIndex;
    
    278
    -    const selectedOption =
    
    279
    -      this.elements.locationDropdownSelect.options[selectedIndex];
    
    280
    -    if (!selectedOption.value) {
    
    281
    -      this.elements.tryBridgeButton.setAttribute("disabled", "disabled");
    
    282
    -    } else {
    
    283
    -      this.elements.tryBridgeButton.removeAttribute("disabled");
    
    284
    -    }
    
    285
    -  }
    
    286
    -
    
    287 229
       setTitle(title, className) {
    
    288 230
         this.elements.heading.textContent = title;
    
    289 231
         this.elements.title.className = "title";
    
    ... ... @@ -407,7 +349,9 @@ class AboutTorConnect {
    407 349
     
    
    408 350
         const prevStage = this.shownStage;
    
    409 351
         this.shownStage = stage.name;
    
    410
    -    this.selectedLocation = stage.defaultRegion;
    
    352
    +    // Make a request to change the selected region in the next call to
    
    353
    +    // selectRegionOption.
    
    354
    +    this.selectedRegion = stage.defaultRegion;
    
    411 355
     
    
    412 356
         // By default we want to reset the focus to the top of the page when
    
    413 357
         // changing the displayed page since we want a user to read the new page
    
    ... ... @@ -708,24 +652,105 @@ class AboutTorConnect {
    708 652
         }
    
    709 653
       }
    
    710 654
     
    
    655
    +  /**
    
    656
    +   * Try and select the region specified in `selectedRegion`.
    
    657
    +   */
    
    658
    +  selectRegionOption() {
    
    659
    +    // NOTE: If the region appears in both the frequent list and the full list,
    
    660
    +    // then this will select the region option in
    
    661
    +    // frequentRegionsOptionGroup, even if the user had prior selected the
    
    662
    +    // option from fullRegionsOptionGroup. But the overall value should be the
    
    663
    +    // same.
    
    664
    +    this.elements.locationDropdownSelect.value = this.selectedRegion;
    
    665
    +    if (this.elements.locationDropdownSelect.selectedIndex === -1) {
    
    666
    +      // Select the first, as a fallback. E.g. in RegionNotFound the
    
    667
    +      // selectedRegion may still be "automatic", but this is no longer
    
    668
    +      // available.
    
    669
    +      this.elements.locationDropdownSelect.selectedIndex = 0;
    
    670
    +    }
    
    671
    +    this.validateRegion();
    
    672
    +  }
    
    673
    +
    
    674
    +  /**
    
    675
    +   * Ensure that the current selected region is valid for the shown stage.
    
    676
    +   */
    
    677
    +  validateRegion() {
    
    678
    +    this.elements.tryBridgeButton.toggleAttribute(
    
    679
    +      "disabled",
    
    680
    +      !this.elements.locationDropdownSelect.value
    
    681
    +    );
    
    682
    +  }
    
    683
    +
    
    684
    +  /**
    
    685
    +   * Populate the full list of regions, if necessary.
    
    686
    +   */
    
    687
    +  async populateDelayedRegionOptions() {
    
    688
    +    if (this.regions) {
    
    689
    +      // Already populated, or about to populate.
    
    690
    +      return;
    
    691
    +    }
    
    692
    +
    
    693
    +    this.regions = RPMSendQuery("torconnect:get-regions");
    
    694
    +    const regions = this.regions;
    
    695
    +    const { names, frequent } = await regions;
    
    696
    +
    
    697
    +    if (regions !== this.regions) {
    
    698
    +      // Replaced by a new call.
    
    699
    +      return;
    
    700
    +    }
    
    701
    +
    
    702
    +    this.setRegionOptions(
    
    703
    +      this.elements.frequentRegionsOptionGroup,
    
    704
    +      frequent.map(code => [code, names[code]])
    
    705
    +    );
    
    706
    +
    
    707
    +    this.setRegionOptions(
    
    708
    +      this.elements.fullRegionsOptionGroup,
    
    709
    +      Object.entries(names)
    
    710
    +    );
    
    711
    +
    
    712
    +    // Now that the list has been re-populated we want to re-select the
    
    713
    +    // requested region.
    
    714
    +    this.selectRegionOption();
    
    715
    +  }
    
    716
    +
    
    717
    +  /**
    
    718
    +   * Set the shown region options.
    
    719
    +   *
    
    720
    +   * @param {HTMLOptGroupElement} group - The group to set the children of.
    
    721
    +   * @param {[string, string|undefined][]} regions - The list of region
    
    722
    +   *   key-value entries to fill the group with. The key is the region code and
    
    723
    +   *   the value is the region's localised name.
    
    724
    +   */
    
    725
    +  setRegionOptions(group, regions) {
    
    726
    +    const regionNodes = regions
    
    727
    +      .sort(([_code1, name1], [_code2, name2]) => name1.localeCompare(name2))
    
    728
    +      .map(([code, name]) => {
    
    729
    +        const option = document.createElement("option");
    
    730
    +        option.value = code;
    
    731
    +        // If the name is unexpectedly empty or undefined we use the code
    
    732
    +        // instead.
    
    733
    +        option.textContent = name || code;
    
    734
    +        return option;
    
    735
    +      });
    
    736
    +    group.replaceChildren(...regionNodes);
    
    737
    +  }
    
    738
    +
    
    711 739
       showLocationForm(isChoose, buttonLabel) {
    
    712 740
         this.hideButtons();
    
    713
    -    RPMSendQuery("torconnect:get-frequent-regions").then(codes => {
    
    714
    -      if (codes && codes.length) {
    
    715
    -        this.populateFrequentLocations(codes);
    
    716
    -        this.setLocation();
    
    717
    -      }
    
    718
    -    });
    
    719
    -    let firstOpt = this.elements.locationDropdownSelect.options[0];
    
    720
    -    if (isChoose) {
    
    721
    -      firstOpt.value = "automatic";
    
    722
    -      firstOpt.textContent = TorStrings.torConnect.automatic;
    
    723
    -    } else {
    
    724
    -      firstOpt.value = "";
    
    725
    -      firstOpt.textContent = TorStrings.torConnect.selectCountryRegion;
    
    726
    -    }
    
    727
    -    this.setLocation();
    
    728
    -    this.validateLocation();
    
    741
    +
    
    742
    +    this.elements.firstRegionOption.textContent = isChoose
    
    743
    +      ? TorStrings.torConnect.automatic
    
    744
    +      : TorStrings.torConnect.selectCountryRegion;
    
    745
    +    this.elements.firstRegionOption.value = isChoose ? "automatic" : "";
    
    746
    +
    
    747
    +    // Try and select the region now, prior to waiting for
    
    748
    +    // populateDelayedRegionOptions.
    
    749
    +    this.selectRegionOption();
    
    750
    +
    
    751
    +    // Async fill the rest of the region options, if needed.
    
    752
    +    this.populateDelayedRegionOptions();
    
    753
    +
    
    729 754
         this.show(this.elements.locationDropdownLabel);
    
    730 755
         this.show(this.elements.locationDropdown);
    
    731 756
         this.elements.locationDropdownLabel.classList.toggle("error", !isChoose);
    
    ... ... @@ -735,29 +760,6 @@ class AboutTorConnect {
    735 760
         }
    
    736 761
       }
    
    737 762
     
    
    738
    -  getLocation() {
    
    739
    -    const selectedIndex = this.elements.locationDropdownSelect.selectedIndex;
    
    740
    -    return this.elements.locationDropdownSelect.options[selectedIndex].value;
    
    741
    -  }
    
    742
    -
    
    743
    -  setLocation() {
    
    744
    -    const code = this.selectedLocation;
    
    745
    -    if (this.getLocation() === code) {
    
    746
    -      return;
    
    747
    -    }
    
    748
    -    const options = this.elements.locationDropdownSelect.options;
    
    749
    -    // We need to do this way, because we have repeated values that break
    
    750
    -    // the .value way to select (which would however require the label,
    
    751
    -    // rather than the code)...
    
    752
    -    for (let i = 0; i < options.length; i++) {
    
    753
    -      if (options[i].value === code) {
    
    754
    -        this.elements.locationDropdownSelect.selectedIndex = i;
    
    755
    -        break;
    
    756
    -      }
    
    757
    -    }
    
    758
    -    this.validateLocation();
    
    759
    -  }
    
    760
    -
    
    761 763
       initElements(direction) {
    
    762 764
         const isAndroid = navigator.userAgent.includes("Android");
    
    763 765
         document.body.classList.toggle("android", isAndroid);
    
    ... ... @@ -826,17 +828,32 @@ class AboutTorConnect {
    826 828
           this.beginBootstrapping(this.shownStage === "Start");
    
    827 829
         });
    
    828 830
     
    
    829
    -    this.populateLocations();
    
    830 831
         this.elements.locationDropdownSelect.addEventListener("change", () => {
    
    831
    -      this.validateLocation();
    
    832
    +      // Overwrite the stage requested selectedRegion.
    
    833
    +      // NOTE: This should not fire in response to a programmatic change in
    
    834
    +      // value.
    
    835
    +      // E.g. if the user selects a region, then changes locale, we want the
    
    836
    +      // same region to be re-selected after the option list is rebuilt.
    
    837
    +      this.selectedRegion = this.elements.locationDropdownSelect.value;
    
    838
    +
    
    839
    +      this.validateRegion();
    
    832 840
         });
    
    833 841
     
    
    834 842
         this.elements.locationDropdownLabel.textContent =
    
    835 843
           TorStrings.torConnect.unblockInternetIn;
    
    836 844
     
    
    845
    +    this.elements.frequentRegionsOptionGroup.setAttribute(
    
    846
    +      "label",
    
    847
    +      TorStrings.torConnect.frequentLocations
    
    848
    +    );
    
    849
    +    this.elements.fullRegionsOptionGroup.setAttribute(
    
    850
    +      "label",
    
    851
    +      TorStrings.torConnect.otherLocations
    
    852
    +    );
    
    853
    +
    
    837 854
         this.elements.tryBridgeButton.textContent = TorStrings.torConnect.tryBridge;
    
    838 855
         this.elements.tryBridgeButton.addEventListener("click", () => {
    
    839
    -      const value = this.getLocation();
    
    856
    +      const value = this.elements.locationDropdownSelect.value;
    
    840 857
           if (value) {
    
    841 858
             this.beginAutoBootstrapping(value);
    
    842 859
           }
    
    ... ... @@ -879,6 +896,15 @@ class AboutTorConnect {
    879 896
         RPMAddMessageListener("torconnect:quickstart-change", ({ data }) => {
    
    880 897
           this.updateQuickstart(data);
    
    881 898
         });
    
    899
    +    RPMAddMessageListener("torconnect:region-names-change", () => {
    
    900
    +      // Reset the regions list.
    
    901
    +      this.regions = null;
    
    902
    +      if (!this.elements.locationDropdown.hidden) {
    
    903
    +        // Re-populate immediately.
    
    904
    +        this.populateDelayedRegionOptions();
    
    905
    +      }
    
    906
    +      // Else, wait until we show the region select to re-populate.
    
    907
    +    });
    
    882 908
       }
    
    883 909
     
    
    884 910
       initKeyboardShortcuts() {
    
    ... ... @@ -897,7 +923,6 @@ class AboutTorConnect {
    897 923
     
    
    898 924
         // various constants
    
    899 925
         TorStrings = Object.freeze(args.TorStrings);
    
    900
    -    this.locations = args.CountryNames;
    
    901 926
     
    
    902 927
         this.initElements(args.Direction);
    
    903 928
         this.initObservers();
    

  • toolkit/modules/RemotePageAccessManager.sys.mjs
    ... ... @@ -242,6 +242,7 @@ export let RemotePageAccessManager = {
    242 242
             "torconnect:stage-change",
    
    243 243
             "torconnect:bootstrap-progress",
    
    244 244
             "torconnect:quickstart-change",
    
    245
    +        "torconnect:region-names-change",
    
    245 246
           ],
    
    246 247
           RPMSendAsyncMessage: [
    
    247 248
             "torconnect:open-tor-preferences",
    
    ... ... @@ -253,10 +254,7 @@ export let RemotePageAccessManager = {
    253 254
             "torconnect:start-again",
    
    254 255
             "torconnect:choose-region",
    
    255 256
           ],
    
    256
    -      RPMSendQuery: [
    
    257
    -        "torconnect:get-init-args",
    
    258
    -        "torconnect:get-frequent-regions",
    
    259
    -      ],
    
    257
    +      RPMSendQuery: ["torconnect:get-init-args", "torconnect:get-regions"],
    
    260 258
         },
    
    261 259
         "about:welcome": {
    
    262 260
           RPMSendAsyncMessage: ["ActivityStream:ContentToMain"],
    

  • toolkit/modules/TorAndroidIntegration.sys.mjs
    ... ... @@ -115,6 +115,10 @@ class TorAndroidIntegrationImpl {
    115 115
               stage: subj.wrappedJSObject ?? "",
    
    116 116
             });
    
    117 117
             break;
    
    118
    +      case lazy.TorConnectTopics.RegionNamesChange:
    
    119
    +        // TODO: Respond to change in region names if we are showing a
    
    120
    +        // TorConnectStage that uses them.
    
    121
    +        break;
    
    118 122
           case lazy.TorConnectTopics.BootstrapProgress:
    
    119 123
             lazy.EventDispatcher.instance.sendRequest({
    
    120 124
               type: EmittedEvents.bootstrapProgress,
    
    ... ... @@ -202,7 +206,7 @@ class TorAndroidIntegrationImpl {
    202 206
               lazy.TorConnect.quickstart = data.enabled;
    
    203 207
               break;
    
    204 208
             case ListenedEvents.countryNamesGet:
    
    205
    -          callback?.onSuccess(lazy.TorConnect.countryNames);
    
    209
    +          callback?.onSuccess(lazy.TorConnect.getRegionNames());
    
    206 210
               return;
    
    207 211
           }
    
    208 212
           callback?.onSuccess();
    

  • toolkit/modules/TorConnect.sys.mjs
    ... ... @@ -89,6 +89,7 @@ export const TorConnectTopics = Object.freeze({
    89 89
       StateChange: "torconnect:state-change",
    
    90 90
       QuickstartChange: "torconnect:quickstart-change",
    
    91 91
       InternetStatusChange: "torconnect:internet-status-change",
    
    92
    +  RegionNamesChange: "torconnect:region-names-change",
    
    92 93
       BootstrapProgress: "torconnect:bootstrap-progress",
    
    93 94
       BootstrapComplete: "torconnect:bootstrap-complete",
    
    94 95
       // TODO: Remove torconnect:error when pages have switched to stage.
    
    ... ... @@ -842,17 +843,13 @@ export const TorConnect = {
    842 843
        */
    
    843 844
       _moatRegionsPromise: null,
    
    844 845
     
    
    845
    -  _countryNames: Object.freeze(
    
    846
    -    (() => {
    
    847
    -      const codes = Services.intl.getAvailableLocaleDisplayNames("region");
    
    848
    -      const names = Services.intl.getRegionDisplayNames(undefined, codes);
    
    849
    -      let codesNames = {};
    
    850
    -      for (let i = 0; i < codes.length; i++) {
    
    851
    -        codesNames[codes[i]] = names[i];
    
    852
    -      }
    
    853
    -      return codesNames;
    
    854
    -    })()
    
    855
    -  ),
    
    846
    +  /**
    
    847
    +   * The map of all regions and their localized names. Or `null` when
    
    848
    +   * uninitialized.
    
    849
    +   *
    
    850
    +   * @type {?object}
    
    851
    +   */
    
    852
    +  _regionNames: null,
    
    856 853
     
    
    857 854
       /**
    
    858 855
        * The status of the most recent bootstrap attempt.
    
    ... ... @@ -937,6 +934,7 @@ export const TorConnect = {
    937 934
         observeTopic(lazy.TorProviderTopics.HasWarnOrErr);
    
    938 935
         observeTopic(lazy.TorSettingsTopics.SettingsChanged);
    
    939 936
         observeTopic(NETWORK_LINK_TOPIC);
    
    937
    +    observeTopic("intl:app-locales-changed");
    
    940 938
     
    
    941 939
         this._updateInternetStatus();
    
    942 940
     
    
    ... ... @@ -1003,6 +1001,12 @@ export const TorConnect = {
    1003 1001
           case NETWORK_LINK_TOPIC:
    
    1004 1002
             this._updateInternetStatus();
    
    1005 1003
             break;
    
    1004
    +      case "intl:app-locales-changed":
    
    1005
    +        // Unset the regionNames to use the new app locale when requested.
    
    1006
    +        this._regionNames = null;
    
    1007
    +        // Let consumers know that the list of names has changed.
    
    1008
    +        Services.obs.notifyObservers(null, TorConnectTopics.RegionNamesChange);
    
    1009
    +        break;
    
    1006 1010
         }
    
    1007 1011
       },
    
    1008 1012
     
    
    ... ... @@ -1148,8 +1152,22 @@ export const TorConnect = {
    1148 1152
         return null;
    
    1149 1153
       },
    
    1150 1154
     
    
    1151
    -  get countryNames() {
    
    1152
    -    return this._countryNames;
    
    1155
    +  /**
    
    1156
    +   * Get a map of all region codes and their localized names.
    
    1157
    +   *
    
    1158
    +   * @returns {object} - The region names in the current locale.
    
    1159
    +   */
    
    1160
    +  getRegionNames() {
    
    1161
    +    if (!this._regionNames) {
    
    1162
    +      this._regionNames = {};
    
    1163
    +      const codes = Services.intl.getAvailableLocaleDisplayNames("region");
    
    1164
    +      // Get the localised names, for the current app locale.
    
    1165
    +      const names = Services.intl.getRegionDisplayNames(undefined, codes);
    
    1166
    +      for (let i = 0; i < codes.length; i++) {
    
    1167
    +        this._regionNames[codes[i]] = names[i];
    
    1168
    +      }
    
    1169
    +    }
    
    1170
    +    return structuredClone(this._regionNames);
    
    1153 1171
       },
    
    1154 1172
     
    
    1155 1173
       /**