richard pushed to branch tor-browser-115.7.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits: 6211ed8f by Henry Wilkes at 2024-01-23T18:27:26+00:00 fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
Bug 42036: Refactor tor setting dialogs to follow firefox style.
Also, move the user focus to their new bridges when the bridge dialogs succeed.
- - - - -
16 changed files:
- + browser/components/torpreferences/content/bridgeQrDialog.js - − browser/components/torpreferences/content/bridgeQrDialog.mjs - browser/components/torpreferences/content/bridgeQrDialog.xhtml - browser/components/torpreferences/content/builtinBridgeDialog.mjs → browser/components/torpreferences/content/builtinBridgeDialog.js - browser/components/torpreferences/content/builtinBridgeDialog.xhtml - browser/components/torpreferences/content/connectionPane.js - browser/components/torpreferences/content/connectionSettingsDialog.mjs → browser/components/torpreferences/content/connectionSettingsDialog.js - browser/components/torpreferences/content/connectionSettingsDialog.xhtml - browser/components/torpreferences/content/provideBridgeDialog.mjs → browser/components/torpreferences/content/provideBridgeDialog.js - browser/components/torpreferences/content/provideBridgeDialog.xhtml - browser/components/torpreferences/content/requestBridgeDialog.mjs → browser/components/torpreferences/content/requestBridgeDialog.js - browser/components/torpreferences/content/requestBridgeDialog.xhtml - + browser/components/torpreferences/content/torLogDialog.js - − browser/components/torpreferences/content/torLogDialog.mjs - browser/components/torpreferences/content/torLogDialog.xhtml - browser/components/torpreferences/jar.mn
Changes:
===================================== browser/components/torpreferences/content/bridgeQrDialog.js ===================================== @@ -0,0 +1,34 @@ +"use strict"; + +const { QRCode } = ChromeUtils.importESModule( + "resource://gre/modules/QRCode.sys.mjs" +); + +const { TorStrings } = ChromeUtils.importESModule( + "resource://gre/modules/TorStrings.sys.mjs" +); + +window.addEventListener( + "DOMContentLoaded", + () => { + const bridgeString = window.arguments[0]; + + document.documentElement.setAttribute( + "title", + TorStrings.settings.scanQrTitle + ); + const target = document.getElementById("bridgeQr-target"); + const style = window.getComputedStyle(target); + const width = style.width.substr(0, style.width.length - 2); + const height = style.height.substr(0, style.height.length - 2); + new QRCode(target, { + text: bridgeString, + width, + height, + colorDark: style.color, + colorLight: style.backgroundColor, + document, + }); + }, + { once: true } +);
===================================== browser/components/torpreferences/content/bridgeQrDialog.mjs deleted ===================================== @@ -1,44 +0,0 @@ -import { QRCode } from "resource://gre/modules/QRCode.sys.mjs"; - -import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs"; - -export class BridgeQrDialog { - constructor() { - this._bridgeString = ""; - } - - static get selectors() { - return { - target: "#bridgeQr-target", - }; - } - - _populateXUL(window, dialog) { - dialog.parentElement.setAttribute("title", TorStrings.settings.scanQrTitle); - const target = dialog.querySelector(BridgeQrDialog.selectors.target); - const style = window.getComputedStyle(target); - const width = style.width.substr(0, style.width.length - 2); - const height = style.height.substr(0, style.height.length - 2); - new QRCode(target, { - text: this._bridgeString, - width, - height, - colorDark: style.color, - colorLight: style.backgroundColor, - document: window.document, - }); - } - - init(window, dialog) { - this._populateXUL(window, dialog); - } - - openDialog(gSubDialog, bridgeString) { - this._bridgeString = bridgeString; - gSubDialog.open( - "chrome://browser/content/torpreferences/bridgeQrDialog.xhtml", - { features: "resizable=yes" }, - this - ); - } -}
===================================== browser/components/torpreferences/content/bridgeQrDialog.xhtml ===================================== @@ -9,6 +9,8 @@ xmlns:html="http://www.w3.org/1999/xhtml"
<dialog id="bridgeQr-dialog" buttons="accept"> + <script src="chrome://browser/content/torpreferences/bridgeQrDialog.js" /> + html:div <html:div id="bridgeQr"> <html:div id="bridgeQr-target" /> @@ -16,16 +18,5 @@ <html:div id="bridgeQr-onion" /> </html:div> </html:div> - <script type="application/javascript"> - <![CDATA[ - "use strict"; - - let dialogObject = window.arguments[0]; - document.addEventListener("DOMContentLoaded", () => { - let dialogElement = document.getElementById("bridgeQr-dialog"); - dialogObject.init(window, dialogElement); - }); - ]]> - </script> </dialog> </window>
===================================== browser/components/torpreferences/content/builtinBridgeDialog.mjs → browser/components/torpreferences/content/builtinBridgeDialog.js ===================================== @@ -1,38 +1,32 @@ -import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs"; - -import { - TorSettings, - TorBridgeSource, -} from "resource://gre/modules/TorSettings.sys.mjs"; - -import { - TorConnect, - TorConnectTopics, -} from "resource://gre/modules/TorConnect.sys.mjs"; - -export 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._acceptButton = null; - this._radioGroup = null; - } - - _populateXUL(window, dialog) { - const dialogWin = dialog.parentElement; - dialogWin.setAttribute("title", TorStrings.settings.builtinBridgeHeader); - - dialog.querySelector( - "#torPreferences-builtinBridge-description" +"use strict"; + +const { TorStrings } = ChromeUtils.importESModule( + "resource://gre/modules/TorStrings.sys.mjs" +); + +const { TorSettings, TorBridgeSource } = ChromeUtils.importESModule( + "resource://gre/modules/TorSettings.sys.mjs" +); + +const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule( + "resource://gre/modules/TorConnect.sys.mjs" +); + +const gBuiltinBridgeDialog = { + init() { + this._result = window.arguments[0]; + + document.documentElement.setAttribute( + "title", + TorStrings.settings.builtinBridgeHeader + ); + + document.getElementById( + "torPreferences-builtinBridge-description" ).textContent = TorStrings.settings.builtinBridgeDescription2;
- this._radioGroup = dialog.querySelector( - "#torPreferences-builtinBridge-typeSelection" + this._radioGroup = document.getElementById( + "torPreferences-builtinBridge-typeSelection" );
const typeStrings = { @@ -84,8 +78,12 @@ export class BuiltinBridgeDialog { }
this._radioGroup.addEventListener("select", () => this.onSelectChange()); + + const dialog = document.getElementById( + "torPreferences-builtinBridge-dialog" + ); dialog.addEventListener("dialogaccept", () => { - this.onSubmit(this._radioGroup.value, TorConnect.canBeginBootstrap); + this._result.accepted = true; });
this._acceptButton = dialog.getButton("accept"); @@ -94,20 +92,28 @@ export class BuiltinBridgeDialog {
this.onSelectChange(); this.onAcceptStateChange(); - } + }, + + uninit() { + Services.obs.removeObserver(this, TorConnectTopics.StateChange); + },
onSelectChange() { - this._acceptButton.disabled = !this._radioGroup.value; - } + const value = this._radioGroup.value; + this._acceptButton.disabled = !value; + this._result.type = value; + },
onAcceptStateChange() { + const connect = TorConnect.canBeginBootstrap; + this._result.connect = connect; this._acceptButton.setAttribute( "label", - TorConnect.canBeginBootstrap + connect ? TorStrings.settings.bridgeButtonConnect : TorStrings.settings.bridgeButtonAccept ); - } + },
observe(subject, topic, data) { switch (topic) { @@ -115,27 +121,20 @@ export class BuiltinBridgeDialog { this.onAcceptStateChange(); break; } - } - - init(window, aDialog) { - this._populateXUL(window, aDialog); - } - - close() { - // Unregister our observer topics. - Services.obs.removeObserver(this, TorConnectTopics.StateChange); - } - - openDialog(gSubDialog) { - gSubDialog.open( - "chrome://browser/content/torpreferences/builtinBridgeDialog.xhtml", - { - features: "resizable=yes", - closingCallback: () => { - this.close(); - }, + }, +}; + +window.addEventListener( + "DOMContentLoaded", + () => { + gBuiltinBridgeDialog.init(); + window.addEventListener( + "unload", + () => { + gBuiltinBridgeDialog.uninit(); }, - this + { once: true } ); - } -} + }, + { once: true } +);
===================================== browser/components/torpreferences/content/builtinBridgeDialog.xhtml ===================================== @@ -9,6 +9,8 @@ xmlns:html="http://www.w3.org/1999/xhtml"
<dialog id="torPreferences-builtinBridge-dialog" buttons="accept,cancel"> + <script src="chrome://browser/content/torpreferences/builtinBridgeDialog.js" /> + <description id="torPreferences-builtinBridge-description"> </description> <radiogroup id="torPreferences-builtinBridge-typeSelection"> <vbox class="builtin-bridges-option"> @@ -78,16 +80,5 @@ </html:div> </vbox> </radiogroup> - <script type="application/javascript"> - <![CDATA[ - "use strict"; - - let builtinBridgeDialog = window.arguments[0]; - document.addEventListener("DOMContentLoaded", () => { - let dialog = document.getElementById("torPreferences-builtinBridge-dialog"); - builtinBridgeDialog.init(window, dialog); - }); - ]]> - </script> </dialog> </window>
===================================== browser/components/torpreferences/content/connectionPane.js ===================================== @@ -24,30 +24,6 @@ const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule( const { TorConnect, TorConnectTopics, TorConnectState, TorCensorshipLevel } = ChromeUtils.importESModule("resource://gre/modules/TorConnect.sys.mjs");
-const { TorLogDialog } = ChromeUtils.importESModule( - "chrome://browser/content/torpreferences/torLogDialog.mjs" -); - -const { ConnectionSettingsDialog } = ChromeUtils.importESModule( - "chrome://browser/content/torpreferences/connectionSettingsDialog.mjs" -); - -const { BridgeQrDialog } = ChromeUtils.importESModule( - "chrome://browser/content/torpreferences/bridgeQrDialog.mjs" -); - -const { BuiltinBridgeDialog } = ChromeUtils.importESModule( - "chrome://browser/content/torpreferences/builtinBridgeDialog.mjs" -); - -const { RequestBridgeDialog } = ChromeUtils.importESModule( - "chrome://browser/content/torpreferences/requestBridgeDialog.mjs" -); - -const { ProvideBridgeDialog } = ChromeUtils.importESModule( - "chrome://browser/content/torpreferences/provideBridgeDialog.mjs" -); - const { MoatRPC } = ChromeUtils.importESModule( "resource://gre/modules/Moat.sys.mjs" ); @@ -114,6 +90,19 @@ async function getConnectedBridgeId() { return bridge?.fingerprint ?? null; }
+/** + * Show the bridge QR to the user. + * + * @param {string} bridgeString - The string to use in the QR. + */ +function showBridgeQr(bridgeString) { + gSubDialog.open( + "chrome://browser/content/torpreferences/bridgeQrDialog.xhtml", + { features: "resizable=yes" }, + bridgeString + ); +} + // TODO: Instead of aria-live in the DOM, use the proposed ariaNotify // API if it gets accepted into firefox and works with screen readers. // See https://github.com/WICG/proposals/issues/112 @@ -803,8 +792,7 @@ const gBridgeGrid = { if (!bridgeLine) { return; } - const dialog = new BridgeQrDialog(); - dialog.openDialog(gSubDialog, bridgeLine); + showBridgeQr(bridgeLine); }); row.menu .querySelector(".tor-bridges-options-copy-one-menu-item") @@ -1571,8 +1559,7 @@ const gBridgeSettings = { if (!this._canQRBridges) { return; } - const dialog = new BridgeQrDialog(); - dialog.openDialog(gSubDialog, this._bridgeStrings); + showBridgeQr(this._bridgeStrings); },
/** @@ -2136,96 +2123,133 @@ const gConnectionPane = (function () { },
/** - * Save and apply settings, then optionally open about:torconnect and start - * bootstrapping. + * Open a bridge dialog that will change the users bridges. * - * @param {fucntion} changes - The changes to make. - * @param {boolean} connect - Whether to open about:torconnect and start - * bootstrapping if possible. + * @param {string} url - The url of the dialog to open. + * @param {Function} onAccept - The method to call if the bridge dialog was + * accepted by the user. This will be passed a "result" object containing + * data set by the dialog. This should return a promise that resolves once + * the bridge settings have been set, or null if the settings have not + * been applied. */ - async saveBridgeSettings(changes, connect) { - // TODO: Move focus into the bridge area. - // dialog.ownerGlobal.addEventListener("unload", () => gCurrentBridgesArea.takeFocus(), { once: true }); - // or use closedCallback in gSubDialog.open() - setTorSettings(changes); - - if (!connect) { - return; - } - - // The bridge dialog button is "connect" when Tor is not bootstrapped, - // so do the connect. + openBridgeDialog(url, onAccept) { + const result = { accepted: false, connect: false }; + let savedSettings = null; + gSubDialog.open( + url, + { + features: "resizable=yes", + closingCallback: () => { + if (!result.accepted) { + return; + } + savedSettings = onAccept(result); + if (!savedSettings) { + // No change in settings. + return; + } + if (!result.connect) { + // Do not open about:torconnect. + return; + }
- // Start Bootstrapping, which should use the configured bridges. - // NOTE: We do this regardless of any previous TorConnect Error. - if (TorConnect.canBeginBootstrap) { - TorConnect.beginBootstrap(); - } - // Open "about:torconnect". - // FIXME: If there has been a previous bootstrapping error then - // "about:torconnect" will be trying to get the user to use - // AutoBootstrapping. It is not set up to handle a forced direct - // entry to plain Bootstrapping from this dialog so the UI will not - // be aligned. In particular the - // AboutTorConnect.uiState.bootstrapCause will be aligned to - // whatever was shown previously in "about:torconnect" instead. - TorConnect.openTorConnect(); + // Wait until the settings are applied before bootstrapping. + savedSettings.then(() => { + // The bridge dialog button is "connect" when Tor is not + // bootstrapped, so do the connect. + + // Start Bootstrapping, which should use the configured bridges. + // NOTE: We do this regardless of any previous TorConnect Error. + if (TorConnect.canBeginBootstrap) { + TorConnect.beginBootstrap(); + } + // Open "about:torconnect". + // FIXME: If there has been a previous bootstrapping error then + // "about:torconnect" will be trying to get the user to use + // AutoBootstrapping. It is not set up to handle a forced direct + // entry to plain Bootstrapping from this dialog so the UI will + // not be aligned. In particular the + // AboutTorConnect.uiState.bootstrapCause will be aligned to + // whatever was shown previously in "about:torconnect" instead. + TorConnect.openTorConnect(); + }); + }, + // closedCallback should be called after gSubDialog has already + // re-assigned focus back to the document. + closedCallback: () => { + if (!savedSettings) { + return; + } + // Wait until the settings have changed, so that the UI could + // respond, then move focus. + savedSettings.then(() => gCurrentBridgesArea.takeFocus()); + }, + }, + result + ); },
onAddBuiltinBridge() { - const builtinBridgeDialog = new BuiltinBridgeDialog( - (bridgeType, connect) => { - this.saveBridgeSettings(() => { + this.openBridgeDialog( + "chrome://browser/content/torpreferences/builtinBridgeDialog.xhtml", + result => { + if (!result.type) { + return null; + } + return setTorSettings(() => { TorSettings.bridges.enabled = true; TorSettings.bridges.source = TorBridgeSource.BuiltIn; - TorSettings.bridges.builtin_type = bridgeType; - }, connect); + TorSettings.bridges.builtin_type = result.type; + }); } ); - builtinBridgeDialog.openDialog(gSubDialog); },
// called when the request bridge button is activated onRequestBridge() { - const requestBridgeDialog = new RequestBridgeDialog( - (aBridges, connect) => { - if (!aBridges.length) { - return; + this.openBridgeDialog( + "chrome://browser/content/torpreferences/requestBridgeDialog.xhtml", + result => { + if (!result.bridges?.length) { + return null; } - - const bridgeStrings = aBridges.join("\n"); - - this.saveBridgeSettings(() => { + return setTorSettings(() => { TorSettings.bridges.enabled = true; TorSettings.bridges.source = TorBridgeSource.BridgeDB; - TorSettings.bridges.bridge_strings = bridgeStrings; - }, connect); + TorSettings.bridges.bridge_strings = result.bridges.join("\n"); + }); } ); - requestBridgeDialog.openDialog(gSubDialog); },
onAddBridgeManually() { - const provideBridgeDialog = new ProvideBridgeDialog( - (aBridgeString, connect) => { - this.saveBridgeSettings(() => { + this.openBridgeDialog( + "chrome://browser/content/torpreferences/provideBridgeDialog.xhtml", + result => { + if (!result.bridgeStrings) { + return null; + } + return setTorSettings(() => { TorSettings.bridges.enabled = true; TorSettings.bridges.source = TorBridgeSource.UserProvided; - TorSettings.bridges.bridge_strings = aBridgeString; - }, connect); + TorSettings.bridges.bridge_strings = result.bridgeStrings; + }); } ); - provideBridgeDialog.openDialog(gSubDialog); },
onAdvancedSettings() { - const connectionSettingsDialog = new ConnectionSettingsDialog(); - connectionSettingsDialog.openDialog(gSubDialog); + gSubDialog.open( + "chrome://browser/content/torpreferences/connectionSettingsDialog.xhtml", + { features: "resizable=yes" } + ); },
onViewTorLogs() { - const torLogDialog = new TorLogDialog(); - torLogDialog.openDialog(gSubDialog); + gSubDialog.open( + "chrome://browser/content/torpreferences/torLogDialog.xhtml", + { features: "resizable=yes" } + ); }, }; return retval;
===================================== browser/components/torpreferences/content/connectionSettingsDialog.mjs → browser/components/torpreferences/content/connectionSettingsDialog.js ===================================== @@ -1,73 +1,67 @@ -import { - TorSettings, - TorProxyType, -} from "resource://gre/modules/TorSettings.sys.mjs"; +"use strict";
-import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs"; +const { TorSettings, TorProxyType } = ChromeUtils.importESModule( + "resource://gre/modules/TorSettings.sys.mjs" +);
-export class ConnectionSettingsDialog { - constructor() { - this._dialog = null; - this._useProxyCheckbox = null; - this._proxyTypeLabel = null; - this._proxyTypeMenulist = null; - this._proxyAddressLabel = null; - this._proxyAddressTextbox = null; - this._proxyPortLabel = null; - this._proxyPortTextbox = null; - this._proxyUsernameLabel = null; - this._proxyUsernameTextbox = null; - this._proxyPasswordLabel = null; - this._proxyPasswordTextbox = null; - this._useFirewallCheckbox = null; - this._allowedPortsLabel = null; - this._allowedPortsTextbox = null; - } +const { TorStrings } = ChromeUtils.importESModule( + "resource://gre/modules/TorStrings.sys.mjs" +);
- static get selectors() { - return { - header: "#torPreferences-connection-header", - useProxyCheckbox: "checkbox#torPreferences-connection-toggleProxy", - proxyTypeLabel: "label#torPreferences-localProxy-type", - proxyTypeList: "menulist#torPreferences-localProxy-builtinList", - proxyAddressLabel: "label#torPreferences-localProxy-address", - proxyAddressTextbox: "input#torPreferences-localProxy-textboxAddress", - proxyPortLabel: "label#torPreferences-localProxy-port", - proxyPortTextbox: "input#torPreferences-localProxy-textboxPort", - proxyUsernameLabel: "label#torPreferences-localProxy-username", - proxyUsernameTextbox: "input#torPreferences-localProxy-textboxUsername", - proxyPasswordLabel: "label#torPreferences-localProxy-password", - proxyPasswordTextbox: "input#torPreferences-localProxy-textboxPassword", - useFirewallCheckbox: "checkbox#torPreferences-connection-toggleFirewall", - firewallAllowedPortsLabel: "label#torPreferences-connection-allowedPorts", - firewallAllowedPortsTextbox: - "input#torPreferences-connection-textboxAllowedPorts", - }; - } +const gConnectionSettingsDialog = { + _useProxyCheckbox: null, + _proxyTypeLabel: null, + _proxyTypeMenulist: null, + _proxyAddressLabel: null, + _proxyAddressTextbox: null, + _proxyPortLabel: null, + _proxyPortTextbox: null, + _proxyUsernameLabel: null, + _proxyUsernameTextbox: null, + _proxyPasswordLabel: null, + _proxyPasswordTextbox: null, + _useFirewallCheckbox: null, + _allowedPortsLabel: null, + _allowedPortsTextbox: null, + + selectors: { + header: "#torPreferences-connection-header", + useProxyCheckbox: "checkbox#torPreferences-connection-toggleProxy", + proxyTypeLabel: "label#torPreferences-localProxy-type", + proxyTypeList: "menulist#torPreferences-localProxy-builtinList", + proxyAddressLabel: "label#torPreferences-localProxy-address", + proxyAddressTextbox: "input#torPreferences-localProxy-textboxAddress", + proxyPortLabel: "label#torPreferences-localProxy-port", + proxyPortTextbox: "input#torPreferences-localProxy-textboxPort", + proxyUsernameLabel: "label#torPreferences-localProxy-username", + proxyUsernameTextbox: "input#torPreferences-localProxy-textboxUsername", + proxyPasswordLabel: "label#torPreferences-localProxy-password", + proxyPasswordTextbox: "input#torPreferences-localProxy-textboxPassword", + useFirewallCheckbox: "checkbox#torPreferences-connection-toggleFirewall", + firewallAllowedPortsLabel: "label#torPreferences-connection-allowedPorts", + firewallAllowedPortsTextbox: + "input#torPreferences-connection-textboxAllowedPorts", + },
// disables the provided list of elements _setElementsDisabled(elements, disabled) { for (let currentElement of elements) { currentElement.disabled = disabled; } - } + },
- _populateXUL(window, aDialog) { - const selectors = ConnectionSettingsDialog.selectors; + init() { + const selectors = this.selectors;
- this._dialog = aDialog; - const dialogWin = this._dialog.parentElement; - dialogWin.setAttribute( + document.documentElement.setAttribute( "title", TorStrings.settings.connectionSettingsDialogTitle ); - this._dialog.querySelector(selectors.header).textContent = + document.querySelector(selectors.header).textContent = TorStrings.settings.connectionSettingsDialogHeader;
// Local Proxy - this._useProxyCheckbox = this._dialog.querySelector( - selectors.useProxyCheckbox - ); + this._useProxyCheckbox = document.querySelector(selectors.useProxyCheckbox); this._useProxyCheckbox.setAttribute( "label", TorStrings.settings.useLocalProxy @@ -76,7 +70,7 @@ export class ConnectionSettingsDialog { const checked = this._useProxyCheckbox.checked; this.onToggleProxy(checked); }); - this._proxyTypeLabel = this._dialog.querySelector(selectors.proxyTypeLabel); + this._proxyTypeLabel = document.querySelector(selectors.proxyTypeLabel); this._proxyTypeLabel.setAttribute("value", TorStrings.settings.proxyType);
let mockProxies = [ @@ -90,9 +84,7 @@ export class ConnectionSettingsDialog { }, { value: TorProxyType.HTTPS, label: TorStrings.settings.proxyTypeHTTP }, ]; - this._proxyTypeMenulist = this._dialog.querySelector( - selectors.proxyTypeList - ); + this._proxyTypeMenulist = document.querySelector(selectors.proxyTypeList); this._proxyTypeMenulist.addEventListener("command", e => { const value = this._proxyTypeMenulist.value; this.onSelectProxyType(value); @@ -104,14 +96,14 @@ export class ConnectionSettingsDialog { this._proxyTypeMenulist.querySelector("menupopup").appendChild(menuEntry); }
- this._proxyAddressLabel = this._dialog.querySelector( + this._proxyAddressLabel = document.querySelector( selectors.proxyAddressLabel ); this._proxyAddressLabel.setAttribute( "value", TorStrings.settings.proxyAddress ); - this._proxyAddressTextbox = this._dialog.querySelector( + this._proxyAddressTextbox = document.querySelector( selectors.proxyAddressTextbox ); this._proxyAddressTextbox.setAttribute( @@ -129,33 +121,31 @@ export class ConnectionSettingsDialog { } } }); - this._proxyPortLabel = this._dialog.querySelector(selectors.proxyPortLabel); + this._proxyPortLabel = document.querySelector(selectors.proxyPortLabel); this._proxyPortLabel.setAttribute("value", TorStrings.settings.proxyPort); - this._proxyPortTextbox = this._dialog.querySelector( - selectors.proxyPortTextbox - ); - this._proxyUsernameLabel = this._dialog.querySelector( + this._proxyPortTextbox = document.querySelector(selectors.proxyPortTextbox); + this._proxyUsernameLabel = document.querySelector( selectors.proxyUsernameLabel ); this._proxyUsernameLabel.setAttribute( "value", TorStrings.settings.proxyUsername ); - this._proxyUsernameTextbox = this._dialog.querySelector( + this._proxyUsernameTextbox = document.querySelector( selectors.proxyUsernameTextbox ); this._proxyUsernameTextbox.setAttribute( "placeholder", TorStrings.settings.proxyUsernamePasswordPlaceholder ); - this._proxyPasswordLabel = this._dialog.querySelector( + this._proxyPasswordLabel = document.querySelector( selectors.proxyPasswordLabel ); this._proxyPasswordLabel.setAttribute( "value", TorStrings.settings.proxyPassword ); - this._proxyPasswordTextbox = this._dialog.querySelector( + this._proxyPasswordTextbox = document.querySelector( selectors.proxyPasswordTextbox ); this._proxyPasswordTextbox.setAttribute( @@ -174,7 +164,7 @@ export class ConnectionSettingsDialog { }
// Local firewall - this._useFirewallCheckbox = this._dialog.querySelector( + this._useFirewallCheckbox = document.querySelector( selectors.useFirewallCheckbox ); this._useFirewallCheckbox.setAttribute( @@ -185,14 +175,14 @@ export class ConnectionSettingsDialog { const checked = this._useFirewallCheckbox.checked; this.onToggleFirewall(checked); }); - this._allowedPortsLabel = this._dialog.querySelector( + this._allowedPortsLabel = document.querySelector( selectors.firewallAllowedPortsLabel ); this._allowedPortsLabel.setAttribute( "value", TorStrings.settings.allowedPorts ); - this._allowedPortsTextbox = this._dialog.querySelector( + this._allowedPortsTextbox = document.querySelector( selectors.firewallAllowedPortsTextbox ); this._allowedPortsTextbox.setAttribute( @@ -207,10 +197,11 @@ export class ConnectionSettingsDialog { TorSettings.firewall.allowed_ports.join(", "); }
- this._dialog.addEventListener("dialogaccept", e => { + const dialog = document.getElementById("torPreferences-connection-dialog"); + dialog.addEventListener("dialogaccept", e => { this._applySettings(); }); - } + },
// callback when proxy is toggled onToggleProxy(enabled) { @@ -235,7 +226,7 @@ export class ConnectionSettingsDialog { if (enabled) { this.onSelectProxyType(this._proxyTypeMenulist.value); } - } + },
// callback when proxy type is changed onSelectProxyType(value) { @@ -308,7 +299,7 @@ export class ConnectionSettingsDialog { break; } } - } + },
// callback when firewall proxy is toggled onToggleFirewall(enabled) { @@ -319,7 +310,7 @@ export class ConnectionSettingsDialog { [this._allowedPortsLabel, this._allowedPortsTextbox], disabled ); - } + },
// pushes settings from UI to tor _applySettings() { @@ -372,17 +363,13 @@ export class ConnectionSettingsDialog {
TorSettings.saveToPrefs(); TorSettings.applySettings(); - } - - init(window, aDialog) { - this._populateXUL(window, aDialog); - } + }, +};
- openDialog(gSubDialog) { - gSubDialog.open( - "chrome://browser/content/torpreferences/connectionSettingsDialog.xhtml", - { features: "resizable=yes" }, - this - ); - } -} +window.addEventListener( + "DOMContentLoaded", + () => { + gConnectionSettingsDialog.init(); + }, + { once: true } +);
===================================== browser/components/torpreferences/content/connectionSettingsDialog.xhtml ===================================== @@ -9,6 +9,8 @@ xmlns:html="http://www.w3.org/1999/xhtml"
<dialog id="torPreferences-connection-dialog" buttons="accept,cancel"> + <script src="chrome://browser/content/torpreferences/connectionSettingsDialog.js" /> + <html:h3 id="torPreferences-connection-header">​</html:h3> <!-- Local Proxy --> <checkbox id="torPreferences-connection-toggleProxy" label="​" /> @@ -78,16 +80,5 @@ /> </hbox> </box> - <script type="application/javascript"> - <![CDATA[ - "use strict"; - - let connectionSettingsDialog = window.arguments[0]; - document.addEventListener("DOMContentLoaded", () => { - let dialog = document.getElementById("torPreferences-connection-dialog"); - connectionSettingsDialog.init(window, dialog); - }); - ]]> - </script> </dialog> </window>
===================================== browser/components/torpreferences/content/provideBridgeDialog.mjs → browser/components/torpreferences/content/provideBridgeDialog.js ===================================== @@ -1,53 +1,44 @@ -import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs"; - -import { - TorSettings, - TorBridgeSource, -} from "resource://gre/modules/TorSettings.sys.mjs"; - -import { - TorConnect, - TorConnectTopics, -} from "resource://gre/modules/TorConnect.sys.mjs"; - -export class ProvideBridgeDialog { - constructor(onSubmit) { - this.onSubmit = onSubmit; - this._dialog = null; - this._textarea = null; - this._acceptButton = null; - } - - static get selectors() { - return { - description: "#torPreferences-provideBridge-description", - textarea: "#torPreferences-provideBridge-textarea", - }; - } - - _populateXUL(window, aDialog) { - const selectors = ProvideBridgeDialog.selectors; - - const openHelp = () => { +"use strict"; + +const { TorStrings } = ChromeUtils.importESModule( + "resource://gre/modules/TorStrings.sys.mjs" +); + +const { TorSettings, TorBridgeSource } = ChromeUtils.importESModule( + "resource://gre/modules/TorSettings.sys.mjs" +); + +const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule( + "resource://gre/modules/TorConnect.sys.mjs" +); + +const gProvideBridgeDialog = { + init() { + this._result = window.arguments[0]; + + document.documentElement.setAttribute( + "title", + TorStrings.settings.provideBridgeTitleAdd + ); + const learnMore = document.createXULElement("label"); + learnMore.className = "learnMore text-link"; + learnMore.setAttribute("is", "text-link"); + learnMore.setAttribute("value", TorStrings.settings.learnMore); + learnMore.addEventListener("click", () => { window.top.openTrustedLinkIn( TorStrings.settings.learnMoreBridgesURL, "tab" ); - }; + });
- this._dialog = aDialog; - const dialogWin = this._dialog.parentElement; - dialogWin.setAttribute("title", TorStrings.settings.provideBridgeTitleAdd); - const learnMore = window.document.createXULElement("label"); - learnMore.className = "learnMore text-link"; - learnMore.setAttribute("is", "text-link"); - learnMore.setAttribute("value", TorStrings.settings.learnMore); - learnMore.addEventListener("click", openHelp); - const descr = this._dialog.querySelector(selectors.description); - descr.textContent = ""; const pieces = TorStrings.settings.provideBridgeDescription.split("%S"); - descr.append(pieces[0], learnMore, pieces[1] || ""); - this._textarea = this._dialog.querySelector(selectors.textarea); + document + .getElementById("torPreferences-provideBridge-description") + .replaceChildren(pieces[0], learnMore, pieces[1] || ""); + + this._textarea = document.getElementById( + "torPreferences-provideBridge-textarea" + ); this._textarea.setAttribute( "placeholder", TorStrings.settings.provideBridgePlaceholder @@ -58,32 +49,44 @@ export class ProvideBridgeDialog { this._textarea.value = TorSettings.bridges.bridge_strings.join("\n"); }
- this._dialog.addEventListener("dialogaccept", e => { - this.onSubmit(this._textarea.value, TorConnect.canBeginBootstrap); + const dialog = document.getElementById( + "torPreferences-provideBridge-dialog" + ); + dialog.addEventListener("dialogaccept", e => { + this._result.accepted = true; });
- this._acceptButton = this._dialog.getButton("accept"); + this._acceptButton = dialog.getButton("accept");
Services.obs.addObserver(this, TorConnectTopics.StateChange);
this.onValueChange(); this.onAcceptStateChange(); - } + }, + + uninit() { + Services.obs.removeObserver(this, TorConnectTopics.StateChange); + },
onValueChange() { // TODO: Do some proper value parsing and error reporting. See // tor-browser#40552. - this._acceptButton.disabled = !this._textarea.value.trim(); - } + const value = this._textarea.value.trim(); + this._acceptButton.disabled = !value; + this._result.bridgeStrings = value; + },
onAcceptStateChange() { + const connect = TorConnect.canBeginBootstrap; + this._result.connect = connect; + this._acceptButton.setAttribute( "label", - TorConnect.canBeginBootstrap + connect ? TorStrings.settings.bridgeButtonConnect : TorStrings.settings.bridgeButtonAccept ); - } + },
observe(subject, topic, data) { switch (topic) { @@ -91,27 +94,20 @@ export class ProvideBridgeDialog { this.onAcceptStateChange(); break; } - } - - init(window, aDialog) { - this._populateXUL(window, aDialog); - } - - close() { - // Unregister our observer topics. - Services.obs.removeObserver(this, TorConnectTopics.StateChange); - } - - openDialog(gSubDialog) { - gSubDialog.open( - "chrome://browser/content/torpreferences/provideBridgeDialog.xhtml", - { - features: "resizable=yes", - closingCallback: () => { - this.close(); - }, + }, +}; + +window.addEventListener( + "DOMContentLoaded", + () => { + gProvideBridgeDialog.init(); + window.addEventListener( + "unload", + () => { + gProvideBridgeDialog.uninit(); }, - this + { once: true } ); - } -} + }, + { once: true } +);
===================================== browser/components/torpreferences/content/provideBridgeDialog.xhtml ===================================== @@ -9,6 +9,8 @@ xmlns:html="http://www.w3.org/1999/xhtml"
<dialog id="torPreferences-provideBridge-dialog" buttons="accept,cancel"> + <script src="chrome://browser/content/torpreferences/provideBridgeDialog.js" /> + <description> <html:div id="torPreferences-provideBridge-description" >​<br />​</html:div @@ -19,16 +21,5 @@ multiline="true" rows="3" /> - <script type="application/javascript"> - <![CDATA[ - "use strict"; - - let provideBridgeDialog = window.arguments[0]; - document.addEventListener("DOMContentLoaded", () => { - let dialog = document.getElementById("torPreferences-provideBridge-dialog"); - provideBridgeDialog.init(window, dialog); - }); - ]]> - </script> </dialog> </window>
===================================== browser/components/torpreferences/content/requestBridgeDialog.mjs → browser/components/torpreferences/content/requestBridgeDialog.js ===================================== @@ -1,44 +1,39 @@ -import { BridgeDB } from "resource://gre/modules/BridgeDB.sys.mjs"; -import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs"; - -import { - TorConnect, - TorConnectTopics, -} from "resource://gre/modules/TorConnect.sys.mjs"; - -export class RequestBridgeDialog { - constructor(onSubmit) { - this.onSubmit = onSubmit; - this._dialog = null; - this._submitButton = null; - this._dialogHeader = null; - this._captchaImage = null; - this._captchaEntryTextbox = null; - this._captchaRefreshButton = null; - this._incorrectCaptchaHbox = null; - this._incorrectCaptchaLabel = null; - } - - static get selectors() { - return { - dialogHeader: "h3#torPreferences-requestBridge-header", - captchaImage: "image#torPreferences-requestBridge-captchaImage", - captchaEntryTextbox: "input#torPreferences-requestBridge-captchaTextbox", - refreshCaptchaButton: - "button#torPreferences-requestBridge-refreshCaptchaButton", - incorrectCaptchaHbox: - "hbox#torPreferences-requestBridge-incorrectCaptchaHbox", - incorrectCaptchaLabel: - "label#torPreferences-requestBridge-incorrectCaptchaError", - }; - } - - _populateXUL(window, dialog) { - const selectors = RequestBridgeDialog.selectors; +"use strict"; + +const { BridgeDB } = ChromeUtils.importESModule( + "resource://gre/modules/BridgeDB.sys.mjs" +); +const { TorStrings } = ChromeUtils.importESModule( + "resource://gre/modules/TorStrings.sys.mjs" +); + +const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule( + "resource://gre/modules/TorConnect.sys.mjs" +); + +const gRequestBridgeDialog = { + selectors: { + dialogHeader: "h3#torPreferences-requestBridge-header", + captchaImage: "image#torPreferences-requestBridge-captchaImage", + captchaEntryTextbox: "input#torPreferences-requestBridge-captchaTextbox", + refreshCaptchaButton: + "button#torPreferences-requestBridge-refreshCaptchaButton", + incorrectCaptchaHbox: + "hbox#torPreferences-requestBridge-incorrectCaptchaHbox", + incorrectCaptchaLabel: + "label#torPreferences-requestBridge-incorrectCaptchaError", + }, + + init() { + this._result = window.arguments[0]; + + const selectors = this.selectors; + + this._dialog = document.getElementById( + "torPreferences-requestBridge-dialog" + );
- this._dialog = dialog; - const dialogWin = dialog.parentElement; - dialogWin.setAttribute( + document.documentElement.setAttribute( "title", TorStrings.settings.requestBridgeDialogTitle ); @@ -104,16 +99,24 @@ export class RequestBridgeDialog {
Services.obs.addObserver(this, TorConnectTopics.StateChange); this.onAcceptStateChange(); - } + }, + + uninit() { + BridgeDB.close(); + // Unregister our observer topics. + Services.obs.removeObserver(this, TorConnectTopics.StateChange); + },
onAcceptStateChange() { + const connect = TorConnect.canBeginBootstrap; + this._result.connect = connect; this._submitButton.setAttribute( "label", - TorConnect.canBeginBootstrap + connect ? TorStrings.settings.bridgeButtonConnect : TorStrings.settings.submitCaptcha ); - } + },
observe(subject, topic, data) { switch (topic) { @@ -121,7 +124,7 @@ export class RequestBridgeDialog { this.onAcceptStateChange(); break; } - } + },
_setcaptchaImage(uri) { if (uri != this._captchaImage.src) { @@ -131,27 +134,17 @@ export class RequestBridgeDialog { this._captchaEntryTextbox.focus(); this._captchaEntryTextbox.select(); } - } + },
_setUIDisabled(disabled) { this._submitButton.disabled = this._captchaGuessIsEmpty() || disabled; this._captchaEntryTextbox.disabled = disabled; this._captchaRefreshButton.disabled = disabled; - } + },
_captchaGuessIsEmpty() { return this._captchaEntryTextbox.value == ""; - } - - init(window, dialog) { - this._populateXUL(window, dialog); - } - - close() { - BridgeDB.close(); - // Unregister our observer topics. - Services.obs.removeObserver(this, TorConnectTopics.StateChange); - } + },
/* Event Handlers @@ -169,8 +162,9 @@ export class RequestBridgeDialog {
BridgeDB.submitCaptchaGuess(captchaText) .then(aBridges => { - if (aBridges) { - this.onSubmit(aBridges, TorConnect.canBeginBootstrap); + if (aBridges && aBridges.length) { + this._result.accepted = true; + this._result.bridges = aBridges; this._submitButton.disabled = false; // This was successful, but use cancelDialog() to close, since // we intercept the `dialogaccept` event. @@ -186,7 +180,7 @@ export class RequestBridgeDialog { this._incorrectCaptchaHbox.style.visibility = "visible"; console.log(aError); }); - } + },
onRefreshCaptcha() { this._setUIDisabled(true); @@ -198,18 +192,20 @@ export class RequestBridgeDialog { BridgeDB.requestNewCaptchaImage().then(uri => { this._setcaptchaImage(uri); }); - } - - openDialog(gSubDialog) { - gSubDialog.open( - "chrome://browser/content/torpreferences/requestBridgeDialog.xhtml", - { - features: "resizable=yes", - closingCallback: () => { - this.close(); - }, + }, +}; + +window.addEventListener( + "DOMContentLoaded", + () => { + gRequestBridgeDialog.init(); + window.addEventListener( + "unload", + () => { + gRequestBridgeDialog.uninit(); }, - this + { once: true } ); - } -} + }, + { once: true } +);
===================================== browser/components/torpreferences/content/requestBridgeDialog.xhtml ===================================== @@ -9,6 +9,8 @@ xmlns:html="http://www.w3.org/1999/xhtml"
<dialog id="torPreferences-requestBridge-dialog" buttons="accept,cancel"> + <script src="chrome://browser/content/torpreferences/requestBridgeDialog.js" /> + <!-- ok, so ​ is a zero-width space. We need to have *something* in the innerText so that XUL knows how tall the title node is so that it can determine how large to make the dialog element's inner draw area. If we have nothing in the innerText, then it collapse to 0 height, and the contents of the dialog ends up partially hidden >:( --> @@ -30,16 +32,5 @@ <image id="torPreferences-requestBridge-errorIcon" /> <label id="torPreferences-requestBridge-incorrectCaptchaError" flex="1" /> </hbox> - <script type="application/javascript"> - <![CDATA[ - "use strict"; - - let requestBridgeDialog = window.arguments[0]; - document.addEventListener("DOMContentLoaded", () => { - let dialog = document.getElementById("torPreferences-requestBridge-dialog"); - requestBridgeDialog.init(window, dialog); - }); - ]]> - </script> </dialog> </window>
===================================== browser/components/torpreferences/content/torLogDialog.js ===================================== @@ -0,0 +1,62 @@ +"use strict"; + +const { setTimeout, clearTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +const { TorProviderBuilder } = ChromeUtils.importESModule( + "resource://gre/modules/TorProviderBuilder.sys.mjs" +); +const { TorStrings } = ChromeUtils.importESModule( + "resource://gre/modules/TorStrings.sys.mjs" +); + +window.addEventListener( + "DOMContentLoaded", + () => { + document.documentElement.setAttribute( + "title", + TorStrings.settings.torLogDialogTitle + ); + + const dialog = document.getElementById("torPreferences-torLog-dialog"); + const copyLogButton = dialog.getButton("extra1"); + copyLogButton.setAttribute("label", TorStrings.settings.copyLog); + + const logText = document.getElementById( + "torPreferences-torDialog-textarea" + ); + + let restoreButtonTimeout = null; + copyLogButton.addEventListener("command", () => { + // Copy tor log messages to the system clipboard. + let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( + Ci.nsIClipboardHelper + ); + clipboard.copyString(logText.value); + + const label = copyLogButton.querySelector("label"); + label.setAttribute("value", TorStrings.settings.copied); + copyLogButton.classList.add("primary"); + + const RESTORE_TIME = 1200; + if (restoreButtonTimeout !== null) { + clearTimeout(restoreButtonTimeout); + } + restoreButtonTimeout = setTimeout(() => { + label.setAttribute("value", TorStrings.settings.copyLog); + copyLogButton.classList.remove("primary"); + restoreButtonTimeout = null; + }, RESTORE_TIME); + }); + + // A waiting state should not be needed at this point. + // Also, we probably cannot even arrive here if the provider failed to + // initialize, otherwise we could use a try/catch, and write the exception + // text in the logs, instead. + TorProviderBuilder.build().then( + provider => (logText.value = provider.getLog()) + ); + }, + { once: true } +);
===================================== browser/components/torpreferences/content/torLogDialog.mjs deleted ===================================== @@ -1,78 +0,0 @@ -import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs"; - -import { TorProviderBuilder } from "resource://gre/modules/TorProviderBuilder.sys.mjs"; -import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs"; - -export class TorLogDialog { - constructor() { - this._dialog = null; - this._logTextarea = null; - this._copyLogButton = null; - this._restoreButtonTimeout = null; - } - - static get selectors() { - return { - copyLogButton: "extra1", - logTextarea: "textarea#torPreferences-torDialog-textarea", - }; - } - - async _populateXUL(aDialog) { - this._dialog = aDialog; - const dialogWin = this._dialog.parentElement; - dialogWin.setAttribute("title", TorStrings.settings.torLogDialogTitle); - - this._logTextarea = this._dialog.querySelector( - TorLogDialog.selectors.logTextarea - ); - - this._copyLogButton = this._dialog.getButton( - TorLogDialog.selectors.copyLogButton - ); - this._copyLogButton.setAttribute("label", TorStrings.settings.copyLog); - this._copyLogButton.addEventListener("command", () => { - this.copyTorLog(); - const label = this._copyLogButton.querySelector("label"); - label.setAttribute("value", TorStrings.settings.copied); - this._copyLogButton.classList.add("primary"); - - const RESTORE_TIME = 1200; - if (this._restoreButtonTimeout !== null) { - clearTimeout(this._restoreButtonTimeout); - } - this._restoreButtonTimeout = setTimeout(() => { - label.setAttribute("value", TorStrings.settings.copyLog); - this._copyLogButton.classList.remove("primary"); - this._restoreButtonTimeout = null; - }, RESTORE_TIME); - }); - - // A waiting state should not be needed at this point. - // Also, we probably cannot even arrive here if the provider failed to - // initialize, otherwise we could use a try/catch, and write the exception - // text in the logs, instead. - const provider = await TorProviderBuilder.build(); - this._logTextarea.value = provider.getLog(); - } - - init(window, aDialog) { - this._populateXUL(aDialog); - } - - copyTorLog() { - // Copy tor log messages to the system clipboard. - let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( - Ci.nsIClipboardHelper - ); - clipboard.copyString(this._logTextarea.value); - } - - openDialog(gSubDialog) { - gSubDialog.open( - "chrome://browser/content/torpreferences/torLogDialog.xhtml", - { features: "resizable=yes" }, - this - ); - } -}
===================================== browser/components/torpreferences/content/torLogDialog.xhtml ===================================== @@ -9,21 +9,12 @@ xmlns:html="http://www.w3.org/1999/xhtml"
<dialog id="torPreferences-torLog-dialog" buttons="accept,extra1"> + <script src="chrome://browser/content/torpreferences/torLogDialog.js" /> + <html:textarea id="torPreferences-torDialog-textarea" multiline="true" readonly="true" /> - <script type="application/javascript"> - <![CDATA[ - "use strict"; - - let torLogDialog = window.arguments[0]; - document.addEventListener("DOMContentLoaded", () => { - let dialog = document.getElementById("torPreferences-torLog-dialog"); - torLogDialog.init(window, dialog); - }); - ]]> - </script> </dialog> </window>
===================================== browser/components/torpreferences/jar.mn ===================================== @@ -2,19 +2,19 @@ browser.jar: content/browser/torpreferences/bridge.svg (content/bridge.svg) content/browser/torpreferences/bridge-qr.svg (content/bridge-qr.svg) content/browser/torpreferences/bridgeQrDialog.xhtml (content/bridgeQrDialog.xhtml) - content/browser/torpreferences/bridgeQrDialog.mjs (content/bridgeQrDialog.mjs) + content/browser/torpreferences/bridgeQrDialog.js (content/bridgeQrDialog.js) content/browser/torpreferences/builtinBridgeDialog.xhtml (content/builtinBridgeDialog.xhtml) - content/browser/torpreferences/builtinBridgeDialog.mjs (content/builtinBridgeDialog.mjs) + content/browser/torpreferences/builtinBridgeDialog.js (content/builtinBridgeDialog.js) content/browser/torpreferences/connectionSettingsDialog.xhtml (content/connectionSettingsDialog.xhtml) - content/browser/torpreferences/connectionSettingsDialog.mjs (content/connectionSettingsDialog.mjs) + content/browser/torpreferences/connectionSettingsDialog.js (content/connectionSettingsDialog.js) content/browser/torpreferences/network.svg (content/network.svg) content/browser/torpreferences/network-broken.svg (content/network-broken.svg) content/browser/torpreferences/provideBridgeDialog.xhtml (content/provideBridgeDialog.xhtml) - content/browser/torpreferences/provideBridgeDialog.mjs (content/provideBridgeDialog.mjs) + content/browser/torpreferences/provideBridgeDialog.js (content/provideBridgeDialog.js) content/browser/torpreferences/requestBridgeDialog.xhtml (content/requestBridgeDialog.xhtml) - content/browser/torpreferences/requestBridgeDialog.mjs (content/requestBridgeDialog.mjs) + content/browser/torpreferences/requestBridgeDialog.js (content/requestBridgeDialog.js) content/browser/torpreferences/connectionCategory.inc.xhtml (content/connectionCategory.inc.xhtml) - content/browser/torpreferences/torLogDialog.mjs (content/torLogDialog.mjs) + content/browser/torpreferences/torLogDialog.js (content/torLogDialog.js) content/browser/torpreferences/torLogDialog.xhtml (content/torLogDialog.xhtml) content/browser/torpreferences/connectionPane.js (content/connectionPane.js) content/browser/torpreferences/connectionPane.xhtml (content/connectionPane.xhtml)
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/6211ed8f...