Pier Angelo Vendrame pushed to branch tor-browser-102.12.0esr-12.5-1 at The Tor Project / Applications / Tor Browser
Commits:
-
db0a953f
by Henry Wilkes at 2023-06-07T14:09:59+01:00
-
e86c7eaf
by Henry Wilkes at 2023-06-07T14:09:59+01:00
7 changed files:
- browser/components/torpreferences/content/builtinBridgeDialog.jsm
- browser/components/torpreferences/content/builtinBridgeDialog.xhtml
- browser/components/torpreferences/content/connectionPane.js
- browser/components/torpreferences/content/connectionPane.xhtml
- browser/components/torpreferences/content/torPreferences.css
- browser/modules/TorStrings.jsm
- toolkit/torbutton/chrome/locale/en-US/settings.properties
Changes:
... | ... | @@ -17,82 +17,81 @@ const { TorConnect, TorConnectTopics } = ChromeUtils.import( |
17 | 17 | );
|
18 | 18 | |
19 | 19 | class BuiltinBridgeDialog {
|
20 | + /**
|
|
21 | + * Create a new instance.
|
|
22 | + *
|
|
23 | + * @param {Function} onSubmit - A callback for when the user accepts the
|
|
24 | + * dialog selection.
|
|
25 | + */
|
|
20 | 26 | constructor(onSubmit) {
|
21 | 27 | this._onSubmit_ = onSubmit;
|
22 | - this._dialog = null;
|
|
23 | 28 | this._acceptButton = null;
|
24 | 29 | }
|
25 | 30 | |
26 | - static get selectors() {
|
|
27 | - return {
|
|
28 | - description: "#torPreferences-builtinBridge-description",
|
|
29 | - radiogroup: "#torPreferences-builtinBridge-typeSelection",
|
|
30 | - obfsRadio: "#torPreferences-builtinBridges-radioObfs",
|
|
31 | - obfsDescr: "#torPreferences-builtinBridges-descrObfs",
|
|
32 | - snowflakeRadio: "#torPreferences-builtinBridges-radioSnowflake",
|
|
33 | - snowflakeDescr: "#torPreferences-builtinBridges-descrSnowflake",
|
|
34 | - meekAzureRadio: "#torPreferences-builtinBridges-radioMeekAzure",
|
|
35 | - meekAzureDescr: "#torPreferences-builtinBridges-descrMeekAzure",
|
|
36 | - };
|
|
37 | - }
|
|
38 | - |
|
39 | - _populateXUL(window, aDialog) {
|
|
40 | - const selectors = BuiltinBridgeDialog.selectors;
|
|
41 | - |
|
42 | - this._dialog = aDialog;
|
|
43 | - const dialogWin = this._dialog.parentElement;
|
|
31 | + _populateXUL(window, dialog) {
|
|
32 | + const dialogWin = dialog.parentElement;
|
|
44 | 33 | dialogWin.setAttribute("title", TorStrings.settings.builtinBridgeHeader);
|
45 | 34 | |
46 | - this._dialog.querySelector(selectors.description).textContent =
|
|
47 | - TorStrings.settings.builtinBridgeDescription2;
|
|
35 | + dialog.querySelector(
|
|
36 | + "#torPreferences-builtinBridge-description"
|
|
37 | + ).textContent = TorStrings.settings.builtinBridgeDescription2;
|
|
48 | 38 | |
49 | - this._acceptButton = this._dialog.getButton("accept");
|
|
39 | + this._acceptButton = dialog.getButton("accept");
|
|
50 | 40 | this.onTorStateChange();
|
51 | 41 | |
52 | - let radioGroup = this._dialog.querySelector(selectors.radiogroup);
|
|
42 | + const radioGroup = dialog.querySelector(
|
|
43 | + "#torPreferences-builtinBridge-typeSelection"
|
|
44 | + );
|
|
53 | 45 | |
54 | - let types = {
|
|
46 | + const typeStrings = {
|
|
55 | 47 | obfs4: {
|
56 | - elemRadio: this._dialog.querySelector(selectors.obfsRadio),
|
|
57 | - elemDescr: this._dialog.querySelector(selectors.obfsDescr),
|
|
58 | 48 | label: TorStrings.settings.builtinBridgeObfs4Title,
|
59 | 49 | descr: TorStrings.settings.builtinBridgeObfs4Description2,
|
60 | 50 | },
|
61 | 51 | snowflake: {
|
62 | - elemRadio: this._dialog.querySelector(selectors.snowflakeRadio),
|
|
63 | - elemDescr: this._dialog.querySelector(selectors.snowflakeDescr),
|
|
64 | 52 | label: TorStrings.settings.builtinBridgeSnowflake,
|
65 | 53 | descr: TorStrings.settings.builtinBridgeSnowflakeDescription2,
|
66 | 54 | },
|
67 | 55 | "meek-azure": {
|
68 | - elemRadio: this._dialog.querySelector(selectors.meekAzureRadio),
|
|
69 | - elemDescr: this._dialog.querySelector(selectors.meekAzureDescr),
|
|
70 | 56 | label: TorStrings.settings.builtinBridgeMeekAzure,
|
71 | 57 | descr: TorStrings.settings.builtinBridgeMeekAzureDescription2,
|
72 | 58 | },
|
73 | 59 | };
|
74 | 60 | |
75 | - TorBuiltinBridgeTypes.forEach(type => {
|
|
76 | - types[type].elemRadio.setAttribute("label", types[type].label);
|
|
77 | - types[type].elemRadio.setAttribute("hidden", "false");
|
|
78 | - types[type].elemDescr.textContent = types[type].descr;
|
|
79 | - types[type].elemDescr.removeAttribute("hidden");
|
|
80 | - });
|
|
81 | - |
|
82 | - if (
|
|
61 | + const currentBuiltinType =
|
|
83 | 62 | TorSettings.bridges.enabled &&
|
84 | 63 | TorSettings.bridges.source == TorBridgeSource.BuiltIn
|
85 | - ) {
|
|
86 | - radioGroup.selectedItem =
|
|
87 | - types[TorSettings.bridges.builtin_type]?.elemRadio;
|
|
64 | + ? TorSettings.bridges.builtin_type
|
|
65 | + : null;
|
|
66 | + if (currentBuiltinType) {
|
|
67 | + radioGroup.value = currentBuiltinType;
|
|
88 | 68 | } else {
|
89 | 69 | radioGroup.selectedItem = null;
|
90 | 70 | }
|
91 | 71 | |
92 | - this._dialog.addEventListener("dialogaccept", () => {
|
|
72 | + for (const optionEl of radioGroup.querySelectorAll(
|
|
73 | + ".builtin-bridges-option"
|
|
74 | + )) {
|
|
75 | + const radio = optionEl.querySelector("radio");
|
|
76 | + const type = radio.value;
|
|
77 | + optionEl.hidden = !TorBuiltinBridgeTypes.includes(type);
|
|
78 | + radio.label = typeStrings[type].label;
|
|
79 | + optionEl.querySelector(
|
|
80 | + ".builtin-bridges-option-description"
|
|
81 | + ).textContent = typeStrings[type].descr;
|
|
82 | + optionEl.querySelector(
|
|
83 | + ".torPreferences-current-bridge-label"
|
|
84 | + ).textContent = TorStrings.settings.currentBridge;
|
|
85 | + optionEl.classList.toggle(
|
|
86 | + "current-builtin-bridge-type",
|
|
87 | + type === currentBuiltinType
|
|
88 | + );
|
|
89 | + }
|
|
90 | + |
|
91 | + dialog.addEventListener("dialogaccept", () => {
|
|
93 | 92 | this.onSubmit(radioGroup.value, TorConnect.canBeginBootstrap);
|
94 | 93 | });
|
95 | - this._dialog.addEventListener("dialoghelp", e => {
|
|
94 | + dialog.addEventListener("dialoghelp", e => {
|
|
96 | 95 | window.top.openTrustedLinkIn(
|
97 | 96 | TorStrings.settings.learnMoreCircumventionURL,
|
98 | 97 | "tab"
|
... | ... | @@ -100,8 +99,8 @@ class BuiltinBridgeDialog { |
100 | 99 | });
|
101 | 100 | |
102 | 101 | // Hack: see the CSS
|
103 | - this._dialog.style.minWidth = "0";
|
|
104 | - this._dialog.style.minHeight = "0";
|
|
102 | + dialog.style.minWidth = "0";
|
|
103 | + dialog.style.minHeight = "0";
|
|
105 | 104 | |
106 | 105 | Services.obs.addObserver(this, TorConnectTopics.StateChange);
|
107 | 106 | }
|
... | ... | @@ -8,16 +8,57 @@ |
8 | 8 | xmlns:html="http://www.w3.org/1999/xhtml">
|
9 | 9 | <dialog id="torPreferences-builtinBridge-dialog"
|
10 | 10 | buttons="help,accept,cancel">
|
11 | - <description>
|
|
12 | - <html:div id="torPreferences-builtinBridge-description">​<br/>​</html:div>
|
|
11 | + <description id="torPreferences-builtinBridge-description">
|
|
13 | 12 | </description>
|
14 | 13 | <radiogroup id="torPreferences-builtinBridge-typeSelection">
|
15 | - <radio id="torPreferences-builtinBridges-radioObfs" value="obfs4" hidden="true"/>
|
|
16 | - <html:div id="torPreferences-builtinBridges-descrObfs" class="indent" hidden="true">​</html:div>
|
|
17 | - <radio id="torPreferences-builtinBridges-radioSnowflake" value="snowflake" hidden="true"/>
|
|
18 | - <html:div id="torPreferences-builtinBridges-descrSnowflake" class="indent" hidden="true">​</html:div>
|
|
19 | - <radio id="torPreferences-builtinBridges-radioMeekAzure" value="meek-azure" hidden="true"/>
|
|
20 | - <html:div id="torPreferences-builtinBridges-descrMeekAzure" class="indent" hidden="true">​</html:div>
|
|
14 | + <vbox class="builtin-bridges-option">
|
|
15 | + <hbox>
|
|
16 | + <!-- The radio option is described by both the "Current bridge" label
|
|
17 | + - and the full description. If the "Connected" label is hidden, then
|
|
18 | + - only the latter description should contribute. -->
|
|
19 | + <radio aria-describedby="obfs-bridges-current obfs-bridges-description"
|
|
20 | + value="obfs4"/>
|
|
21 | + <html:span class="torPreferences-current-bridge-badge">
|
|
22 | + <image class="torPreferences-current-bridge-icon"/>
|
|
23 | + <html:span id="obfs-bridges-current"
|
|
24 | + class="torPreferences-current-bridge-label">
|
|
25 | + </html:span>
|
|
26 | + </html:span>
|
|
27 | + </hbox>
|
|
28 | + <html:div id="obfs-bridges-description"
|
|
29 | + class="indent builtin-bridges-option-description">
|
|
30 | + </html:div>
|
|
31 | + </vbox>
|
|
32 | + <vbox class="builtin-bridges-option">
|
|
33 | + <hbox>
|
|
34 | + <radio aria-describedby="snowflake-bridges-current snowflake-bridges-description"
|
|
35 | + value="snowflake"/>
|
|
36 | + <html:span class="torPreferences-current-bridge-badge">
|
|
37 | + <image class="torPreferences-current-bridge-icon"/>
|
|
38 | + <html:span id="snowflake-bridges-current"
|
|
39 | + class="torPreferences-current-bridge-label">
|
|
40 | + </html:span>
|
|
41 | + </html:span>
|
|
42 | + </hbox>
|
|
43 | + <html:div id="snowflake-bridges-description"
|
|
44 | + class="indent builtin-bridges-option-description">
|
|
45 | + </html:div>
|
|
46 | + </vbox>
|
|
47 | + <vbox class="builtin-bridges-option">
|
|
48 | + <hbox>
|
|
49 | + <radio aria-describedby="meek-bridges-current meek-bridges-description"
|
|
50 | + value="meek-azure"/>
|
|
51 | + <html:span class="torPreferences-current-bridge-badge">
|
|
52 | + <image class="torPreferences-current-bridge-icon"/>
|
|
53 | + <html:span id="meek-bridges-current"
|
|
54 | + class="torPreferences-current-bridge-label">
|
|
55 | + </html:span>
|
|
56 | + </html:span>
|
|
57 | + </hbox>
|
|
58 | + <html:div id="meek-bridges-description"
|
|
59 | + class="indent builtin-bridges-option-description">
|
|
60 | + </html:div>
|
|
61 | + </vbox>
|
|
21 | 62 | </radiogroup>
|
22 | 63 | <script type="application/_javascript_"><![CDATA[
|
23 | 64 | "use strict";
|
... | ... | @@ -124,7 +124,7 @@ const gConnectionPane = (function() { |
124 | 124 | cardId: ".torPreferences-bridgeCard-id",
|
125 | 125 | cardHeadingManualLink: ".torPreferences-bridgeCard-manualLink",
|
126 | 126 | cardHeadingAddr: ".torPreferences-bridgeCard-headingAddr",
|
127 | - cardConnectedLabel: ".torPreferences-bridgeCard-connectedLabel",
|
|
127 | + cardConnectedLabel: ".torPreferences-current-bridge-label",
|
|
128 | 128 | cardOptions: ".torPreferences-bridgeCard-options",
|
129 | 129 | cardMenu: "#torPreferences-bridgeCard-menu",
|
130 | 130 | cardQrGrid: ".torPreferences-bridgeCard-grid",
|
... | ... | @@ -168,7 +168,7 @@ const gConnectionPane = (function() { |
168 | 168 | |
169 | 169 | _controller: null,
|
170 | 170 | |
171 | - _currentBridge: "",
|
|
171 | + _currentBridgeId: null,
|
|
172 | 172 | |
173 | 173 | // populate xul with strings and cache the relevant elements
|
174 | 174 | _populateXUL() {
|
... | ... | @@ -471,7 +471,7 @@ const gConnectionPane = (function() { |
471 | 471 | }
|
472 | 472 | bridgeTemplate.querySelector(
|
473 | 473 | selectors.bridges.cardConnectedLabel
|
474 | - ).textContent = TorStrings.settings.statusTorConnected;
|
|
474 | + ).textContent = TorStrings.settings.connectedBridge;
|
|
475 | 475 | bridgeTemplate
|
476 | 476 | .querySelector(selectors.bridges.cardCopy)
|
477 | 477 | .setAttribute("label", TorStrings.settings.bridgeCopy);
|
... | ... | @@ -607,7 +607,7 @@ const gConnectionPane = (function() { |
607 | 607 | restoreTimeout = null;
|
608 | 608 | }, RESTORE_TIME);
|
609 | 609 | });
|
610 | - if (details && details.id === this._currentBridge) {
|
|
610 | + if (details?.id && details.id === this._currentBridgeId) {
|
|
611 | 611 | card.classList.add("currently-connected");
|
612 | 612 | bridgeCards.prepend(card);
|
613 | 613 | } else {
|
... | ... | @@ -714,9 +714,9 @@ const gConnectionPane = (function() { |
714 | 714 | // Add only the new strings that remained in the set
|
715 | 715 | for (const bridge of newStrings) {
|
716 | 716 | if (shownCards >= toShow) {
|
717 | - if (this._currentBridge === "") {
|
|
717 | + if (!this._currentBridgeId) {
|
|
718 | 718 | break;
|
719 | - } else if (!bridge.includes(this._currentBridge)) {
|
|
719 | + } else if (!bridge.includes(this._currentBridgeId)) {
|
|
720 | 720 | continue;
|
721 | 721 | }
|
722 | 722 | }
|
... | ... | @@ -787,7 +787,7 @@ const gConnectionPane = (function() { |
787 | 787 | )) {
|
788 | 788 | card.classList.remove("currently-connected");
|
789 | 789 | }
|
790 | - if (this._currentBridge === "") {
|
|
790 | + if (!this._currentBridgeId) {
|
|
791 | 791 | return;
|
792 | 792 | }
|
793 | 793 | // Make sure we have the connected bridge in the list
|
... | ... | @@ -796,7 +796,7 @@ const gConnectionPane = (function() { |
796 | 796 | // case also with built-in bridges!). E.g., one line for the IPv4
|
797 | 797 | // address and one for the IPv6 address, so use querySelectorAll
|
798 | 798 | const cards = bridgeCards.querySelectorAll(
|
799 | - `[data-bridge-id="${this._currentBridge}"]`
|
|
799 | + `[data-bridge-id="${this._currentBridgeId}"]`
|
|
800 | 800 | );
|
801 | 801 | for (const card of cards) {
|
802 | 802 | card.classList.add("currently-connected");
|
... | ... | @@ -823,6 +823,12 @@ const gConnectionPane = (function() { |
823 | 823 | // this circuit to check if the bridge can be used. We do this by
|
824 | 824 | // checking if the stream has SOCKS username, which actually contains
|
825 | 825 | // the destination of the stream.
|
826 | + // FIXME: We only know the currentBridge *after* a circuit event, but
|
|
827 | + // if the circuit event is sent *before* about:torpreferences is
|
|
828 | + // opened we will miss it. Therefore this approach only works if a
|
|
829 | + // circuit is created after opening about:torconnect. A dedicated
|
|
830 | + // backend outside of about:preferences would help, and could be
|
|
831 | + // shared with gTorCircuitPanel. See tor-browser#41700.
|
|
826 | 832 | this._controller.watchEvent(
|
827 | 833 | "STREAM",
|
828 | 834 | event =>
|
... | ... | @@ -836,10 +842,22 @@ const gConnectionPane = (function() { |
836 | 842 | }
|
837 | 843 | for (const status of circuitStatuses) {
|
838 | 844 | if (status.id === event.CircuitID && status.circuit.length) {
|
839 | - // The id in the circuit begins with a $ sign
|
|
840 | - const bridgeId = status.circuit[0][0].substring(1);
|
|
841 | - if (bridgeId !== this._currentBridge) {
|
|
842 | - this._currentBridge = bridgeId;
|
|
845 | + // The id in the circuit begins with a $ sign.
|
|
846 | + const id = status.circuit[0][0].replace(/^\$/, "");
|
|
847 | + if (id !== this._currentBridgeId) {
|
|
848 | + const bridge = (
|
|
849 | + await this._controller.getConf("bridge")
|
|
850 | + )?.find(
|
|
851 | + foundBridge =>
|
|
852 | + foundBridge.ID?.toUpperCase() === id.toUpperCase()
|
|
853 | + );
|
|
854 | + if (!bridge) {
|
|
855 | + // Either there is no bridge, or bridge with no
|
|
856 | + // fingerprint.
|
|
857 | + this._currentBridgeId = null;
|
|
858 | + } else {
|
|
859 | + this._currentBridgeId = id;
|
|
860 | + }
|
|
843 | 861 | this._updateConnectedBridges();
|
844 | 862 | }
|
845 | 863 | break;
|
... | ... | @@ -109,9 +109,9 @@ |
109 | 109 | <label class="torPreferences-bridgeCard-manualLink learnMore text-link stop-click" is="text-link"/>
|
110 | 110 | <html:div class="torPreferences-bridgeCard-headingAddr"/>
|
111 | 111 | <html:div class="torPreferences-bridgeCard-buttons">
|
112 | - <html:span class="torPreferences-bridgeCard-connectedBadge">
|
|
113 | - <image class="torPreferences-bridgeCard-connectedIcon"/>
|
|
114 | - <html:span class="torPreferences-bridgeCard-connectedLabel"/>
|
|
112 | + <html:span class="torPreferences-current-bridge-badge">
|
|
113 | + <image class="torPreferences-current-bridge-icon"/>
|
|
114 | + <html:span class="torPreferences-current-bridge-label"></html:span>
|
|
115 | 115 | </html:span>
|
116 | 116 | <html:button class="torPreferences-bridgeCard-options stop-click"/>
|
117 | 117 | </html:div>
|
... | ... | @@ -298,28 +298,38 @@ html:dir(rtl) input[type="checkbox"].toggle-button::before { |
298 | 298 | align-self: center;
|
299 | 299 | }
|
300 | 300 | |
301 | -.torPreferences-bridgeCard-connectedBadge {
|
|
301 | +.torPreferences-current-bridge-badge {
|
|
302 | + /* Hidden by default, otherwise display is "flex". */
|
|
302 | 303 | display: none;
|
303 | - margin-inline-end: 12px;
|
|
304 | - color: var(--purple-60);
|
|
305 | -}
|
|
306 | - |
|
307 | -@media (prefers-color-scheme: dark) {
|
|
308 | - .torPreferences-bridgeCard-connectedBadge {
|
|
309 | - color: var(--purple-30);
|
|
310 | - }
|
|
304 | + align-items: center;
|
|
305 | + font-size: 0.85em;
|
|
311 | 306 | }
|
312 | 307 | |
313 | -.currently-connected .torPreferences-bridgeCard-connectedBadge {
|
|
308 | +:is(
|
|
309 | + .builtin-bridges-option.current-builtin-bridge-type,
|
|
310 | + .torPreferences-bridgeCard.currently-connected
|
|
311 | +) .torPreferences-current-bridge-badge {
|
|
314 | 312 | display: flex;
|
315 | 313 | }
|
316 | 314 | |
317 | -.torPreferences-bridgeCard-connectedIcon {
|
|
315 | +.torPreferences-current-bridge-icon {
|
|
318 | 316 | margin-inline-start: 1px;
|
319 | 317 | margin-inline-end: 7px;
|
320 | 318 | list-style-image: url("chrome://browser/content/torpreferences/check.svg");
|
321 | 319 | -moz-context-properties: fill;
|
322 | 320 | fill: currentColor;
|
321 | + flex: 0 0 auto;
|
|
322 | +}
|
|
323 | + |
|
324 | +.torPreferences-bridgeCard .torPreferences-current-bridge-badge {
|
|
325 | + color: var(--purple-60);
|
|
326 | + margin-inline-end: 12px;
|
|
327 | +}
|
|
328 | + |
|
329 | +@media (prefers-color-scheme: dark) {
|
|
330 | + .torPreferences-bridgeCard .torPreferences-current-bridge-badge {
|
|
331 | + color: var(--purple-30);
|
|
332 | + }
|
|
323 | 333 | }
|
324 | 334 | |
325 | 335 | .torPreferences-bridgeCard-options {
|
... | ... | @@ -564,6 +574,10 @@ dialog#torPreferences-requestBridge-dialog > hbox { |
564 | 574 | font-weight: 700;
|
565 | 575 | }
|
566 | 576 | |
577 | +.builtin-bridges-option .torPreferences-current-bridge-badge {
|
|
578 | + color: var(--in-content-accent-color);
|
|
579 | +}
|
|
580 | + |
|
567 | 581 | /* Request bridge dialog */
|
568 | 582 | /*
|
569 | 583 | This hbox is hidden by css here by default so that the
|
... | ... | @@ -105,6 +105,8 @@ const Loader = { |
105 | 105 | bridgeCurrentDescription:
|
106 | 106 | "You can keep one or more bridges saved, and Tor will choose which one to use when you connect. Tor will automatically switch to use another bridge when needed.",
|
107 | 107 | bridgeId: "%1$S bridge: %2$S",
|
108 | + currentBridge: "Current bridge",
|
|
109 | + connectedBridge: "Connected",
|
|
108 | 110 | remove: "Remove",
|
109 | 111 | bridgeDisableBuiltIn: "Disable built-in bridges",
|
110 | 112 | bridgeShare:
|
... | ... | @@ -39,6 +39,8 @@ settings.bridgeCurrentDescription=You can keep one or more bridges saved, and To |
39 | 39 | |
40 | 40 | # Translation note: %1$S = bridge type; %2$S = bridge emoji id
|
41 | 41 | settings.bridgeId=%1$S bridge: %2$S
|
42 | +settings.connectedBridge=Connected
|
|
43 | +settings.currentBridge=Current bridge
|
|
42 | 44 | settings.remove=Remove
|
43 | 45 | settings.bridgeDisableBuiltIn=Disable built-in bridges
|
44 | 46 | settings.bridgeShare=Share this bridge using the QR code or by copying its address:
|