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 fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
Bug 41734 - Add a connected label to the built-in bridge dialog.
- - - - - e86c7eaf by Henry Wilkes at 2023-06-07T14:09:59+01:00 fixup! Add TorStrings module for localization
Bug 41734 - Add a connected label to the built-in bridge dialog.
- - - - -
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:
===================================== browser/components/torpreferences/content/builtinBridgeDialog.jsm ===================================== @@ -17,82 +17,81 @@ const { TorConnect, TorConnectTopics } = ChromeUtils.import( );
class BuiltinBridgeDialog { + /** + * Create a new instance. + * + * @param {Function} onSubmit - A callback for when the user accepts the + * dialog selection. + */ constructor(onSubmit) { this.onSubmit = onSubmit; - this._dialog = null; this._acceptButton = null; }
- static get selectors() { - return { - description: "#torPreferences-builtinBridge-description", - radiogroup: "#torPreferences-builtinBridge-typeSelection", - obfsRadio: "#torPreferences-builtinBridges-radioObfs", - obfsDescr: "#torPreferences-builtinBridges-descrObfs", - snowflakeRadio: "#torPreferences-builtinBridges-radioSnowflake", - snowflakeDescr: "#torPreferences-builtinBridges-descrSnowflake", - meekAzureRadio: "#torPreferences-builtinBridges-radioMeekAzure", - meekAzureDescr: "#torPreferences-builtinBridges-descrMeekAzure", - }; - } - - _populateXUL(window, aDialog) { - const selectors = BuiltinBridgeDialog.selectors; - - this._dialog = aDialog; - const dialogWin = this._dialog.parentElement; + _populateXUL(window, dialog) { + const dialogWin = dialog.parentElement; dialogWin.setAttribute("title", TorStrings.settings.builtinBridgeHeader);
- this._dialog.querySelector(selectors.description).textContent = - TorStrings.settings.builtinBridgeDescription2; + dialog.querySelector( + "#torPreferences-builtinBridge-description" + ).textContent = TorStrings.settings.builtinBridgeDescription2;
- this._acceptButton = this._dialog.getButton("accept"); + this._acceptButton = dialog.getButton("accept"); this.onTorStateChange();
- let radioGroup = this._dialog.querySelector(selectors.radiogroup); + const radioGroup = dialog.querySelector( + "#torPreferences-builtinBridge-typeSelection" + );
- let types = { + const typeStrings = { obfs4: { - elemRadio: this._dialog.querySelector(selectors.obfsRadio), - elemDescr: this._dialog.querySelector(selectors.obfsDescr), label: TorStrings.settings.builtinBridgeObfs4Title, descr: TorStrings.settings.builtinBridgeObfs4Description2, }, snowflake: { - elemRadio: this._dialog.querySelector(selectors.snowflakeRadio), - elemDescr: this._dialog.querySelector(selectors.snowflakeDescr), label: TorStrings.settings.builtinBridgeSnowflake, descr: TorStrings.settings.builtinBridgeSnowflakeDescription2, }, "meek-azure": { - elemRadio: this._dialog.querySelector(selectors.meekAzureRadio), - elemDescr: this._dialog.querySelector(selectors.meekAzureDescr), label: TorStrings.settings.builtinBridgeMeekAzure, descr: TorStrings.settings.builtinBridgeMeekAzureDescription2, }, };
- TorBuiltinBridgeTypes.forEach(type => { - types[type].elemRadio.setAttribute("label", types[type].label); - types[type].elemRadio.setAttribute("hidden", "false"); - types[type].elemDescr.textContent = types[type].descr; - types[type].elemDescr.removeAttribute("hidden"); - }); - - if ( + const currentBuiltinType = TorSettings.bridges.enabled && TorSettings.bridges.source == TorBridgeSource.BuiltIn - ) { - radioGroup.selectedItem = - types[TorSettings.bridges.builtin_type]?.elemRadio; + ? TorSettings.bridges.builtin_type + : null; + if (currentBuiltinType) { + radioGroup.value = currentBuiltinType; } else { radioGroup.selectedItem = null; }
- this._dialog.addEventListener("dialogaccept", () => { + for (const optionEl of radioGroup.querySelectorAll( + ".builtin-bridges-option" + )) { + const radio = optionEl.querySelector("radio"); + const type = radio.value; + optionEl.hidden = !TorBuiltinBridgeTypes.includes(type); + radio.label = typeStrings[type].label; + optionEl.querySelector( + ".builtin-bridges-option-description" + ).textContent = typeStrings[type].descr; + optionEl.querySelector( + ".torPreferences-current-bridge-label" + ).textContent = TorStrings.settings.currentBridge; + optionEl.classList.toggle( + "current-builtin-bridge-type", + type === currentBuiltinType + ); + } + + dialog.addEventListener("dialogaccept", () => { this.onSubmit(radioGroup.value, TorConnect.canBeginBootstrap); }); - this._dialog.addEventListener("dialoghelp", e => { + dialog.addEventListener("dialoghelp", e => { window.top.openTrustedLinkIn( TorStrings.settings.learnMoreCircumventionURL, "tab" @@ -100,8 +99,8 @@ class BuiltinBridgeDialog { });
// Hack: see the CSS - this._dialog.style.minWidth = "0"; - this._dialog.style.minHeight = "0"; + dialog.style.minWidth = "0"; + dialog.style.minHeight = "0";
Services.obs.addObserver(this, TorConnectTopics.StateChange); }
===================================== browser/components/torpreferences/content/builtinBridgeDialog.xhtml ===================================== @@ -8,16 +8,57 @@ xmlns:html="http://www.w3.org/1999/xhtml%22%3E <dialog id="torPreferences-builtinBridge-dialog" buttons="help,accept,cancel"> - <description> - <html:div id="torPreferences-builtinBridge-description">​<br/>​</html:div> + <description id="torPreferences-builtinBridge-description"> </description> <radiogroup id="torPreferences-builtinBridge-typeSelection"> - <radio id="torPreferences-builtinBridges-radioObfs" value="obfs4" hidden="true"/> - <html:div id="torPreferences-builtinBridges-descrObfs" class="indent" hidden="true">​</html:div> - <radio id="torPreferences-builtinBridges-radioSnowflake" value="snowflake" hidden="true"/> - <html:div id="torPreferences-builtinBridges-descrSnowflake" class="indent" hidden="true">​</html:div> - <radio id="torPreferences-builtinBridges-radioMeekAzure" value="meek-azure" hidden="true"/> - <html:div id="torPreferences-builtinBridges-descrMeekAzure" class="indent" hidden="true">​</html:div> + <vbox class="builtin-bridges-option"> + <hbox> + <!-- The radio option is described by both the "Current bridge" label + - and the full description. If the "Connected" label is hidden, then + - only the latter description should contribute. --> + <radio aria-describedby="obfs-bridges-current obfs-bridges-description" + value="obfs4"/> + <html:span class="torPreferences-current-bridge-badge"> + <image class="torPreferences-current-bridge-icon"/> + <html:span id="obfs-bridges-current" + class="torPreferences-current-bridge-label"> + </html:span> + </html:span> + </hbox> + <html:div id="obfs-bridges-description" + class="indent builtin-bridges-option-description"> + </html:div> + </vbox> + <vbox class="builtin-bridges-option"> + <hbox> + <radio aria-describedby="snowflake-bridges-current snowflake-bridges-description" + value="snowflake"/> + <html:span class="torPreferences-current-bridge-badge"> + <image class="torPreferences-current-bridge-icon"/> + <html:span id="snowflake-bridges-current" + class="torPreferences-current-bridge-label"> + </html:span> + </html:span> + </hbox> + <html:div id="snowflake-bridges-description" + class="indent builtin-bridges-option-description"> + </html:div> + </vbox> + <vbox class="builtin-bridges-option"> + <hbox> + <radio aria-describedby="meek-bridges-current meek-bridges-description" + value="meek-azure"/> + <html:span class="torPreferences-current-bridge-badge"> + <image class="torPreferences-current-bridge-icon"/> + <html:span id="meek-bridges-current" + class="torPreferences-current-bridge-label"> + </html:span> + </html:span> + </hbox> + <html:div id="meek-bridges-description" + class="indent builtin-bridges-option-description"> + </html:div> + </vbox> </radiogroup> <script type="application/javascript"><![CDATA[ "use strict";
===================================== browser/components/torpreferences/content/connectionPane.js ===================================== @@ -124,7 +124,7 @@ const gConnectionPane = (function() { cardId: ".torPreferences-bridgeCard-id", cardHeadingManualLink: ".torPreferences-bridgeCard-manualLink", cardHeadingAddr: ".torPreferences-bridgeCard-headingAddr", - cardConnectedLabel: ".torPreferences-bridgeCard-connectedLabel", + cardConnectedLabel: ".torPreferences-current-bridge-label", cardOptions: ".torPreferences-bridgeCard-options", cardMenu: "#torPreferences-bridgeCard-menu", cardQrGrid: ".torPreferences-bridgeCard-grid", @@ -168,7 +168,7 @@ const gConnectionPane = (function() {
_controller: null,
- _currentBridge: "", + _currentBridgeId: null,
// populate xul with strings and cache the relevant elements _populateXUL() { @@ -471,7 +471,7 @@ const gConnectionPane = (function() { } bridgeTemplate.querySelector( selectors.bridges.cardConnectedLabel - ).textContent = TorStrings.settings.statusTorConnected; + ).textContent = TorStrings.settings.connectedBridge; bridgeTemplate .querySelector(selectors.bridges.cardCopy) .setAttribute("label", TorStrings.settings.bridgeCopy); @@ -607,7 +607,7 @@ const gConnectionPane = (function() { restoreTimeout = null; }, RESTORE_TIME); }); - if (details && details.id === this._currentBridge) { + if (details?.id && details.id === this._currentBridgeId) { card.classList.add("currently-connected"); bridgeCards.prepend(card); } else { @@ -714,9 +714,9 @@ const gConnectionPane = (function() { // Add only the new strings that remained in the set for (const bridge of newStrings) { if (shownCards >= toShow) { - if (this._currentBridge === "") { + if (!this._currentBridgeId) { break; - } else if (!bridge.includes(this._currentBridge)) { + } else if (!bridge.includes(this._currentBridgeId)) { continue; } } @@ -787,7 +787,7 @@ const gConnectionPane = (function() { )) { card.classList.remove("currently-connected"); } - if (this._currentBridge === "") { + if (!this._currentBridgeId) { return; } // Make sure we have the connected bridge in the list @@ -796,7 +796,7 @@ const gConnectionPane = (function() { // case also with built-in bridges!). E.g., one line for the IPv4 // address and one for the IPv6 address, so use querySelectorAll const cards = bridgeCards.querySelectorAll( - `[data-bridge-id="${this._currentBridge}"]` + `[data-bridge-id="${this._currentBridgeId}"]` ); for (const card of cards) { card.classList.add("currently-connected"); @@ -823,6 +823,12 @@ const gConnectionPane = (function() { // this circuit to check if the bridge can be used. We do this by // checking if the stream has SOCKS username, which actually contains // the destination of the stream. + // FIXME: We only know the currentBridge *after* a circuit event, but + // if the circuit event is sent *before* about:torpreferences is + // opened we will miss it. Therefore this approach only works if a + // circuit is created after opening about:torconnect. A dedicated + // backend outside of about:preferences would help, and could be + // shared with gTorCircuitPanel. See tor-browser#41700. this._controller.watchEvent( "STREAM", event => @@ -836,10 +842,22 @@ const gConnectionPane = (function() { } for (const status of circuitStatuses) { if (status.id === event.CircuitID && status.circuit.length) { - // The id in the circuit begins with a $ sign - const bridgeId = status.circuit[0][0].substring(1); - if (bridgeId !== this._currentBridge) { - this._currentBridge = bridgeId; + // The id in the circuit begins with a $ sign. + const id = status.circuit[0][0].replace(/^$/, ""); + if (id !== this._currentBridgeId) { + const bridge = ( + await this._controller.getConf("bridge") + )?.find( + foundBridge => + foundBridge.ID?.toUpperCase() === id.toUpperCase() + ); + if (!bridge) { + // Either there is no bridge, or bridge with no + // fingerprint. + this._currentBridgeId = null; + } else { + this._currentBridgeId = id; + } this._updateConnectedBridges(); } break;
===================================== browser/components/torpreferences/content/connectionPane.xhtml ===================================== @@ -109,9 +109,9 @@ <label class="torPreferences-bridgeCard-manualLink learnMore text-link stop-click" is="text-link"/> <html:div class="torPreferences-bridgeCard-headingAddr"/> <html:div class="torPreferences-bridgeCard-buttons"> - <html:span class="torPreferences-bridgeCard-connectedBadge"> - <image class="torPreferences-bridgeCard-connectedIcon"/> - <html:span class="torPreferences-bridgeCard-connectedLabel"/> + <html:span class="torPreferences-current-bridge-badge"> + <image class="torPreferences-current-bridge-icon"/> + <html:span class="torPreferences-current-bridge-label"></html:span> </html:span> <html:button class="torPreferences-bridgeCard-options stop-click"/> </html:div>
===================================== browser/components/torpreferences/content/torPreferences.css ===================================== @@ -298,28 +298,38 @@ html:dir(rtl) input[type="checkbox"].toggle-button::before { align-self: center; }
-.torPreferences-bridgeCard-connectedBadge { +.torPreferences-current-bridge-badge { + /* Hidden by default, otherwise display is "flex". */ display: none; - margin-inline-end: 12px; - color: var(--purple-60); -} - -@media (prefers-color-scheme: dark) { - .torPreferences-bridgeCard-connectedBadge { - color: var(--purple-30); - } + align-items: center; + font-size: 0.85em; }
-.currently-connected .torPreferences-bridgeCard-connectedBadge { +:is( + .builtin-bridges-option.current-builtin-bridge-type, + .torPreferences-bridgeCard.currently-connected +) .torPreferences-current-bridge-badge { display: flex; }
-.torPreferences-bridgeCard-connectedIcon { +.torPreferences-current-bridge-icon { margin-inline-start: 1px; margin-inline-end: 7px; list-style-image: url("chrome://browser/content/torpreferences/check.svg"); -moz-context-properties: fill; fill: currentColor; + flex: 0 0 auto; +} + +.torPreferences-bridgeCard .torPreferences-current-bridge-badge { + color: var(--purple-60); + margin-inline-end: 12px; +} + +@media (prefers-color-scheme: dark) { + .torPreferences-bridgeCard .torPreferences-current-bridge-badge { + color: var(--purple-30); + } }
.torPreferences-bridgeCard-options { @@ -564,6 +574,10 @@ dialog#torPreferences-requestBridge-dialog > hbox { font-weight: 700; }
+.builtin-bridges-option .torPreferences-current-bridge-badge { + color: var(--in-content-accent-color); +} + /* Request bridge dialog */ /* This hbox is hidden by css here by default so that the
===================================== browser/modules/TorStrings.jsm ===================================== @@ -105,6 +105,8 @@ const Loader = { bridgeCurrentDescription: "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.", bridgeId: "%1$S bridge: %2$S", + currentBridge: "Current bridge", + connectedBridge: "Connected", remove: "Remove", bridgeDisableBuiltIn: "Disable built-in bridges", bridgeShare:
===================================== toolkit/torbutton/chrome/locale/en-US/settings.properties ===================================== @@ -39,6 +39,8 @@ settings.bridgeCurrentDescription=You can keep one or more bridges saved, and To
# Translation note: %1$S = bridge type; %2$S = bridge emoji id settings.bridgeId=%1$S bridge: %2$S +settings.connectedBridge=Connected +settings.currentBridge=Current bridge settings.remove=Remove settings.bridgeDisableBuiltIn=Disable built-in bridges settings.bridgeShare=Share this bridge using the QR code or by copying its address:
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/c2d6837...
tor-commits@lists.torproject.org