Pier Angelo Vendrame pushed to branch tor-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
-
e54a67ba
by Henry Wilkes at 2025-03-27T09:59:37+00:00
-
c71487dc
by Henry Wilkes at 2025-03-27T09:59:37+00:00
-
31ac7a09
by Henry Wilkes at 2025-03-27T09:59:37+00:00
-
d24a4c8c
by Henry Wilkes at 2025-03-27T09:59:37+00:00
7 changed files:
- browser/components/torpreferences/content/connectionPane.js
- toolkit/components/torconnect/TorConnectParent.sys.mjs
- toolkit/components/torconnect/content/aboutTorConnect.html
- toolkit/components/torconnect/content/aboutTorConnect.js
- toolkit/modules/RemotePageAccessManager.sys.mjs
- toolkit/modules/TorAndroidIntegration.sys.mjs
- toolkit/modules/TorConnect.sys.mjs
Changes:
... | ... | @@ -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 =
|
... | ... | @@ -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 | }
|
... | ... | @@ -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">
|
... | ... | @@ -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();
|
... | ... | @@ -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"],
|
... | ... | @@ -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();
|
... | ... | @@ -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 | /**
|