This is an automated email from the git hooks/post-receive script.
richard pushed a commit to branch tor-browser-91.8.0esr-11.5-1 in repository tor-browser.
commit 207037ff0a0f29a5c152e76649bc4024466be940 Author: Pier Angelo Vendrame pierov@torproject.org AuthorDate: Thu Feb 10 10:35:38 2022 +0100
squash! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
Bug 40774: Update about:preferences page to match new UI designs --- browser/components/preferences/preferences.js | 10 +- browser/components/preferences/preferences.xhtml | 5 +- .../torpreferences/content/bridgeQrDialog.jsm | 51 + .../torpreferences/content/bridgeQrDialog.xhtml | 23 + .../torpreferences/content/builtinBridgeDialog.jsm | 142 +++ .../content/builtinBridgeDialog.xhtml | 43 + ...gory.inc.xhtml => connectionCategory.inc.xhtml} | 8 +- .../torpreferences/content/connectionPane.js | 1315 ++++++++++++++++++++ .../torpreferences/content/connectionPane.xhtml | 177 +++ .../content/connectionSettingsDialog.jsm | 393 ++++++ .../content/connectionSettingsDialog.xhtml | 62 + .../components/torpreferences/content/network.svg | 6 + .../torpreferences/content/provideBridgeDialog.jsm | 69 + .../content/provideBridgeDialog.xhtml | 21 + .../torpreferences/content/requestBridgeDialog.jsm | 32 +- .../content/requestBridgeDialog.xhtml | 10 +- .../torpreferences/content/torLogDialog.jsm | 18 + .../components/torpreferences/content/torPane.js | 940 -------------- .../torpreferences/content/torPane.xhtml | 157 --- .../torpreferences/content/torPreferences.css | 394 +++++- browser/components/torpreferences/jar.mn | 15 +- 21 files changed, 2739 insertions(+), 1152 deletions(-)
diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js index ce338584142ed..5981bcd38fc83 100644 --- a/browser/components/preferences/preferences.js +++ b/browser/components/preferences/preferences.js @@ -13,7 +13,7 @@ /* import-globals-from findInPage.js */ /* import-globals-from ../../base/content/utilityOverlay.js */ /* import-globals-from ../../../toolkit/content/preferencesBindings.js */ -/* import-globals-from ../torpreferences/content/torPane.js */ +/* import-globals-from ../torpreferences/content/connectionPane.js */
"use strict";
@@ -137,12 +137,12 @@ function init_all() { register_module("paneSync", gSyncPane); } register_module("paneSearchResults", gSearchResultsPane); - if (gTorPane.enabled) { - document.getElementById("category-tor").hidden = false; - register_module("paneTor", gTorPane); + if (gConnectionPane.enabled) { + document.getElementById("category-connection").hidden = false; + register_module("paneConnection", gConnectionPane); } else { // Remove the pane from the DOM so it doesn't get incorrectly included in search results. - document.getElementById("template-paneTor").remove(); + document.getElementById("template-paneConnection").remove(); }
gSearchResultsPane.init(); diff --git a/browser/components/preferences/preferences.xhtml b/browser/components/preferences/preferences.xhtml index 07ab5cc7b626d..30ce70079adb7 100644 --- a/browser/components/preferences/preferences.xhtml +++ b/browser/components/preferences/preferences.xhtml @@ -6,6 +6,7 @@ <?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://global/skin/in-content/common.css"?> +<?xml-stylesheet href="chrome://global/skin/in-content/toggle-button.css"?> <?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> <?xml-stylesheet href="chrome://browser/content/preferences/dialogs/handlers.css"?> <?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?> @@ -158,7 +159,7 @@ <label class="category-name" flex="1" data-l10n-id="pane-experimental-title"></label> </richlistitem>
-#include ../torpreferences/content/torCategory.inc.xhtml +#include ../torpreferences/content/connectionCategory.inc.xhtml
</richlistbox>
@@ -213,7 +214,7 @@ #include containers.inc.xhtml #include sync.inc.xhtml #include experimental.inc.xhtml -#include ../torpreferences/content/torPane.xhtml +#include ../torpreferences/content/connectionPane.xhtml </vbox> </vbox> </vbox> diff --git a/browser/components/torpreferences/content/bridgeQrDialog.jsm b/browser/components/torpreferences/content/bridgeQrDialog.jsm new file mode 100644 index 0000000000000..e63347742ea50 --- /dev/null +++ b/browser/components/torpreferences/content/bridgeQrDialog.jsm @@ -0,0 +1,51 @@ +"use strict"; + +var EXPORTED_SYMBOLS = ["BridgeQrDialog"]; + +const { QRCode } = ChromeUtils.import("resource://gre/modules/QRCode.jsm"); + +const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); + +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) { + // Defer to later until Firefox has populated the dialog with all our elements + window.setTimeout(() => { + this._populateXUL(window, dialog); + }, 0); + } + + openDialog(gSubDialog, bridgeString) { + this._bridgeString = bridgeString; + gSubDialog.open( + "chrome://browser/content/torpreferences/bridgeQrDialog.xhtml", + { features: "resizable=yes" }, + this + ); + } +} diff --git a/browser/components/torpreferences/content/bridgeQrDialog.xhtml b/browser/components/torpreferences/content/bridgeQrDialog.xhtml new file mode 100644 index 0000000000000..2a49e4c0e7d9e --- /dev/null +++ b/browser/components/torpreferences/content/bridgeQrDialog.xhtml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> +<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?> + +<window type="child" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml%22%3E +<dialog id="bridgeQr-dialog" buttons="accept"> + <html:div id="bridgeQr-container"> + <html:div id="bridgeQr-target"/> + <html:div id="bridgeQr-onionBox"/> + <html:div id="bridgeQr-onion"/> + </html:div> + <script type="application/javascript"><![CDATA[ + "use strict"; + + let dialogObject = window.arguments[0]; + let dialogElement = document.getElementById("bridgeQr-dialog"); + dialogObject.init(window, dialogElement); + ]]></script> +</dialog> +</window> diff --git a/browser/components/torpreferences/content/builtinBridgeDialog.jsm b/browser/components/torpreferences/content/builtinBridgeDialog.jsm new file mode 100644 index 0000000000000..1d4dda8f5ca9c --- /dev/null +++ b/browser/components/torpreferences/content/builtinBridgeDialog.jsm @@ -0,0 +1,142 @@ +"use strict"; + +var EXPORTED_SYMBOLS = ["BuiltinBridgeDialog"]; + +const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); + +const { + TorSettings, + TorBridgeSource, + TorBuiltinBridgeTypes, +} = ChromeUtils.import("resource:///modules/TorSettings.jsm"); + +class BuiltinBridgeDialog { + constructor() { + this._dialog = null; + this._bridgeType = ""; + this._windowPadding = 0; + } + + static get selectors() { + return { + header: "#torPreferences-builtinBridge-header", + 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; + { + dialogWin.setAttribute("title", TorStrings.settings.builtinBridgeTitle); + let windowStyle = window.getComputedStyle(dialogWin); + this._windowPadding = + parseFloat(windowStyle.paddingLeft) + + parseFloat(windowStyle.paddingRight); + } + const initialWidth = dialogWin.clientWidth - this._windowPadding; + + this._dialog.querySelector(selectors.header).textContent = + TorStrings.settings.builtinBridgeHeader; + this._dialog.querySelector(selectors.description).textContent = + TorStrings.settings.builtinBridgeDescription; + let radioGroup = this._dialog.querySelector(selectors.radiogroup); + + let types = { + obfs4: { + elemRadio: this._dialog.querySelector(selectors.obfsRadio), + elemDescr: this._dialog.querySelector(selectors.obfsDescr), + label: TorStrings.settings.builtinBridgeObfs4, + descr: TorStrings.settings.builtinBridgeObfs4Description, + }, + snowflake: { + elemRadio: this._dialog.querySelector(selectors.snowflakeRadio), + elemDescr: this._dialog.querySelector(selectors.snowflakeDescr), + label: TorStrings.settings.builtinBridgeSnowflake, + descr: TorStrings.settings.builtinBridgeSnowflakeDescription, + }, + "meek-azure": { + elemRadio: this._dialog.querySelector(selectors.meekAzureRadio), + elemDescr: this._dialog.querySelector(selectors.meekAzureDescr), + label: TorStrings.settings.builtinBridgeMeekAzure, + descr: TorStrings.settings.builtinBridgeMeekAzureDescription, + }, + }; + + TorBuiltinBridgeTypes.forEach(type => { + types[type].elemRadio.parentElement.setAttribute("hidden", "false"); + types[type].elemDescr.parentElement.setAttribute("hidden", "false"); + types[type].elemRadio.setAttribute("label", types[type].label); + types[type].elemDescr.textContent = types[type].descr; + }); + + if ( + TorSettings.bridges.enabled && + TorSettings.bridges.source == TorBridgeSource.BuiltIn + ) { + radioGroup.selectedItem = + types[TorSettings.bridges.builtin_type]?.elemRadio; + this._bridgeType = TorSettings.bridges.builtin_type; + } else { + radioGroup.selectedItem = null; + this._bridgeType = ""; + } + + // Use the initial width, because the window is expanded when we add texts + this.resized(initialWidth); + + this._dialog.addEventListener("dialogaccept", e => { + this._bridgeType = radioGroup.value; + }); + this._dialog.addEventListener("dialoghelp", e => { + window.top.openTrustedLinkIn( + "https://tb-manual.torproject.org/circumvention/", + "tab" + ); + }); + } + + resized(width) { + if (this._dialog === null) { + return; + } + const dialogWin = this._dialog.parentElement; + if (width === undefined) { + width = dialogWin.clientWidth - this._windowPadding; + } + let windowPos = dialogWin.getBoundingClientRect(); + dialogWin.querySelectorAll("div").forEach(div => { + let divPos = div.getBoundingClientRect(); + div.style.width = width - (divPos.left - windowPos.left) + "px"; + }); + } + + init(window, aDialog) { + // defer to later until firefox has populated the dialog with all our elements + window.setTimeout(() => { + this._populateXUL(window, aDialog); + }, 0); + } + + openDialog(gSubDialog, aCloseCallback) { + gSubDialog.open( + "chrome://browser/content/torpreferences/builtinBridgeDialog.xhtml", + { + features: "resizable=yes", + closingCallback: () => { + aCloseCallback(this._bridgeType); + }, + }, + this + ); + } +} diff --git a/browser/components/torpreferences/content/builtinBridgeDialog.xhtml b/browser/components/torpreferences/content/builtinBridgeDialog.xhtml new file mode 100644 index 0000000000000..c1bf202ca1be5 --- /dev/null +++ b/browser/components/torpreferences/content/builtinBridgeDialog.xhtml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> +<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?> + +<window type="child" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml%22%3E +<dialog id="torPreferences-builtinBridge-dialog" + buttons="help,accept,cancel"> + <html:h3 id="torPreferences-builtinBridge-header">​</html:h3> + <description> + <html:div id="torPreferences-builtinBridge-description">​<br/>​</html:div> + </description> + <radiogroup id="torPreferences-builtinBridge-typeSelection"> + <hbox hidden="true"> + <radio id="torPreferences-builtinBridges-radioObfs" value="obfs4"/> + </hbox> + <hbox hidden="true" class="indent"> + <html:div id="torPreferences-builtinBridges-descrObfs"></html:div> + </hbox> + <hbox hidden="true"> + <radio id="torPreferences-builtinBridges-radioSnowflake" value="snowflake"/> + </hbox> + <hbox hidden="true" class="indent"> + <html:div id="torPreferences-builtinBridges-descrSnowflake"></html:div> + </hbox> + <hbox hidden="true"> + <radio id="torPreferences-builtinBridges-radioMeekAzure" value="meek-azure"/> + </hbox> + <hbox hidden="true" class="indent"> + <html:div id="torPreferences-builtinBridges-descrMeekAzure"></html:div> + </hbox> + </radiogroup> + <script type="application/javascript"><![CDATA[ + "use strict"; + + let builtinBridgeDialog = window.arguments[0]; + let dialog = document.getElementById("torPreferences-builtinBridge-dialog"); + builtinBridgeDialog.init(window, dialog); + ]]></script> +</dialog> +</window> diff --git a/browser/components/torpreferences/content/torCategory.inc.xhtml b/browser/components/torpreferences/content/connectionCategory.inc.xhtml similarity index 57% rename from browser/components/torpreferences/content/torCategory.inc.xhtml rename to browser/components/torpreferences/content/connectionCategory.inc.xhtml index abe56200f571b..15cf24cfe6950 100644 --- a/browser/components/torpreferences/content/torCategory.inc.xhtml +++ b/browser/components/torpreferences/content/connectionCategory.inc.xhtml @@ -1,9 +1,9 @@ -<richlistitem id="category-tor" +<richlistitem id="category-connection" class="category" - value="paneTor" - helpTopic="prefs-tor" + value="paneConnection" + helpTopic="prefs-connection" align="center" hidden="true"> <image class="category-icon"/> - <label id="torPreferences-labelCategory" class="category-name" flex="1" value="Tor"/> + <label id="torPreferences-labelCategory" class="category-name" flex="1" value="Connection"/> </richlistitem> diff --git a/browser/components/torpreferences/content/connectionPane.js b/browser/components/torpreferences/content/connectionPane.js new file mode 100644 index 0000000000000..309d6498a0c80 --- /dev/null +++ b/browser/components/torpreferences/content/connectionPane.js @@ -0,0 +1,1315 @@ +"use strict"; + +/* global Services, gSubDialog */ + +const { setTimeout, clearTimeout } = ChromeUtils.import( + "resource://gre/modules/Timer.jsm" +); + +const { + TorSettings, + TorSettingsTopics, + TorSettingsData, + TorBridgeSource, +} = ChromeUtils.import("resource:///modules/TorSettings.jsm"); + +const { TorProtocolService } = ChromeUtils.import( + "resource:///modules/TorProtocolService.jsm" +); + +const { + TorConnect, + TorConnectTopics, + TorConnectState, + TorCensorshipLevel, +} = ChromeUtils.import("resource:///modules/TorConnect.jsm"); + +const { TorLogDialog } = ChromeUtils.import( + "chrome://browser/content/torpreferences/torLogDialog.jsm" +); + +const { ConnectionSettingsDialog } = ChromeUtils.import( + "chrome://browser/content/torpreferences/connectionSettingsDialog.jsm" +); + +const { BridgeQrDialog } = ChromeUtils.import( + "chrome://browser/content/torpreferences/bridgeQrDialog.jsm" +); + +const { BuiltinBridgeDialog } = ChromeUtils.import( + "chrome://browser/content/torpreferences/builtinBridgeDialog.jsm" +); + +const { RequestBridgeDialog } = ChromeUtils.import( + "chrome://browser/content/torpreferences/requestBridgeDialog.jsm" +); + +const { ProvideBridgeDialog } = ChromeUtils.import( + "chrome://browser/content/torpreferences/provideBridgeDialog.jsm" +); + +const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm"); + +const { QRCode } = ChromeUtils.import("resource://gre/modules/QRCode.jsm"); + +ChromeUtils.defineModuleGetter( + this, + "TorStrings", + "resource:///modules/TorStrings.jsm" +); + +const InternetStatus = Object.freeze({ + Unknown: 0, + Online: 1, + Offline: -1, +}); + +/* + Connection Pane + + Code for populating the XUL in about:preferences#connection, handling input events, interfacing with tor-launcher +*/ +const gConnectionPane = (function() { + /* CSS selectors for all of the Tor Network DOM elements we need to access */ + const selectors = { + category: { + title: "label#torPreferences-labelCategory", + }, + messageBox: { + box: "div#torPreferences-connectMessageBox", + message: "td#torPreferences-connectMessageBox-message", + button: "button#torPreferences-connectMessageBox-button", + }, + torPreferences: { + header: "h1#torPreferences-header", + description: "span#torPreferences-description", + learnMore: "label#torPreferences-learnMore", + }, + status: { + internetLabel: "#torPreferences-status-internet-label", + internetTest: "#torPreferences-status-internet-test", + internetIcon: "#torPreferences-status-internet-statusIcon", + internetStatus: "#torPreferences-status-internet-status", + torLabel: "#torPreferences-status-tor-label", + torIcon: "#torPreferences-status-tor-statusIcon", + torStatus: "#torPreferences-status-tor-status", + }, + quickstart: { + header: "h2#torPreferences-quickstart-header", + description: "span#torPreferences-quickstart-description", + enableQuickstartCheckbox: "checkbox#torPreferences-quickstart-toggle", + }, + bridges: { + header: "h1#torPreferences-bridges-header", + description: "span#torPreferences-bridges-description", + learnMore: "label#torPreferences-bridges-learnMore", + locationGroup: "#torPreferences-bridges-locationGroup", + locationLabel: "#torPreferences-bridges-locationLabel", + location: "#torPreferences-bridges-location", + locationEntries: "#torPreferences-bridges-locationEntries", + chooseForMe: "#torPreferences-bridges-buttonChooseBridgeForMe", + currentHeader: "#torPreferences-currentBridges-header", + currentHeaderText: "#torPreferences-currentBridges-headerText", + switch: "#torPreferences-currentBridges-switch", + cards: "#torPreferences-currentBridges-cards", + cardTemplate: "#torPreferences-bridgeCard-template", + card: ".torPreferences-bridgeCard", + cardId: ".torPreferences-bridgeCard-id", + cardHeadingAddr: ".torPreferences-bridgeCard-headingAddr", + cardConnectedLabel: ".torPreferences-bridgeCard-connectedLabel", + cardOptions: ".torPreferences-bridgeCard-options", + cardMenu: "#torPreferences-bridgeCard-menu", + cardQrGrid: ".torPreferences-bridgeCard-grid", + cardQrContainer: ".torPreferences-bridgeCard-qr", + cardQr: ".torPreferences-bridgeCard-qrCode", + cardShare: ".torPreferences-bridgeCard-share", + cardAddr: ".torPreferences-bridgeCard-addr", + cardLearnMore: ".torPreferences-bridgeCard-learnMore", + cardCopy: ".torPreferences-bridgeCard-copyButton", + showAll: "#torPreferences-currentBridges-showAll", + removeAll: "#torPreferences-currentBridges-removeAll", + addHeader: "#torPreferences-addBridge-header", + addBuiltinLabel: "#torPreferences-addBridge-labelBuiltinBridge", + addBuiltinButton: "#torPreferences-addBridge-buttonBuiltinBridge", + requestLabel: "#torPreferences-addBridge-labelRequestBridge", + requestButton: "#torPreferences-addBridge-buttonRequestBridge", + enterLabel: "#torPreferences-addBridge-labelEnterBridge", + enterButton: "#torPreferences-addBridge-buttonEnterBridge", + }, + advanced: { + header: "h1#torPreferences-advanced-header", + label: "#torPreferences-advanced-label", + button: "#torPreferences-advanced-button", + torLogsLabel: "label#torPreferences-torLogs", + torLogsButton: "button#torPreferences-buttonTorLogs", + }, + }; /* selectors */ + + let retval = { + // cached frequently accessed DOM elements + _enableQuickstartCheckbox: null, + + _internetStatus: InternetStatus.Unknown, + + _controller: null, + + _currentBridge: "", + + // disables the provided list of elements + _setElementsDisabled(elements, disabled) { + for (let currentElement of elements) { + currentElement.disabled = disabled; + } + }, + + // populate xul with strings and cache the relevant elements + _populateXUL() { + // saves tor settings to disk when navigate away from about:preferences + window.addEventListener("blur", val => { + TorProtocolService.flushSettings(); + }); + + document + .querySelector(selectors.category.title) + .setAttribute("value", TorStrings.settings.categoryTitle); + + let prefpane = document.getElementById("mainPrefPane"); + + // 'Connect to Tor' Message Bar + + const messageBox = prefpane.querySelector(selectors.messageBox.box); + const messageBoxMessage = prefpane.querySelector( + selectors.messageBox.message + ); + const messageBoxButton = prefpane.querySelector( + selectors.messageBox.button + ); + // wire up connect button + messageBoxButton.addEventListener("click", () => { + TorConnect.beginBootstrap(); + TorConnect.openTorConnect(); + }); + + this._populateMessagebox = () => { + if ( + TorConnect.shouldShowTorConnect && + TorConnect.state === TorConnectState.Configuring + ) { + // set messagebox style and text + if (TorProtocolService.torBootstrapErrorOccurred()) { + messageBox.parentNode.style.display = null; + messageBox.className = "error"; + messageBoxMessage.innerText = TorStrings.torConnect.tryAgainMessage; + messageBoxButton.innerText = TorStrings.torConnect.tryAgain; + } else { + messageBox.parentNode.style.display = null; + messageBox.className = "warning"; + messageBoxMessage.innerText = TorStrings.torConnect.connectMessage; + messageBoxButton.innerText = TorStrings.torConnect.torConnectButton; + } + } else { + // we need to explicitly hide the groupbox, as switching between + // the tor pane and other panes will 'unhide' (via the 'hidden' + // attribute) the groupbox, offsetting all of the content down + // by the groupbox's margin (even if content is 0 height) + messageBox.parentNode.style.display = "none"; + messageBox.className = "hidden"; + messageBoxMessage.innerText = ""; + messageBoxButton.innerText = ""; + } + }; + this._populateMessagebox(); + + // Heading + prefpane.querySelector(selectors.torPreferences.header).innerText = + TorStrings.settings.categoryTitle; + prefpane.querySelector(selectors.torPreferences.description).textContent = + TorStrings.settings.torPreferencesDescription; + { + let learnMore = prefpane.querySelector( + selectors.torPreferences.learnMore + ); + learnMore.setAttribute("value", TorStrings.settings.learnMore); + learnMore.setAttribute( + "href", + TorStrings.settings.learnMoreTorBrowserURL + ); + } + + // Internet and Tor status + prefpane.querySelector(selectors.status.internetLabel).textContent = + TorStrings.settings.statusInternetLabel; + prefpane.querySelector(selectors.status.torLabel).textContent = + TorStrings.settings.statusTorLabel; + const internetTest = prefpane.querySelector( + selectors.status.internetTest + ); + internetTest.setAttribute( + "label", + TorStrings.settings.statusInternetTest + ); + internetTest.addEventListener("command", async () => { + this.onInternetTest(); + }); + const internetIcon = prefpane.querySelector( + selectors.status.internetIcon + ); + const internetStatus = prefpane.querySelector( + selectors.status.internetStatus + ); + const torIcon = prefpane.querySelector(selectors.status.torIcon); + const torStatus = prefpane.querySelector(selectors.status.torStatus); + this._populateStatus = () => { + switch (this._internetStatus) { + case InternetStatus.Unknown: + internetTest.removeAttribute("hidden"); + break; + case InternetStatus.Online: + internetTest.setAttribute("hidden", "true"); + internetIcon.className = "online"; + internetStatus.textContent = + TorStrings.settings.statusInternetOnline; + break; + case InternetStatus.Offline: + internetTest.setAttribute("hidden", "true"); + internetIcon.className = "offline"; + internetStatus.textContent = + TorStrings.settings.statusInternetOffline; + break; + } + if (TorConnect.state === TorConnectState.Bootstrapped) { + torIcon.className = "connected"; + torStatus.textContent = TorStrings.settings.statusTorConnected; + } else if ( + TorConnect.detectedCensorshipLevel > TorCensorshipLevel.None + ) { + torIcon.className = "blocked"; + torStatus.textContent = TorStrings.settings.statusTorBlocked; + } else { + torIcon.className = ""; + torStatus.textContent = TorStrings.settings.statusTorNotConnected; + } + }; + this._populateStatus(); + + // Quickstart + prefpane.querySelector(selectors.quickstart.header).innerText = + TorStrings.settings.quickstartHeading; + prefpane.querySelector(selectors.quickstart.description).textContent = + TorStrings.settings.quickstartDescription; + + this._enableQuickstartCheckbox = prefpane.querySelector( + selectors.quickstart.enableQuickstartCheckbox + ); + this._enableQuickstartCheckbox.setAttribute( + "label", + TorStrings.settings.quickstartCheckbox + ); + this._enableQuickstartCheckbox.addEventListener("command", e => { + const checked = this._enableQuickstartCheckbox.checked; + TorSettings.quickstart.enabled = checked; + TorSettings.saveToPrefs().applySettings(); + }); + this._enableQuickstartCheckbox.checked = TorSettings.quickstart.enabled; + Services.obs.addObserver(this, TorSettingsTopics.SettingChanged); + + // Bridge setup + prefpane.querySelector(selectors.bridges.header).innerText = + TorStrings.settings.bridgesHeading; + prefpane.querySelector(selectors.bridges.description).textContent = + TorStrings.settings.bridgesDescription; + { + let learnMore = prefpane.querySelector(selectors.bridges.learnMore); + learnMore.setAttribute("value", TorStrings.settings.learnMore); + learnMore.setAttribute("href", TorStrings.settings.learnMoreBridgesURL); + } + + // Location + { + const locationGroup = prefpane.querySelector( + selectors.bridges.locationGroup + ); + prefpane.querySelector(selectors.bridges.locationLabel).textContent = + TorStrings.settings.bridgeLocation; + const location = prefpane.querySelector(selectors.bridges.location); + const locationEntries = prefpane.querySelector( + selectors.bridges.locationEntries + ); + const chooseForMe = prefpane.querySelector( + selectors.bridges.chooseForMe + ); + chooseForMe.setAttribute( + "label", + TorStrings.settings.bridgeChooseForMe + ); + chooseForMe.addEventListener("command", e => { + TorConnect.beginAutoBootstrap(location.value); + }); + this._populateLocations = () => { + let value = location.value; + locationEntries.textContent = ""; + + { + const item = document.createXULElement("menuitem"); + item.setAttribute("value", ""); + item.setAttribute( + "label", + TorStrings.settings.bridgeLocationAutomatic + ); + locationEntries.appendChild(item); + } + + const codes = TorConnect.countryCodes; + const items = codes.map(code => { + const item = document.createXULElement("menuitem"); + item.setAttribute("value", code); + item.setAttribute( + "label", + TorConnect.countryNames[code] + ? TorConnect.countryNames[code] + : code + ); + return item; + }); + items.sort((left, right) => + left.textContent.localeCompare(right.textContent) + ); + locationEntries.append(...items); + location.value = value; + }; + this._showAutoconfiguration = () => { + if ( + !TorConnect.shouldShowTorConnect || + !TorProtocolService.torBootstrapErrorOccurred() + ) { + locationGroup.setAttribute("hidden", "true"); + return; + } + // Populate locations, even though we will show only the automatic + // item for a moment. In my opinion showing the button immediately is + // better then waiting for the Moat query to finish (after a while) + // and showing the controls only after that. + this._populateLocations(); + locationGroup.removeAttribute("hidden"); + if (!TorConnect.countryCodes.length) { + TorConnect.getCountryCodes().then(() => this._populateLocations()); + } + }; + this._showAutoconfiguration(); + } + + // Bridge cards + const bridgeHeader = prefpane.querySelector( + selectors.bridges.currentHeader + ); + bridgeHeader.querySelector( + selectors.bridges.currentHeaderText + ).textContent = TorStrings.settings.bridgeCurrent; + const bridgeSwitch = bridgeHeader.querySelector(selectors.bridges.switch); + bridgeSwitch.addEventListener("change", () => { + TorSettings.bridges.enabled = bridgeSwitch.checked; + TorSettings.saveToPrefs(); + TorSettings.applySettings().then(result => { + this._populateBridgeCards(); + }); + }); + const bridgeTemplate = prefpane.querySelector( + selectors.bridges.cardTemplate + ); + { + const learnMore = bridgeTemplate.querySelector( + selectors.bridges.cardLearnMore + ); + learnMore.setAttribute("value", TorStrings.settings.learnMore); + learnMore.setAttribute("href", "about:blank"); + } + bridgeTemplate.querySelector( + selectors.bridges.cardConnectedLabel + ).textContent = TorStrings.settings.statusTorConnected; + bridgeTemplate + .querySelector(selectors.bridges.cardCopy) + .setAttribute("label", TorStrings.settings.bridgeCopy); + bridgeTemplate.querySelector(selectors.bridges.cardShare).textContent = + TorStrings.settings.bridgeShare; + const bridgeCards = prefpane.querySelector(selectors.bridges.cards); + const bridgeMenu = prefpane.querySelector(selectors.bridges.cardMenu); + + this._addBridgeCard = bridgeString => { + const card = bridgeTemplate.cloneNode(true); + card.removeAttribute("id"); + const grid = card.querySelector(selectors.bridges.cardQrGrid); + card.addEventListener("click", e => { + let target = e.target; + let apply = true; + while (target !== null && target !== card && apply) { + // Deal with mixture of "command" and "click" events + apply = !target.classList?.contains("stop-click"); + target = target.parentElement; + } + if (apply) { + if (card.classList.toggle("expanded")) { + grid.classList.add("to-animate"); + grid.style.height = `${grid.scrollHeight}px`; + } else { + // Be sure we still have the to-animate class + grid.classList.add("to-animate"); + grid.style.height = ""; + } + } + }); + const emojis = makeBridgeId(bridgeString).map(e => { + const span = document.createElement("span"); + span.className = "emoji"; + span.textContent = e; + return span; + }); + const idString = TorStrings.settings.bridgeId; + const id = card.querySelector(selectors.bridges.cardId); + const details = parseBridgeLine(bridgeString); + if (details && details.id !== undefined) { + card.setAttribute("data-bridge-id", details.id); + } + // TODO: properly handle "vanilla" bridges? + const type = + details && details.transport !== undefined + ? details.transport + : "vanilla"; + for (const piece of idString.split(/(#[12])/)) { + if (piece == "#1") { + id.append(type); + } else if (piece == "#2") { + id.append(...emojis); + } else { + id.append(piece); + } + } + card.querySelector( + selectors.bridges.cardHeadingAddr + ).textContent = bridgeString; + const optionsButton = card.querySelector(selectors.bridges.cardOptions); + if (TorSettings.bridges.source === TorBridgeSource.BuiltIn) { + optionsButton.setAttribute("hidden", "true"); + } else { + // Cloning the menupopup element does not work as expected. + // Therefore, we use only one, and just before opening it, we remove + // its previous items, and add the ones relative to the bridge whose + // button has been pressed. + optionsButton.addEventListener("click", () => { + const menuItem = document.createXULElement("menuitem"); + menuItem.setAttribute("label", TorStrings.settings.remove); + menuItem.classList.add("menuitem-iconic"); + menuItem.image = "chrome://global/skin/icons/delete.svg"; + menuItem.addEventListener("command", e => { + const strings = TorSettings.bridges.bridge_strings; + const index = strings.indexOf(bridgeString); + if (index !== -1) { + strings.splice(index, 1); + } + TorSettings.bridges.enabled = + bridgeSwitch.checked && !!strings.length; + TorSettings.bridges.bridge_strings = strings.join("\n"); + TorSettings.saveToPrefs(); + TorSettings.applySettings().then(result => { + this._populateBridgeCards(); + }); + }); + if (bridgeMenu.firstChild) { + bridgeMenu.firstChild.remove(); + } + bridgeMenu.append(menuItem); + bridgeMenu.openPopup(optionsButton, { + position: "bottomleft topleft", + }); + }); + } + const bridgeAddr = card.querySelector(selectors.bridges.cardAddr); + bridgeAddr.setAttribute("value", bridgeString); + const bridgeCopy = card.querySelector(selectors.bridges.cardCopy); + let restoreTimeout = null; + bridgeCopy.addEventListener("command", e => { + this.onCopyBridgeAddress(bridgeAddr); + const label = bridgeCopy.querySelector("label"); + label.setAttribute("value", TorStrings.settings.copied); + bridgeCopy.classList.add("primary"); + + const RESTORE_TIME = 1200; + if (restoreTimeout !== null) { + clearTimeout(restoreTimeout); + } + restoreTimeout = setTimeout(() => { + label.setAttribute("value", TorStrings.settings.bridgeCopy); + bridgeCopy.classList.remove("primary"); + restoreTimeout = null; + }, RESTORE_TIME); + }); + if (details && details.id === this._currentBridge) { + card.classList.add("currently-connected"); + bridgeCards.prepend(card); + } else { + bridgeCards.append(card); + } + // Add the QR only after appending the card, to have the computed style + try { + const container = card.querySelector(selectors.bridges.cardQr); + const style = getComputedStyle(container); + const width = style.width.substr(0, style.width.length - 2); + const height = style.height.substr(0, style.height.length - 2); + new QRCode(container, { + text: bridgeString, + width, + height, + colorDark: style.color, + colorLight: style.backgroundColor, + document, + }); + container.parentElement.addEventListener("click", () => { + this.onShowQr(bridgeString); + }); + } catch (err) { + // TODO: Add a generic image in case of errors such as code overflow. + // It should never happen with correct codes, but after all this + // content can be generated by users... + console.error("Could not generate the QR code for the bridge:", err); + } + }; + this._checkBridgeCardsHeight = () => { + for (const card of bridgeCards.children) { + // Expanded cards have the height set manually to their details for + // the CSS animation. However, when resizing the window, we may need + // to adjust their height. + if (card.classList.contains("expanded")) { + const grid = card.querySelector(selectors.bridges.cardQrGrid); + // Reset it first, to avoid having a height that is higher than + // strictly needed. Also, remove the to-animate class, because the + // animation interferes with this process! + grid.classList.remove("to-animate"); + grid.style.height = ""; + grid.style.height = `${grid.scrollHeight}px`; + } + } + }; + this._currentBridgesExpanded = false; + const showAll = prefpane.querySelector(selectors.bridges.showAll); + showAll.setAttribute("label", TorStrings.settings.bridgeShowAll); + showAll.addEventListener("command", () => { + this._currentBridgesExpanded = true; + this._populateBridgeCards(); + }); + const removeAll = prefpane.querySelector(selectors.bridges.removeAll); + removeAll.setAttribute("label", TorStrings.settings.bridgeRemoveAll); + removeAll.addEventListener("command", () => { + this.onRemoveAllBridges(); + }); + this._populateBridgeCards = async () => { + const collapseThreshold = 4; + + let newStrings = new Set(TorSettings.bridges.bridge_strings); + const numBridges = newStrings.size; + if (!newStrings.size) { + bridgeHeader.setAttribute("hidden", "true"); + bridgeCards.setAttribute("hidden", "true"); + showAll.setAttribute("hidden", "true"); + removeAll.setAttribute("hidden", "true"); + bridgeCards.textContent = ""; + return; + } + bridgeHeader.removeAttribute("hidden"); + bridgeCards.removeAttribute("hidden"); + bridgeSwitch.checked = TorSettings.bridges.enabled; + bridgeCards.classList.toggle("disabled", !TorSettings.bridges.enabled); + + let shownCards = 0; + const toShow = this._currentBridgesExpanded + ? numBridges + : collapseThreshold; + + // Do not remove all the old cards, because it makes scrollbar "jump" + const currentCards = bridgeCards.querySelectorAll( + selectors.bridges.card + ); + for (const card of currentCards) { + const string = card.querySelector(selectors.bridges.cardAddr).value; + const hadString = newStrings.delete(string); + if (!hadString || shownCards == toShow) { + card.remove(); + } else { + shownCards++; + } + } + + // Add only the new strings that remained in the set + for (const bridge of newStrings) { + if (shownCards >= toShow) { + if (this._currentBridge === "") { + break; + } else if (!bridge.includes(this._currentBridge)) { + continue; + } + } + this._addBridgeCard(bridge); + shownCards++; + } + + // If we know the connected bridge, we may have added more than the ones + // we should actually show (but the connected ones have been prepended, + // if needed). So, remove any exceeding ones. + while (shownCards > toShow) { + bridgeCards.lastElementChild.remove(); + shownCards--; + } + + // And finally update the buttons + if (numBridges > collapseThreshold && !this._currentBridgesExpanded) { + showAll.removeAttribute("hidden"); + if (TorSettings.bridges.enabled) { + showAll.classList.add("primary"); + } else { + showAll.classList.remove("primary"); + } + removeAll.setAttribute("hidden", "true"); + if (TorSettings.bridges.enabled) { + // We do not want both collapsed and disabled at the same time, + // because we use collapsed only to display a gradient on the list. + bridgeCards.classList.add("list-collapsed"); + } + } else { + showAll.setAttribute("hidden", "true"); + removeAll.removeAttribute("hidden"); + bridgeCards.classList.remove("list-collapsed"); + } + }; + this._populateBridgeCards(); + this._updateConnectedBridges = () => { + for (const card of bridgeCards.querySelectorAll( + ".currently-connected" + )) { + card.classList.remove("currently-connected"); + } + if (this._currentBridge === "") { + return; + } + // Make sure we have the connected bridge in the list + this._populateBridgeCards(); + // At the moment, IDs do not have to be unique (and it is a concrete + // 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}"]` + ); + for (const card of cards) { + card.classList.add("currently-connected"); + } + const placeholder = document.createElement("span"); + bridgeCards.prepend(placeholder); + placeholder.replaceWith(...cards); + }; + try { + const { controller } = ChromeUtils.import( + "resource://torbutton/modules/tor-control-port.js", + {} + ); + // Avoid the cache because we set our custom event watcher, and at the + // moment, watchers cannot be removed from a controller. + controller(true).then(aController => { + this._controller = aController; + // Getting the circuits may be enough, if we have bootstrapped for a + // while, but at the beginning it gives many bridges as connected, + // because tor pokes all the bridges to find the best one. + // Also, watching circuit events does not work, at the moment, but in + // any case, checking the stream has the advantage that we can see if + // it really used for a connection, rather than tor having created + // 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. + this._controller.watchEvent( + "STREAM", + event => + event.StreamStatus === "SUCCEEDED" && "SOCKS_USERNAME" in event, + async event => { + const circuitStatuses = await this._controller.getInfo( + "circuit-status" + ); + if (!circuitStatuses) { + return; + } + 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].substr(1); + if (bridgeId !== this._currentBridge) { + this._currentBridge = bridgeId; + this._updateConnectedBridges(); + } + break; + } + } + } + ); + }); + } catch (err) { + console.warn( + "We could not load torbutton, bridge statuses will not be updated", + err + ); + } + + // Add a new bridge + prefpane.querySelector(selectors.bridges.addHeader).textContent = + TorStrings.settings.bridgeAdd; + prefpane + .querySelector(selectors.bridges.addBuiltinLabel) + .setAttribute("value", TorStrings.settings.bridgeSelectBrowserBuiltin); + { + let button = prefpane.querySelector(selectors.bridges.addBuiltinButton); + button.setAttribute("label", TorStrings.settings.bridgeSelectBuiltin); + button.addEventListener("command", e => { + this.onAddBuiltinBridge(); + }); + } + prefpane + .querySelector(selectors.bridges.requestLabel) + .setAttribute("value", TorStrings.settings.bridgeRequestFromTorProject); + { + let button = prefpane.querySelector(selectors.bridges.requestButton); + button.setAttribute("label", TorStrings.settings.bridgeRequest); + button.addEventListener("command", e => { + this.onRequestBridge(); + }); + } + prefpane + .querySelector(selectors.bridges.enterLabel) + .setAttribute("value", TorStrings.settings.bridgeEnterKnown); + { + const button = prefpane.querySelector(selectors.bridges.enterButton); + button.setAttribute("label", TorStrings.settings.bridgeAddManually); + button.addEventListener("command", e => { + this.onAddBridgeManually(); + }); + } + + Services.obs.addObserver(this, TorConnectTopics.StateChange); + + // Advanced setup + prefpane.querySelector(selectors.advanced.header).innerText = + TorStrings.settings.advancedHeading; + prefpane.querySelector(selectors.advanced.label).textContent = + TorStrings.settings.advancedLabel; + { + let settingsButton = prefpane.querySelector(selectors.advanced.button); + settingsButton.setAttribute( + "label", + TorStrings.settings.advancedButton + ); + settingsButton.addEventListener("command", () => { + this.onAdvancedSettings(); + }); + } + + // Tor logs + prefpane + .querySelector(selectors.advanced.torLogsLabel) + .setAttribute("value", TorStrings.settings.showTorDaemonLogs); + let torLogsButton = prefpane.querySelector( + selectors.advanced.torLogsButton + ); + torLogsButton.setAttribute("label", TorStrings.settings.showLogs); + torLogsButton.addEventListener("command", () => { + this.onViewTorLogs(); + }); + }, + + init() { + this._populateXUL(); + + let onUnload = () => { + window.removeEventListener("unload", onUnload); + gConnectionPane.uninit(); + }; + window.addEventListener("unload", onUnload); + + window.addEventListener("resize", () => { + this._checkBridgeCardsHeight(); + }); + }, + + uninit() { + // unregister our observer topics + Services.obs.removeObserver(this, TorSettingsTopics.SettingChanged); + Services.obs.removeObserver(this, TorConnectTopics.StateChange); + + if (this._controller !== null) { + this._controller.close(); + this._controller = null; + } + }, + + // whether the page should be present in about:preferences + get enabled() { + return TorProtocolService.ownsTorDaemon; + }, + + // + // Callbacks + // + + observe(subject, topic, data) { + switch (topic) { + // triggered when a TorSettings param has changed + case TorSettingsTopics.SettingChanged: { + let obj = subject?.wrappedJSObject; + switch (data) { + case TorSettingsData.QuickStartEnabled: { + this._enableQuickstartCheckbox.checked = obj.value; + break; + } + } + break; + } + // triggered when tor connect state changes and we may + // need to update the messagebox + case TorConnectTopics.StateChange: { + this.onStateChange(); + break; + } + } + }, + + async onInternetTest() { + const mrpc = new MoatRPC(); + let status = null; + try { + await mrpc.init(); + status = await mrpc.testInternetConnection(); + } catch (err) { + console.log("Error while checking the Internet connection", err); + } finally { + mrpc.uninit(); + } + if (status) { + this._internetStatus = status.successful + ? InternetStatus.Online + : InternetStatus.Offline; + this._populateStatus(); + } + }, + + onStateChange() { + this._populateMessagebox(); + this._populateStatus(); + this._showAutoconfiguration(); + this._populateBridgeCards(); + }, + + onShowQr(bridgeString) { + const dialog = new BridgeQrDialog(); + dialog.openDialog(gSubDialog, bridgeString); + }, + + onCopyBridgeAddress(addressElem) { + let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( + Ci.nsIClipboardHelper + ); + clipboard.copyString(addressElem.value); + }, + + onRemoveAllBridges() { + TorSettings.bridges.enabled = false; + TorSettings.bridges.bridge_strings = ""; + TorSettings.saveToPrefs(); + TorSettings.applySettings().then(result => { + this._populateBridgeCards(); + }); + }, + + onAddBuiltinBridge() { + let builtinBridgeDialog = new BuiltinBridgeDialog(); + + let sizeObserver = null; + { + let ds = document.querySelector("#dialogStack"); + let boxObserver; + boxObserver = new MutationObserver(() => { + let dialogBox = document.querySelector(".dialogBox"); + if (dialogBox) { + sizeObserver = new MutationObserver(mutations => { + for (const m of mutations) { + if (m.attributeName === "style") { + builtinBridgeDialog.resized(); + break; + } + } + }); + sizeObserver.observe(dialogBox, { attributes: true }); + boxObserver.disconnect(); + } + }); + boxObserver.observe(ds, { childList: true, subtree: true }); + } + + builtinBridgeDialog.openDialog(gSubDialog, aBridgeType => { + sizeObserver.disconnect(); + + if (!aBridgeType) { + TorSettings.bridges.enabled = false; + TorSettings.bridges.builtin_type = ""; + } else { + TorSettings.bridges.enabled = true; + TorSettings.bridges.source = TorBridgeSource.BuiltIn; + TorSettings.bridges.builtin_type = aBridgeType; + } + TorSettings.saveToPrefs(); + TorSettings.applySettings().then(result => { + this._populateBridgeCards(); + }); + }); + }, + + // called when the request bridge button is activated + onRequestBridge() { + let requestBridgeDialog = new RequestBridgeDialog(); + requestBridgeDialog.openDialog(gSubDialog, aBridges => { + if (aBridges.length) { + let bridgeStrings = aBridges.join("\n"); + TorSettings.bridges.enabled = true; + TorSettings.bridges.source = TorBridgeSource.BridgeDB; + TorSettings.bridges.bridge_strings = bridgeStrings; + TorSettings.saveToPrefs(); + TorSettings.applySettings().then(result => { + this._populateBridgeCards(); + }); + } else { + TorSettings.bridges.enabled = false; + } + }); + }, + + onAddBridgeManually() { + let provideBridgeDialog = new ProvideBridgeDialog(); + provideBridgeDialog.openDialog(gSubDialog, aBridgeString => { + if (aBridgeString.length) { + TorSettings.bridges.enabled = true; + TorSettings.bridges.source = TorBridgeSource.UserProvided; + TorSettings.bridges.bridge_strings = aBridgeString; + TorSettings.saveToPrefs(); + TorSettings.applySettings().then(result => { + this._populateBridgeCards(); + }); + } else { + TorSettings.bridges.enabled = false; + TorSettings.bridges.source = TorBridgeSource.Invalid; + } + }); + }, + + onAdvancedSettings() { + let connectionSettingsDialog = new ConnectionSettingsDialog(); + connectionSettingsDialog.openDialog(gSubDialog); + }, + + onViewTorLogs() { + let torLogDialog = new TorLogDialog(); + torLogDialog.openDialog(gSubDialog); + }, + }; + return retval; +})(); /* gConnectionPane */ + +function makeBridgeId(bridgeString) { + // JS uses UTF-16. While most of these emojis are surrogate pairs, a few + // ones fit one UTF-16 character. So we could not use neither indices, + // nor substr, nor some function to split the string. + const emojis = [ + "😄️", + "😒️", + "😉", + "😭️", + "😂️", + "😎️", + "🤩️", + "😘", + "😜️", + "😏️", + "😷", + "🤢", + "🤕", + "🤧", + "🥵", + "🥶", + "🥴", + "😵️", + "🤮️", + "🤑", + "🤔", + "🫢", + "🤐", + "😮💨", + "😐", + "🤤", + "😴", + "🤯", + "🤠", + "🥳", + "🥸", + "🤓", + "🧐", + "😨", + "😳", + "🥺", + "🤬", + "😈", + "👿", + "💀", + "💩", + "🤡", + "👺", + "👻", + "👽", + "🦴", + "🤖", + "😸", + "🙈", + "🙉", + "🙊", + "💋", + "💖", + "💯", + "💢", + "💧", + "💨", + "💭", + "💤", + "👋", + "👌", + "✌", + "👍", + "👎", + "🤛", + "🙌", + "💪", + "🙏", + "✍", + "🧠", + "👀", + "👂", + "👅", + "🦷", + "🐾", + "🐶", + "🦊", + "🦝", + "🐈", + "🦁", + "🐯", + "🐴", + "🦄", + "🦓", + "🐮", + "🐷", + "🐑", + "🐪", + "🐘", + "🐭", + "🐰", + "🦔", + "🦇", + "🐻", + "🐨", + "🐼", + "🐔", + "🦨", + "🦘", + "🐦", + "🐧", + "🦩", + "🦉", + "🦜", + "🪶", + "🐸", + "🐊", + "🐢", + "🦎", + "🐍", + "🦖", + "🦀", + "🐬", + "🐙", + "🐌", + "🐝", + "🐞", + "🌸", + "🌲", + "🌵", + "🍀", + "🍁", + "🍇", + "🍉", + "🍊", + "🍋", + "🍌", + "🍍", + "🍎", + "🥥", + "🍐", + "🍒", + "🍓", + "🫐", + "🥝", + "🥔", + "🥕", + "🧅", + "🌰", + "🍄", + "🍞", + "🥞", + "🧀", + "🍖", + "🍔", + "🍟", + "🍕", + "🥚", + "🍿", + "🧂", + "🍙", + "🍦", + "🍩", + "🍪", + "🎂", + "🍬", + "🍭", + "🥛", + "☕", + "🫖", + "🍾", + "🍷", + "🍹", + "🍺", + "🍴", + "🥄", + "🫙", + "🧭", + "🌋", + "🪵", + "🏡", + "🏢", + "🏰", + "⛲", + "⛺", + "🎡", + "🚂", + "🚘", + "🚜", + "🚲", + "🚔", + "🚨", + "⛽", + "🚥", + "🚧", + "⚓", + "⛵", + "🛟", + "🪂", + "🚀", + "⌛", + "⏰", + "🌂", + "🌞", + "🌙", + "🌟", + "⛅", + "⚡", + "🔥", + "🌊", + "🎃", + "🎈", + "🎉", + "✨", + "🎀", + "🎁", + "🏆", + "🏅", + "🔮", + "🪄", + "🎾", + "🎳", + "🎲", + "🎭", + "🎨", + "🧵", + "🎩", + "📢", + "🔔", + "🎵", + "🎤", + "🎧", + "🎷", + "🎸", + "🥁", + "🔋", + "🔌", + "💻", + "💾", + "💿", + "🎬", + "📺", + "📷", + "🎮", + "🧩", + "🔍", + "💡", + "📖", + "💰", + "💼", + "📈", + "📌", + "📎", + "🔒", + "🔑", + "🔧", + "🪛", + "🔩", + "🧲", + "🔬", + "🔭", + "📡", + "🚪", + "🪑", + "⛔", + "🚩", + ]; + + // FNV-1a implementation that is compatible with other languages + const prime = 0x01000193; + const offset = 0x811c9dc5; + let hash = offset; + const encoder = new TextEncoder(); + for (const charCode of encoder.encode(bridgeString)) { + hash = Math.imul(hash ^ charCode, prime); + } + + const hashBytes = [ + ((hash & 0x7f000000) >> 24) | (hash < 0 ? 0x80 : 0), + (hash & 0x00ff0000) >> 16, + (hash & 0x0000ff00) >> 8, + hash & 0x000000ff, + ]; + return hashBytes.map(b => emojis[b]); +} + +function parseBridgeLine(line) { + const re = /^([^\s]+\s+)?([0-9a-fA-F.[]:]+:[0-9]{1,5})\s*([0-9a-fA-F]{40})(\s+.+)?/; + const matches = line.match(re); + if (!matches) { + return null; + } + let bridge = { addr: matches[2] }; + if (matches[1] !== undefined) { + bridge.transport = matches[1].trim(); + } + if (matches[3] !== undefined) { + bridge.id = matches[3].toUpperCase(); + } + if (matches[4] !== undefined) { + bridge.args = matches[4].trim(); + } + return bridge; +} diff --git a/browser/components/torpreferences/content/connectionPane.xhtml b/browser/components/torpreferences/content/connectionPane.xhtml new file mode 100644 index 0000000000000..67f98685d8038 --- /dev/null +++ b/browser/components/torpreferences/content/connectionPane.xhtml @@ -0,0 +1,177 @@ +<!-- Tor panel --> + +<script type="application/javascript" + src="chrome://browser/content/torpreferences/connectionPane.js"/> +<html:template id="template-paneConnection"> + +<!-- Tor Connect Message Box --> +<groupbox data-category="paneConnection" hidden="true"> + <html:div id="torPreferences-connectMessageBox" + class="subcategory" + data-category="paneConnection" + hidden="true"> + html:table + html:tr + html:td + <html:div id="torPreferences-connectMessageBox-icon"/> + </html:td> + <html:td id="torPreferences-connectMessageBox-message"> + </html:td> + html:td + <html:button id="torPreferences-connectMessageBox-button"> + </html:button> + </html:td> + </html:tr> + </html:table> + </html:div> +</groupbox> + +<hbox id="torPreferencesCategory" + class="subcategory" + data-category="paneConnection" + hidden="true"> + <html:h1 id="torPreferences-header"/> +</hbox> + +<groupbox data-category="paneConnection" + hidden="true"> + <description flex="1"> + <html:span id="torPreferences-description" class="tail-with-learn-more"/> + <label id="torPreferences-learnMore" class="learnMore text-link" is="text-link"/> + </description> +</groupbox> + +<groupbox id="torPreferences-status-group" + data-category="paneConnection"> + <hbox id="torPreferences-status-box"> + <image id="torPreferences-status-internet-icon"/> + <html:span id="torPreferences-status-internet-label"/> + <button id="torPreferences-status-internet-test"/> + <image id="torPreferences-status-internet-statusIcon"/> + <html:span id="torPreferences-status-internet-status"/> + <image id="torPreferences-status-tor-icon"/> + <html:span id="torPreferences-status-tor-label"/> + <image id="torPreferences-status-tor-statusIcon"/> + <html:span id="torPreferences-status-tor-status"/> + </hbox> +</groupbox> + +<!-- Quickstart --> +<groupbox id="torPreferences-quickstart-group" + data-category="paneConnection" + hidden="true"> + <html:h2 id="torPreferences-quickstart-header"/> + <description flex="1"> + <html:span id="torPreferences-quickstart-description"/> + </description> + <checkbox id="torPreferences-quickstart-toggle"/> +</groupbox> + +<!-- Bridges --> +<hbox class="subcategory" + data-category="paneConnection" + hidden="true"> + <html:h1 id="torPreferences-bridges-header"/> +</hbox> +<groupbox id="torPreferences-bridges-group" + data-category="paneConnection" + hidden="true"> + <description flex="1"> + <html:span id="torPreferences-bridges-description" class="tail-with-learn-more"/> + <label id="torPreferences-bridges-learnMore" class="learnMore text-link" is="text-link"/> + </description> + <hbox align="center" id="torPreferences-bridges-locationGroup" hidden="true"> + <label id="torPreferences-bridges-locationLabel" + control="torPreferences-bridges-location"/> + <spacer flex="1"/> + <menulist id="torPreferences-bridges-location"> + <menupopup id="torPreferences-bridges-locationEntries"/> + </menulist> + <button id="torPreferences-bridges-buttonChooseBridgeForMe" class="torMarginFix primary"/> + </hbox> + <html:h2 id="torPreferences-currentBridges-header"> + <html:span id="torPreferences-currentBridges-headerText"/> + <html:input type="checkbox" id="torPreferences-currentBridges-switch" class="toggle-button"/> + </html:h2> + <menupopup id="torPreferences-bridgeCard-menu"/> + <vbox id="torPreferences-bridgeCard-template" class="torPreferences-bridgeCard"> + <hbox class="torPreferences-bridgeCard-heading"> + <html:div class="torPreferences-bridgeCard-id"/> + <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> + <html:button class="torPreferences-bridgeCard-options stop-click"/> + </html:div> + </hbox> + <box class="torPreferences-bridgeCard-grid"> + <box class="torPreferences-bridgeCard-qrWrapper"> + <box class="torPreferences-bridgeCard-qr stop-click"> + <html:div class="torPreferences-bridgeCard-qrCode"/> + <html:div class="torPreferences-bridgeCard-qrOnionBox"/> + <html:div class="torPreferences-bridgeCard-qrOnion"/> + </box> + <html:div class="torPreferences-bridgeCard-filler"/> + </box> + <description class="torPreferences-bridgeCard-share"></description> + <hbox class="torPreferences-bridgeCard-addrBox"> + <html:input class="torPreferences-bridgeCard-addr torMarginFix stop-click" type="text" readonly="readonly"/> + </hbox> + <hbox class="torPreferences-bridgeCard-learnMoreBox" align="center"> + <label class="torPreferences-bridgeCard-learnMore learnMore text-link stop-click" is="text-link"/> + </hbox> + <hbox class="torPreferences-bridgeCard-copy" align="center"> + <button class="torPreferences-bridgeCard-copyButton stop-click"/> + </hbox> + </box> + </vbox> + <vbox id="torPreferences-currentBridges-cards"></vbox> + <vbox align="center"> + <button id="torPreferences-currentBridges-showAll"/> + <button id="torPreferences-currentBridges-removeAll" class="primary danger-button"/> + </vbox> + <html:h2 id="torPreferences-addBridge-header"></html:h2> + <hbox align="center"> + <label id="torPreferences-addBridge-labelBuiltinBridge"/> + <space flex="1"/> + <button id="torPreferences-addBridge-buttonBuiltinBridge" class="torMarginFix"/> + </hbox> + <hbox align="center"> + <label id="torPreferences-addBridge-labelRequestBridge"/> + <space flex="1"/> + <button id="torPreferences-addBridge-buttonRequestBridge" class="torMarginFix"/> + </hbox> + <hbox align="center"> + <label id="torPreferences-addBridge-labelEnterBridge"/> + <space flex="1"/> + <button id="torPreferences-addBridge-buttonEnterBridge" class="torMarginFix"/> + </hbox> +</groupbox> + +<!-- Advanced --> +<hbox class="subcategory" + data-category="paneConnection" + hidden="true"> + <html:h1 id="torPreferences-advanced-header"/> +</hbox> +<groupbox id="torPreferences-advanced-group" + data-category="paneConnection" + hidden="true"> + <box id="torPreferences-advanced-grid"> + <hbox id="torPreferences-advanced-hbox" align="center"> + <label id="torPreferences-advanced-label"/> + </hbox> + <hbox align="center"> + <button id="torPreferences-advanced-button"/> + </hbox> + <hbox id="torPreferences-torDaemon-hbox" align="center"> + <label id="torPreferences-torLogs"/> + </hbox> + <hbox align="center" data-subcategory="viewlogs"> + <button id="torPreferences-buttonTorLogs"/> + </hbox> + </box> +</groupbox> +</html:template> diff --git a/browser/components/torpreferences/content/connectionSettingsDialog.jsm b/browser/components/torpreferences/content/connectionSettingsDialog.jsm new file mode 100644 index 0000000000000..abc177c43f884 --- /dev/null +++ b/browser/components/torpreferences/content/connectionSettingsDialog.jsm @@ -0,0 +1,393 @@ +"use strict"; + +var EXPORTED_SYMBOLS = ["ConnectionSettingsDialog"]; + +const { TorSettings, TorProxyType } = ChromeUtils.import( + "resource:///modules/TorSettings.jsm" +); + +const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); + +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; + } + + 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", + }; + } + + // disables the provided list of elements + _setElementsDisabled(elements, disabled) { + for (let currentElement of elements) { + currentElement.disabled = disabled; + } + } + + _populateXUL(window, aDialog) { + const selectors = ConnectionSettingsDialog.selectors; + + this._dialog = aDialog; + const dialogWin = this._dialog.parentElement; + dialogWin.setAttribute( + "title", + TorStrings.settings.connectionSettingsDialogTitle + ); + this._dialog.querySelector(selectors.header).textContent = + TorStrings.settings.connectionSettingsDialogHeader; + + // Local Proxy + this._useProxyCheckbox = this._dialog.querySelector( + selectors.useProxyCheckbox + ); + this._useProxyCheckbox.setAttribute( + "label", + TorStrings.settings.useLocalProxy + ); + this._useProxyCheckbox.addEventListener("command", e => { + const checked = this._useProxyCheckbox.checked; + this.onToggleProxy(checked); + }); + this._proxyTypeLabel = this._dialog.querySelector(selectors.proxyTypeLabel); + this._proxyTypeLabel.setAttribute("value", TorStrings.settings.proxyType); + + let mockProxies = [ + { + value: TorProxyType.Socks4, + label: TorStrings.settings.proxyTypeSOCKS4, + }, + { + value: TorProxyType.Socks5, + label: TorStrings.settings.proxyTypeSOCKS5, + }, + { value: TorProxyType.HTTPS, label: TorStrings.settings.proxyTypeHTTP }, + ]; + this._proxyTypeMenulist = this._dialog.querySelector( + selectors.proxyTypeList + ); + this._proxyTypeMenulist.addEventListener("command", e => { + const value = this._proxyTypeMenulist.value; + this.onSelectProxyType(value); + }); + for (let currentProxy of mockProxies) { + let menuEntry = window.document.createXULElement("menuitem"); + menuEntry.setAttribute("value", currentProxy.value); + menuEntry.setAttribute("label", currentProxy.label); + this._proxyTypeMenulist.querySelector("menupopup").appendChild(menuEntry); + } + + this._proxyAddressLabel = this._dialog.querySelector( + selectors.proxyAddressLabel + ); + this._proxyAddressLabel.setAttribute( + "value", + TorStrings.settings.proxyAddress + ); + this._proxyAddressTextbox = this._dialog.querySelector( + selectors.proxyAddressTextbox + ); + this._proxyAddressTextbox.setAttribute( + "placeholder", + TorStrings.settings.proxyAddressPlaceholder + ); + this._proxyAddressTextbox.addEventListener("blur", e => { + let value = this._proxyAddressTextbox.value.trim(); + let colon = value.lastIndexOf(":"); + if (colon != -1) { + let maybePort = parseInt(value.substr(colon + 1)); + if (!isNaN(maybePort) && maybePort > 0 && maybePort < 65536) { + this._proxyAddressTextbox.value = value.substr(0, colon); + this._proxyPortTextbox.value = maybePort; + } + } + }); + this._proxyPortLabel = this._dialog.querySelector(selectors.proxyPortLabel); + this._proxyPortLabel.setAttribute("value", TorStrings.settings.proxyPort); + this._proxyPortTextbox = this._dialog.querySelector( + selectors.proxyPortTextbox + ); + this._proxyUsernameLabel = this._dialog.querySelector( + selectors.proxyUsernameLabel + ); + this._proxyUsernameLabel.setAttribute( + "value", + TorStrings.settings.proxyUsername + ); + this._proxyUsernameTextbox = this._dialog.querySelector( + selectors.proxyUsernameTextbox + ); + this._proxyUsernameTextbox.setAttribute( + "placeholder", + TorStrings.settings.proxyUsernamePasswordPlaceholder + ); + this._proxyPasswordLabel = this._dialog.querySelector( + selectors.proxyPasswordLabel + ); + this._proxyPasswordLabel.setAttribute( + "value", + TorStrings.settings.proxyPassword + ); + this._proxyPasswordTextbox = this._dialog.querySelector( + selectors.proxyPasswordTextbox + ); + this._proxyPasswordTextbox.setAttribute( + "placeholder", + TorStrings.settings.proxyUsernamePasswordPlaceholder + ); + + this.onToggleProxy(false); + if (TorSettings.proxy.enabled) { + this.onToggleProxy(true); + this.onSelectProxyType(TorSettings.proxy.type); + this._proxyAddressTextbox.value = TorSettings.proxy.address; + this._proxyPortTextbox.value = TorSettings.proxy.port; + this._proxyUsernameTextbox.value = TorSettings.proxy.username; + this._proxyPasswordTextbox.value = TorSettings.proxy.password; + } + + // Local firewall + this._useFirewallCheckbox = this._dialog.querySelector( + selectors.useFirewallCheckbox + ); + this._useFirewallCheckbox.setAttribute( + "label", + TorStrings.settings.useFirewall + ); + this._useFirewallCheckbox.addEventListener("command", e => { + const checked = this._useFirewallCheckbox.checked; + this.onToggleFirewall(checked); + }); + this._allowedPortsLabel = this._dialog.querySelector( + selectors.firewallAllowedPortsLabel + ); + this._allowedPortsLabel.setAttribute( + "value", + TorStrings.settings.allowedPorts + ); + this._allowedPortsTextbox = this._dialog.querySelector( + selectors.firewallAllowedPortsTextbox + ); + this._allowedPortsTextbox.setAttribute( + "placeholder", + TorStrings.settings.allowedPortsPlaceholder + ); + + this.onToggleFirewall(false); + if (TorSettings.firewall.enabled) { + this.onToggleFirewall(true); + this._allowedPortsTextbox.value = TorSettings.firewall.allowed_ports.join( + ", " + ); + } + + this._dialog.addEventListener("dialogaccept", e => { + this._applySettings(); + }); + } + + // callback when proxy is toggled + onToggleProxy(enabled) { + this._useProxyCheckbox.checked = enabled; + let disabled = !enabled; + + this._setElementsDisabled( + [ + this._proxyTypeLabel, + this._proxyTypeMenulist, + this._proxyAddressLabel, + this._proxyAddressTextbox, + this._proxyPortLabel, + this._proxyPortTextbox, + this._proxyUsernameLabel, + this._proxyUsernameTextbox, + this._proxyPasswordLabel, + this._proxyPasswordTextbox, + ], + disabled + ); + if (enabled) { + this.onSelectProxyType(this._proxyTypeMenulist.value); + } + } + + // callback when proxy type is changed + onSelectProxyType(value) { + if (typeof value === "string") { + value = parseInt(value); + } + + this._proxyTypeMenulist.value = value; + switch (value) { + case TorProxyType.Invalid: { + this._setElementsDisabled( + [ + this._proxyAddressLabel, + this._proxyAddressTextbox, + this._proxyPortLabel, + this._proxyPortTextbox, + this._proxyUsernameLabel, + this._proxyUsernameTextbox, + this._proxyPasswordLabel, + this._proxyPasswordTextbox, + ], + true + ); // DISABLE + + this._proxyAddressTextbox.value = ""; + this._proxyPortTextbox.value = ""; + this._proxyUsernameTextbox.value = ""; + this._proxyPasswordTextbox.value = ""; + break; + } + case TorProxyType.Socks4: { + this._setElementsDisabled( + [ + this._proxyAddressLabel, + this._proxyAddressTextbox, + this._proxyPortLabel, + this._proxyPortTextbox, + ], + false + ); // ENABLE + this._setElementsDisabled( + [ + this._proxyUsernameLabel, + this._proxyUsernameTextbox, + this._proxyPasswordLabel, + this._proxyPasswordTextbox, + ], + true + ); // DISABLE + + this._proxyUsernameTextbox.value = ""; + this._proxyPasswordTextbox.value = ""; + break; + } + case TorProxyType.Socks5: + case TorProxyType.HTTPS: { + this._setElementsDisabled( + [ + this._proxyAddressLabel, + this._proxyAddressTextbox, + this._proxyPortLabel, + this._proxyPortTextbox, + this._proxyUsernameLabel, + this._proxyUsernameTextbox, + this._proxyPasswordLabel, + this._proxyPasswordTextbox, + ], + false + ); // ENABLE + break; + } + } + } + + // callback when firewall proxy is toggled + onToggleFirewall(enabled) { + this._useFirewallCheckbox.checked = enabled; + let disabled = !enabled; + + this._setElementsDisabled( + [this._allowedPortsLabel, this._allowedPortsTextbox], + disabled + ); + } + + // pushes settings from UI to tor + _applySettings() { + const type = this._useProxyCheckbox.checked + ? parseInt(this._proxyTypeMenulist.value) + : TorProxyType.Invalid; + const address = this._proxyAddressTextbox.value; + const port = this._proxyPortTextbox.value; + const username = this._proxyUsernameTextbox.value; + const password = this._proxyPasswordTextbox.value; + switch (type) { + case TorProxyType.Invalid: + TorSettings.proxy.enabled = false; + break; + case TorProxyType.Socks4: + TorSettings.proxy.enabled = true; + TorSettings.proxy.type = type; + TorSettings.proxy.address = address; + TorSettings.proxy.port = port; + break; + case TorProxyType.Socks5: + TorSettings.proxy.enabled = true; + TorSettings.proxy.type = type; + TorSettings.proxy.address = address; + TorSettings.proxy.port = port; + TorSettings.proxy.username = username; + TorSettings.proxy.password = password; + break; + case TorProxyType.HTTPS: + TorSettings.proxy.enabled = true; + TorSettings.proxy.type = type; + TorSettings.proxy.address = address; + TorSettings.proxy.port = port; + TorSettings.proxy.username = username; + TorSettings.proxy.password = password; + break; + } + + let portListString = this._useFirewallCheckbox.checked + ? this._allowedPortsTextbox.value + : ""; + if (portListString) { + TorSettings.firewall.enabled = true; + TorSettings.firewall.allowed_ports = portListString; + } else { + TorSettings.firewall.enabled = false; + } + + TorSettings.saveToPrefs(); + TorSettings.applySettings(); + } + + init(window, aDialog) { + // defer to later until firefox has populated the dialog with all our elements + window.setTimeout(() => { + this._populateXUL(window, aDialog); + }, 0); + } + + openDialog(gSubDialog) { + gSubDialog.open( + "chrome://browser/content/torpreferences/connectionSettingsDialog.xhtml", + { features: "resizable=yes" }, + this + ); + } +} diff --git a/browser/components/torpreferences/content/connectionSettingsDialog.xhtml b/browser/components/torpreferences/content/connectionSettingsDialog.xhtml new file mode 100644 index 0000000000000..88aa979256f02 --- /dev/null +++ b/browser/components/torpreferences/content/connectionSettingsDialog.xhtml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> +<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?> + +<window type="child" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml%22%3E +<dialog id="torPreferences-connection-dialog" + buttons="accept,cancel"> + <html:h3 id="torPreferences-connection-header">​</html:h3> + <box id="torPreferences-connection-grid"> + <!-- Local Proxy --> + <hbox class="torPreferences-connection-checkbox-container"> + <checkbox id="torPreferences-connection-toggleProxy" label="​"/> + </hbox> + <hbox class="indent" align="center"> + <label id="torPreferences-localProxy-type"/> + </hbox> + <hbox align="center"> + <spacer flex="1"/> + <menulist id="torPreferences-localProxy-builtinList" class="torMarginFix"> + <menupopup/> + </menulist> + </hbox> + <hbox class="indent" align="center"> + <label id="torPreferences-localProxy-address"/> + </hbox> + <hbox align="center"> + <html:input id="torPreferences-localProxy-textboxAddress" type="text" class="torMarginFix"/> + <label id="torPreferences-localProxy-port"/> + <!-- proxy-port-input class style pulled from preferences.css and used in the vanilla proxy setup menu --> + <html:input id="torPreferences-localProxy-textboxPort" class="proxy-port-input torMarginFix" hidespinbuttons="true" type="number" min="0" max="65535" maxlength="5"/> + </hbox> + <hbox class="indent" align="center"> + <label id="torPreferences-localProxy-username"/> + </hbox> + <hbox align="center"> + <html:input id="torPreferences-localProxy-textboxUsername" type="text" class="torMarginFix"/> + <label id="torPreferences-localProxy-password"/> + <html:input id="torPreferences-localProxy-textboxPassword" class="torMarginFix" type="password"/> + </hbox> + <!-- Firewall --> + <hbox class="torPreferences-connection-checkbox-container"> + <checkbox id="torPreferences-connection-toggleFirewall" label="​"/> + </hbox> + <hbox class="indent" align="center"> + <label id="torPreferences-connection-allowedPorts"/> + </hbox> + <hbox align="center"> + <html:input id="torPreferences-connection-textboxAllowedPorts" type="text" class="torMarginFix" value="80,443"/> + </hbox> + </box> + <script type="application/javascript"><![CDATA[ + "use strict"; + + let connectionSettingsDialog = window.arguments[0]; + let dialog = document.getElementById("torPreferences-connection-dialog"); + connectionSettingsDialog.init(window, dialog); + ]]></script> +</dialog> +</window> diff --git a/browser/components/torpreferences/content/network.svg b/browser/components/torpreferences/content/network.svg new file mode 100644 index 0000000000000..e1689b5e6d649 --- /dev/null +++ b/browser/components/torpreferences/content/network.svg @@ -0,0 +1,6 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity"> + <path d="M8.5 1a7.5 7.5 0 1 0 0 15 7.5 7.5 0 0 0 0-15zm2.447 1.75a6.255 6.255 0 0 1 3.756 5.125l-2.229 0A9.426 9.426 0 0 0 10.54 2.75l.407 0zm-2.049 0a8.211 8.211 0 0 1 2.321 5.125l-5.438 0A8.211 8.211 0 0 1 8.102 2.75l.796 0zm-2.846 0 .408 0a9.434 9.434 0 0 0-1.934 5.125l-2.229 0A6.254 6.254 0 0 1 6.052 2.75zm0 11.5a6.252 6.252 0 0 1-3.755-5.125l2.229 0A9.426 9.426 0 0 0 6.46 14.25l-.408 0zm2.05 0a8.211 8.211 0 0 1-2.321-5.125l5.437 0a8.211 8.211 0 0 1-2.321 5.125l-.795 0zm2.846 0-.40 [...] +</svg> diff --git a/browser/components/torpreferences/content/provideBridgeDialog.jsm b/browser/components/torpreferences/content/provideBridgeDialog.jsm new file mode 100644 index 0000000000000..b73a6f533afa6 --- /dev/null +++ b/browser/components/torpreferences/content/provideBridgeDialog.jsm @@ -0,0 +1,69 @@ +"use strict"; + +var EXPORTED_SYMBOLS = ["ProvideBridgeDialog"]; + +const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); + +const { TorSettings, TorBridgeSource } = ChromeUtils.import( + "resource:///modules/TorSettings.jsm" +); + +class ProvideBridgeDialog { + constructor() { + this._dialog = null; + this._textarea = null; + this._bridgeString = ""; + } + + static get selectors() { + return { + header: "#torPreferences-provideBridge-header", + textarea: "#torPreferences-provideBridge-textarea", + }; + } + + _populateXUL(aDialog) { + const selectors = ProvideBridgeDialog.selectors; + + this._dialog = aDialog; + const dialogWin = this._dialog.parentElement; + dialogWin.setAttribute("title", TorStrings.settings.provideBridgeTitle); + this._dialog.querySelector(selectors.header).textContent = + TorStrings.settings.provideBridgeHeader; + this._textarea = this._dialog.querySelector(selectors.textarea); + this._textarea.setAttribute( + "placeholder", + TorStrings.settings.provideBridgePlaceholder + ); + if ( + TorSettings.bridges.enabled && + TorSettings.bridges.source == TorBridgeSource.UserProvided + ) { + this._textarea.value = TorSettings.bridges.bridge_strings.join("\n"); + } + + this._dialog.addEventListener("dialogaccept", e => { + this._bridgeString = this._textarea.value; + }); + } + + init(window, aDialog) { + // defer to later until firefox has populated the dialog with all our elements + window.setTimeout(() => { + this._populateXUL(aDialog); + }, 0); + } + + openDialog(gSubDialog, aCloseCallback) { + gSubDialog.open( + "chrome://browser/content/torpreferences/provideBridgeDialog.xhtml", + { + features: "resizable=yes", + closingCallback: () => { + aCloseCallback(this._bridgeString); + }, + }, + this + ); + } +} diff --git a/browser/components/torpreferences/content/provideBridgeDialog.xhtml b/browser/components/torpreferences/content/provideBridgeDialog.xhtml new file mode 100644 index 0000000000000..28d19cadaf9c9 --- /dev/null +++ b/browser/components/torpreferences/content/provideBridgeDialog.xhtml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> +<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?> + +<window type="child" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml%22%3E +<dialog id="torPreferences-provideBridge-dialog" + buttons="help,accept,cancel"> + <html:h3 id="torPreferences-provideBridge-header">​</html:h3> + <html:textarea id="torPreferences-provideBridge-textarea" multiline="true" rows="3"/> + <script type="application/javascript"><![CDATA[ + "use strict"; + + let provideBridgeDialog = window.arguments[0]; + let dialog = document.getElementById("torPreferences-provideBridge-dialog"); + provideBridgeDialog.init(window, dialog); + ]]></script> +</dialog> +</window> diff --git a/browser/components/torpreferences/content/requestBridgeDialog.jsm b/browser/components/torpreferences/content/requestBridgeDialog.jsm index 44ae11762def8..f14bbdcbbb448 100644 --- a/browser/components/torpreferences/content/requestBridgeDialog.jsm +++ b/browser/components/torpreferences/content/requestBridgeDialog.jsm @@ -9,7 +9,7 @@ class RequestBridgeDialog { constructor() { this._dialog = null; this._submitButton = null; - this._dialogDescription = null; + this._dialogHeader = null; this._captchaImage = null; this._captchaEntryTextbox = null; this._captchaRefreshButton = null; @@ -22,7 +22,7 @@ class RequestBridgeDialog { return { submitButton: "accept" /* not really a selector but a key for dialog's getButton */, - dialogDescription: "description#torPreferences-requestBridge-description", + dialogHeader: "h3#torPreferences-requestBridge-header", captchaImage: "image#torPreferences-requestBridge-captchaImage", captchaEntryTextbox: "input#torPreferences-requestBridge-captchaTextbox", refreshCaptchaButton: @@ -34,7 +34,7 @@ class RequestBridgeDialog { }; }
- _populateXUL(dialog) { + _populateXUL(window, dialog) { const selectors = RequestBridgeDialog.selectors;
this._dialog = dialog; @@ -66,12 +66,15 @@ class RequestBridgeDialog { e.preventDefault(); this.onSubmitCaptcha(); }); + this._dialog.addEventListener("dialoghelp", e => { + window.top.openTrustedLinkIn( + "https://tb-manual.torproject.org/bridges/", + "tab" + ); + });
- this._dialogDescription = this._dialog.querySelector( - selectors.dialogDescription - ); - this._dialogDescription.textContent = - TorStrings.settings.contactingBridgeDB; + this._dialogHeader = this._dialog.querySelector(selectors.dialogHeader); + this._dialogHeader.textContent = TorStrings.settings.contactingBridgeDB;
this._captchaImage = this._dialog.querySelector(selectors.captchaImage);
@@ -115,7 +118,7 @@ class RequestBridgeDialog { _setcaptchaImage(uri) { if (uri != this._captchaImage.src) { this._captchaImage.src = uri; - this._dialogDescription.textContent = TorStrings.settings.solveTheCaptcha; + this._dialogHeader.textContent = TorStrings.settings.solveTheCaptcha; this._setUIDisabled(false); this._captchaEntryTextbox.focus(); this._captchaEntryTextbox.select(); @@ -135,7 +138,7 @@ class RequestBridgeDialog { init(window, dialog) { // defer to later until firefox has populated the dialog with all our elements window.setTimeout(() => { - this._populateXUL(dialog); + this._populateXUL(window, dialog); }, 0); }
@@ -176,15 +179,14 @@ class RequestBridgeDialog { this._bridges = []; this._setUIDisabled(false); this._incorrectCaptchaHbox.style.visibility = "visible"; - console.log(eError); + console.log(aError); }); }
onRefreshCaptcha() { this._setUIDisabled(true); this._captchaImage.src = ""; - this._dialogDescription.textContent = - TorStrings.settings.contactingBridgeDB; + this._dialogHeader.textContent = TorStrings.settings.contactingBridgeDB; this._captchaEntryTextbox.value = ""; this._incorrectCaptchaHbox.style.visibility = "hidden";
@@ -201,9 +203,9 @@ class RequestBridgeDialog { closingCallback: () => { this.close(); aCloseCallback(this._bridges); - } + }, }, - this, + this ); } } diff --git a/browser/components/torpreferences/content/requestBridgeDialog.xhtml b/browser/components/torpreferences/content/requestBridgeDialog.xhtml index 64c4507807fbf..b7286528a8a5a 100644 --- a/browser/components/torpreferences/content/requestBridgeDialog.xhtml +++ b/browser/components/torpreferences/content/requestBridgeDialog.xhtml @@ -7,11 +7,11 @@ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml%22%3E <dialog id="torPreferences-requestBridge-dialog" - buttons="accept,cancel"> + buttons="help,accept,cancel"> <!-- ok, so ​ is a zero-width space. We need to have *something* in the innerText so that XUL knows how tall the - description 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 >:( --> - <description id="torPreferences-requestBridge-description">​</description> + 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 >:( --> + <html:h3 id="torPreferences-requestBridge-header">​</html:h3> <!-- init to transparent 400x125 png --> <image id="torPreferences-requestBridge-captchaImage" flex="1"/> <hbox id="torPreferences-requestBridge-inputHbox"> @@ -32,4 +32,4 @@ requestBridgeDialog.init(window, dialog); ]]></script> </dialog> -</window> \ No newline at end of file +</window> diff --git a/browser/components/torpreferences/content/torLogDialog.jsm b/browser/components/torpreferences/content/torLogDialog.jsm index ecc684d878c20..94a57b9b165ee 100644 --- a/browser/components/torpreferences/content/torLogDialog.jsm +++ b/browser/components/torpreferences/content/torLogDialog.jsm @@ -2,6 +2,10 @@
var EXPORTED_SYMBOLS = ["TorLogDialog"];
+const { setTimeout, clearTimeout } = ChromeUtils.import( + "resource://gre/modules/Timer.jsm" +); + const { TorProtocolService } = ChromeUtils.import( "resource:///modules/TorProtocolService.jsm" ); @@ -12,6 +16,7 @@ class TorLogDialog { this._dialog = null; this._logTextarea = null; this._copyLogButton = null; + this._restoreButtonTimeout = null; }
static get selectors() { @@ -36,6 +41,19 @@ class TorLogDialog { 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); });
this._logTextarea.value = TorProtocolService.getLog(); diff --git a/browser/components/torpreferences/content/torPane.js b/browser/components/torpreferences/content/torPane.js deleted file mode 100644 index 58eec7ff74aa6..0000000000000 --- a/browser/components/torpreferences/content/torPane.js +++ /dev/null @@ -1,940 +0,0 @@ -"use strict"; - -/* global Services */ - -const { TorSettings, TorSettingsTopics, TorSettingsData, TorBridgeSource, TorBuiltinBridgeTypes, TorProxyType } = ChromeUtils.import( - "resource:///modules/TorSettings.jsm" -); - -const { TorProtocolService } = ChromeUtils.import( - "resource:///modules/TorProtocolService.jsm" -); - -const { TorConnect, TorConnectTopics, TorConnectState } = ChromeUtils.import( - "resource:///modules/TorConnect.jsm" -); - -const { TorLogDialog } = ChromeUtils.import( - "chrome://browser/content/torpreferences/torLogDialog.jsm" -); - -const { RequestBridgeDialog } = ChromeUtils.import( - "chrome://browser/content/torpreferences/requestBridgeDialog.jsm" -); - -ChromeUtils.defineModuleGetter( - this, - "TorStrings", - "resource:///modules/TorStrings.jsm" -); - -/* - Tor Pane - - Code for populating the XUL in about:preferences#tor, handling input events, interfacing with tor-launcher -*/ -const gTorPane = (function() { - /* CSS selectors for all of the Tor Network DOM elements we need to access */ - const selectors = { - category: { - title: "label#torPreferences-labelCategory", - }, - messageBox: { - box: "div#torPreferences-connectMessageBox", - message: "td#torPreferences-connectMessageBox-message", - button: "button#torPreferences-connectMessageBox-button", - }, - torPreferences: { - header: "h1#torPreferences-header", - description: "span#torPreferences-description", - learnMore: "label#torPreferences-learnMore", - }, - quickstart: { - header: "h2#torPreferences-quickstart-header", - description: "span#torPreferences-quickstart-description", - enableQuickstartCheckbox: "checkbox#torPreferences-quickstart-toggle", - }, - bridges: { - header: "h2#torPreferences-bridges-header", - description: "span#torPreferences-bridges-description", - learnMore: "label#torPreferences-bridges-learnMore", - useBridgeCheckbox: "checkbox#torPreferences-bridges-toggle", - bridgeSelectionRadiogroup: - "radiogroup#torPreferences-bridges-bridgeSelection", - builtinBridgeOption: "radio#torPreferences-bridges-radioBuiltin", - builtinBridgeList: "menulist#torPreferences-bridges-builtinList", - requestBridgeOption: "radio#torPreferences-bridges-radioRequestBridge", - requestBridgeButton: "button#torPreferences-bridges-buttonRequestBridge", - requestBridgeTextarea: - "textarea#torPreferences-bridges-textareaRequestBridge", - provideBridgeOption: "radio#torPreferences-bridges-radioProvideBridge", - provideBridgeDescription: - "description#torPreferences-bridges-descriptionProvideBridge", - provideBridgeTextarea: - "textarea#torPreferences-bridges-textareaProvideBridge", - }, - advanced: { - header: "h2#torPreferences-advanced-header", - description: "span#torPreferences-advanced-description", - learnMore: "label#torPreferences-advanced-learnMore", - useProxyCheckbox: "checkbox#torPreferences-advanced-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-advanced-toggleFirewall", - firewallAllowedPortsLabel: "label#torPreferences-advanced-allowedPorts", - firewallAllowedPortsTextbox: - "input#torPreferences-advanced-textboxAllowedPorts", - torLogsLabel: "label#torPreferences-torLogs", - torLogsButton: "button#torPreferences-buttonTorLogs", - }, - }; /* selectors */ - - let retval = { - // cached frequently accessed DOM elements - _messageBox: null, - _messageBoxMessage: null, - _messageBoxButton: null, - _enableQuickstartCheckbox: null, - _useBridgeCheckbox: null, - _bridgeSelectionRadiogroup: null, - _builtinBridgeOption: null, - _builtinBridgeMenulist: null, - _requestBridgeOption: null, - _requestBridgeButton: null, - _requestBridgeTextarea: null, - _provideBridgeOption: null, - _provideBridgeTextarea: null, - _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, - - // tor network settings - _bridgeSettings: null, - _proxySettings: null, - _firewallSettings: null, - - // disables the provided list of elements - _setElementsDisabled(elements, disabled) { - for (let currentElement of elements) { - currentElement.disabled = disabled; - } - }, - - // populate xul with strings and cache the relevant elements - _populateXUL() { - // saves tor settings to disk when navigate away from about:preferences - window.addEventListener("blur", val => { - TorProtocolService.flushSettings(); - }); - - document - .querySelector(selectors.category.title) - .setAttribute("value", TorStrings.settings.categoryTitle); - - let prefpane = document.getElementById("mainPrefPane"); - - // 'Connect to Tor' Message Bar - - this._messageBox = prefpane.querySelector(selectors.messageBox.box); - this._messageBoxMessage = prefpane.querySelector(selectors.messageBox.message); - this._messageBoxButton = prefpane.querySelector(selectors.messageBox.button); - // wire up connect button - this._messageBoxButton.addEventListener("click", () => { - TorConnect.beginBootstrap(); - TorConnect.openTorConnect(); - }); - - this._populateMessagebox = () => { - if (TorConnect.shouldShowTorConnect && - TorConnect.state === TorConnectState.Configuring) { - // set messagebox style and text - if (TorProtocolService.torBootstrapErrorOccurred()) { - this._messageBox.parentNode.style.display = null; - this._messageBox.className = "error"; - this._messageBoxMessage.innerText = TorStrings.torConnect.tryAgainMessage; - this._messageBoxButton.innerText = TorStrings.torConnect.tryAgain; - } else { - this._messageBox.parentNode.style.display = null; - this._messageBox.className = "warning"; - this._messageBoxMessage.innerText = TorStrings.torConnect.connectMessage; - this._messageBoxButton.innerText = TorStrings.torConnect.torConnectButton; - } - } else { - // we need to explicitly hide the groupbox, as switching between - // the tor pane and other panes will 'unhide' (via the 'hidden' - // attribute) the groupbox, offsetting all of the content down - // by the groupbox's margin (even if content is 0 height) - this._messageBox.parentNode.style.display = "none"; - this._messageBox.className = "hidden"; - this._messageBoxMessage.innerText = ""; - this._messageBoxButton.innerText = ""; - } - } - this._populateMessagebox(); - Services.obs.addObserver(this, TorConnectTopics.StateChange); - - // update the messagebox whenever we come back to the page - window.addEventListener("focus", val => { - this._populateMessagebox(); - }); - - // Heading - prefpane.querySelector(selectors.torPreferences.header).innerText = - TorStrings.settings.torPreferencesHeading; - prefpane.querySelector(selectors.torPreferences.description).textContent = - TorStrings.settings.torPreferencesDescription; - { - let learnMore = prefpane.querySelector( - selectors.torPreferences.learnMore - ); - learnMore.setAttribute("value", TorStrings.settings.learnMore); - learnMore.setAttribute( - "href", - TorStrings.settings.learnMoreTorBrowserURL - ); - } - - // Quickstart - prefpane.querySelector(selectors.quickstart.header).innerText = - TorStrings.settings.quickstartHeading; - prefpane.querySelector(selectors.quickstart.description).textContent = - TorStrings.settings.quickstartDescription; - - this._enableQuickstartCheckbox = prefpane.querySelector( - selectors.quickstart.enableQuickstartCheckbox - ); - this._enableQuickstartCheckbox.setAttribute( - "label", - TorStrings.settings.quickstartCheckbox - ); - this._enableQuickstartCheckbox.addEventListener("command", e => { - const checked = this._enableQuickstartCheckbox.checked; - TorSettings.quickstart.enabled = checked; - TorSettings.saveToPrefs().applySettings(); - }); - this._enableQuickstartCheckbox.checked = TorSettings.quickstart.enabled; - Services.obs.addObserver(this, TorSettingsTopics.SettingChanged); - - // Bridge setup - prefpane.querySelector(selectors.bridges.header).innerText = - TorStrings.settings.bridgesHeading; - prefpane.querySelector(selectors.bridges.description).textContent = - TorStrings.settings.bridgesDescription; - { - let learnMore = prefpane.querySelector(selectors.bridges.learnMore); - learnMore.setAttribute("value", TorStrings.settings.learnMore); - learnMore.setAttribute("href", TorStrings.settings.learnMoreBridgesURL); - } - - this._useBridgeCheckbox = prefpane.querySelector( - selectors.bridges.useBridgeCheckbox - ); - this._useBridgeCheckbox.setAttribute( - "label", - TorStrings.settings.useBridge - ); - this._useBridgeCheckbox.addEventListener("command", e => { - const checked = this._useBridgeCheckbox.checked; - gTorPane.onToggleBridge(checked).onUpdateBridgeSettings(); - }); - this._bridgeSelectionRadiogroup = prefpane.querySelector( - selectors.bridges.bridgeSelectionRadiogroup - ); - this._bridgeSelectionRadiogroup.value = TorBridgeSource.BuiltIn; - this._bridgeSelectionRadiogroup.addEventListener("command", e => { - const value = this._bridgeSelectionRadiogroup.value; - gTorPane.onSelectBridgeOption(value).onUpdateBridgeSettings(); - }); - - // Builtin bridges - this._builtinBridgeOption = prefpane.querySelector( - selectors.bridges.builtinBridgeOption - ); - this._builtinBridgeOption.setAttribute( - "label", - TorStrings.settings.selectBridge - ); - this._builtinBridgeOption.setAttribute("value", TorBridgeSource.BuiltIn); - this._builtinBridgeMenulist = prefpane.querySelector( - selectors.bridges.builtinBridgeList - ); - this._builtinBridgeMenulist.addEventListener("command", e => { - gTorPane.onUpdateBridgeSettings(); - }); - - // Request bridge - this._requestBridgeOption = prefpane.querySelector( - selectors.bridges.requestBridgeOption - ); - this._requestBridgeOption.setAttribute( - "label", - TorStrings.settings.requestBridgeFromTorProject - ); - this._requestBridgeOption.setAttribute("value", TorBridgeSource.BridgeDB); - this._requestBridgeButton = prefpane.querySelector( - selectors.bridges.requestBridgeButton - ); - this._requestBridgeButton.setAttribute( - "label", - TorStrings.settings.requestNewBridge - ); - this._requestBridgeButton.addEventListener("command", () => - gTorPane.onRequestBridge() - ); - this._requestBridgeTextarea = prefpane.querySelector( - selectors.bridges.requestBridgeTextarea - ); - - // Provide a bridge - this._provideBridgeOption = prefpane.querySelector( - selectors.bridges.provideBridgeOption - ); - this._provideBridgeOption.setAttribute( - "label", - TorStrings.settings.provideBridge - ); - this._provideBridgeOption.setAttribute( - "value", - TorBridgeSource.UserProvided - ); - prefpane.querySelector( - selectors.bridges.provideBridgeDescription - ).textContent = TorStrings.settings.provideBridgeDirections; - this._provideBridgeTextarea = prefpane.querySelector( - selectors.bridges.provideBridgeTextarea - ); - this._provideBridgeTextarea.setAttribute( - "placeholder", - TorStrings.settings.provideBridgePlaceholder - ); - this._provideBridgeTextarea.addEventListener("blur", () => { - gTorPane.onUpdateBridgeSettings(); - }); - - // Advanced setup - prefpane.querySelector(selectors.advanced.header).innerText = - TorStrings.settings.advancedHeading; - prefpane.querySelector(selectors.advanced.description).textContent = - TorStrings.settings.advancedDescription; - { - let learnMore = prefpane.querySelector(selectors.advanced.learnMore); - learnMore.setAttribute("value", TorStrings.settings.learnMore); - learnMore.setAttribute( - "href", - TorStrings.settings.learnMoreNetworkSettingsURL - ); - } - - // Local Proxy - this._useProxyCheckbox = prefpane.querySelector( - selectors.advanced.useProxyCheckbox - ); - this._useProxyCheckbox.setAttribute( - "label", - TorStrings.settings.useLocalProxy - ); - this._useProxyCheckbox.addEventListener("command", e => { - const checked = this._useProxyCheckbox.checked; - gTorPane.onToggleProxy(checked).onUpdateProxySettings(); - }); - this._proxyTypeLabel = prefpane.querySelector( - selectors.advanced.proxyTypeLabel - ); - this._proxyTypeLabel.setAttribute("value", TorStrings.settings.proxyType); - - let mockProxies = [ - { - value: TorProxyType.Socks4, - label: TorStrings.settings.proxyTypeSOCKS4, - }, - { - value: TorProxyType.Socks5, - label: TorStrings.settings.proxyTypeSOCKS5, - }, - { value: TorProxyType.HTTPS, label: TorStrings.settings.proxyTypeHTTP }, - ]; - this._proxyTypeMenulist = prefpane.querySelector( - selectors.advanced.proxyTypeList - ); - this._proxyTypeMenulist.addEventListener("command", e => { - const value = this._proxyTypeMenulist.value; - gTorPane.onSelectProxyType(value).onUpdateProxySettings(); - }); - for (let currentProxy of mockProxies) { - let menuEntry = document.createXULElement("menuitem"); - menuEntry.setAttribute("value", currentProxy.value); - menuEntry.setAttribute("label", currentProxy.label); - this._proxyTypeMenulist - .querySelector("menupopup") - .appendChild(menuEntry); - } - - this._proxyAddressLabel = prefpane.querySelector( - selectors.advanced.proxyAddressLabel - ); - this._proxyAddressLabel.setAttribute( - "value", - TorStrings.settings.proxyAddress - ); - this._proxyAddressTextbox = prefpane.querySelector( - selectors.advanced.proxyAddressTextbox - ); - this._proxyAddressTextbox.setAttribute( - "placeholder", - TorStrings.settings.proxyAddressPlaceholder - ); - this._proxyAddressTextbox.addEventListener("blur", () => { - gTorPane.onUpdateProxySettings(); - }); - this._proxyPortLabel = prefpane.querySelector( - selectors.advanced.proxyPortLabel - ); - this._proxyPortLabel.setAttribute("value", TorStrings.settings.proxyPort); - this._proxyPortTextbox = prefpane.querySelector( - selectors.advanced.proxyPortTextbox - ); - this._proxyPortTextbox.addEventListener("blur", () => { - gTorPane.onUpdateProxySettings(); - }); - this._proxyUsernameLabel = prefpane.querySelector( - selectors.advanced.proxyUsernameLabel - ); - this._proxyUsernameLabel.setAttribute( - "value", - TorStrings.settings.proxyUsername - ); - this._proxyUsernameTextbox = prefpane.querySelector( - selectors.advanced.proxyUsernameTextbox - ); - this._proxyUsernameTextbox.setAttribute( - "placeholder", - TorStrings.settings.proxyUsernamePasswordPlaceholder - ); - this._proxyUsernameTextbox.addEventListener("blur", () => { - gTorPane.onUpdateProxySettings(); - }); - this._proxyPasswordLabel = prefpane.querySelector( - selectors.advanced.proxyPasswordLabel - ); - this._proxyPasswordLabel.setAttribute( - "value", - TorStrings.settings.proxyPassword - ); - this._proxyPasswordTextbox = prefpane.querySelector( - selectors.advanced.proxyPasswordTextbox - ); - this._proxyPasswordTextbox.setAttribute( - "placeholder", - TorStrings.settings.proxyUsernamePasswordPlaceholder - ); - this._proxyPasswordTextbox.addEventListener("blur", () => { - gTorPane.onUpdateProxySettings(); - }); - - // Local firewall - this._useFirewallCheckbox = prefpane.querySelector( - selectors.advanced.useFirewallCheckbox - ); - this._useFirewallCheckbox.setAttribute( - "label", - TorStrings.settings.useFirewall - ); - this._useFirewallCheckbox.addEventListener("command", e => { - const checked = this._useFirewallCheckbox.checked; - gTorPane.onToggleFirewall(checked).onUpdateFirewallSettings(); - }); - this._allowedPortsLabel = prefpane.querySelector( - selectors.advanced.firewallAllowedPortsLabel - ); - this._allowedPortsLabel.setAttribute( - "value", - TorStrings.settings.allowedPorts - ); - this._allowedPortsTextbox = prefpane.querySelector( - selectors.advanced.firewallAllowedPortsTextbox - ); - this._allowedPortsTextbox.setAttribute( - "placeholder", - TorStrings.settings.allowedPortsPlaceholder - ); - this._allowedPortsTextbox.addEventListener("blur", () => { - gTorPane.onUpdateFirewallSettings(); - }); - - // Tor logs - prefpane - .querySelector(selectors.advanced.torLogsLabel) - .setAttribute("value", TorStrings.settings.showTorDaemonLogs); - let torLogsButton = prefpane.querySelector( - selectors.advanced.torLogsButton - ); - torLogsButton.setAttribute("label", TorStrings.settings.showLogs); - torLogsButton.addEventListener("command", () => { - gTorPane.onViewTorLogs(); - }); - - // Disable all relevant elements by default - this._setElementsDisabled( - [ - this._builtinBridgeOption, - this._builtinBridgeMenulist, - this._requestBridgeOption, - this._requestBridgeButton, - this._requestBridgeTextarea, - this._provideBridgeOption, - this._provideBridgeTextarea, - this._proxyTypeLabel, - this._proxyTypeMenulist, - this._proxyAddressLabel, - this._proxyAddressTextbox, - this._proxyPortLabel, - this._proxyPortTextbox, - this._proxyUsernameLabel, - this._proxyUsernameTextbox, - this._proxyPasswordLabel, - this._proxyPasswordTextbox, - this._allowedPortsLabel, - this._allowedPortsTextbox, - ], - true - ); - - // init bridge UI - for (let currentBridge of TorBuiltinBridgeTypes) { - let menuEntry = document.createXULElement("menuitem"); - menuEntry.setAttribute("value", currentBridge); - menuEntry.setAttribute("label", currentBridge); - this._builtinBridgeMenulist - .querySelector("menupopup") - .appendChild(menuEntry); - } - - if (TorSettings.bridges.enabled) { - this.onSelectBridgeOption(TorSettings.bridges.source); - this.onToggleBridge( - TorSettings.bridges.source != TorBridgeSource.Invalid - ); - switch (TorSettings.bridges.source) { - case TorBridgeSource.Invalid: - break; - case TorBridgeSource.BuiltIn: - this._builtinBridgeMenulist.value = TorSettings.bridges.builtin_type; - break; - case TorBridgeSource.BridgeDB: - this._requestBridgeTextarea.value = TorSettings.bridges.bridge_strings.join("\n"); - break; - case TorBridgeSource.UserProvided: - this._provideBridgeTextarea.value = TorSettings.bridges.bridge_strings.join("\n"); - break; - } - } - - // init proxy UI - if (TorSettings.proxy.enabled) { - this.onToggleProxy(true); - this.onSelectProxyType(TorSettings.proxy.type); - this._proxyAddressTextbox.value = TorSettings.proxy.address; - this._proxyPortTextbox.value = TorSettings.proxy.port; - this._proxyUsernameTextbox.value = TorSettings.proxy.username; - this._proxyPasswordTextbox.value = TorSettings.proxy.password; - } - - // init firewall - if (TorSettings.firewall.enabled) { - this.onToggleFirewall(true); - this._allowedPortsTextbox.value = TorSettings.firewall.allowed_ports.join(", "); - } - }, - - init() { - this._populateXUL(); - - let onUnload = () => { - window.removeEventListener("unload", onUnload); - gTorPane.uninit(); - }; - window.addEventListener("unload", onUnload); - }, - - uninit() { - // unregister our observer topics - Services.obs.removeObserver(this, TorSettingsTopics.SettingChanged); - Services.obs.removeObserver(this, TorConnectTopics.StateChange); - }, - - // whether the page should be present in about:preferences - get enabled() { - return TorProtocolService.ownsTorDaemon; - }, - - // - // Callbacks - // - - observe(subject, topic, data) { - switch (topic) { - // triggered when a TorSettings param has changed - case TorSettingsTopics.SettingChanged: { - let obj = subject?.wrappedJSObject; - switch(data) { - case TorSettingsData.QuickStartEnabled: { - this._enableQuickstartCheckbox.checked = obj.value; - break; - } - } - break; - } - // triggered when tor connect state changes and we may - // need to update the messagebox - case TorConnectTopics.StateChange: { - this._populateMessagebox(); - break; - } - } - }, - - // callback when using bridges toggled - onToggleBridge(enabled) { - this._useBridgeCheckbox.checked = enabled; - let disabled = !enabled; - - // first disable all the bridge related elements - this._setElementsDisabled( - [ - this._builtinBridgeOption, - this._builtinBridgeMenulist, - this._requestBridgeOption, - this._requestBridgeButton, - this._requestBridgeTextarea, - this._provideBridgeOption, - this._provideBridgeTextarea, - ], - disabled - ); - - // and selectively re-enable based on the radiogroup's current value - if (enabled) { - this.onSelectBridgeOption(this._bridgeSelectionRadiogroup.value); - } else { - this.onSelectBridgeOption(TorBridgeSource.Invalid); - } - return this; - }, - - // callback when a bridge option is selected - onSelectBridgeOption(source) { - if (typeof source === "string") { - source = parseInt(source); - } - - // disable all of the bridge elements under radio buttons - this._setElementsDisabled( - [ - this._builtinBridgeMenulist, - this._requestBridgeButton, - this._requestBridgeTextarea, - this._provideBridgeTextarea, - ], - true - ); - - if (source != TorBridgeSource.Invalid) { - this._bridgeSelectionRadiogroup.value = source; - } - - switch (source) { - case TorBridgeSource.BuiltIn: { - this._setElementsDisabled([this._builtinBridgeMenulist], false); - break; - } - case TorBridgeSource.BridgeDB: { - this._setElementsDisabled( - [this._requestBridgeButton, this._requestBridgeTextarea], - false - ); - break; - } - case TorBridgeSource.UserProvided: { - this._setElementsDisabled([this._provideBridgeTextarea], false); - break; - } - } - return this; - }, - - // called when the request bridge button is activated - onRequestBridge() { - let requestBridgeDialog = new RequestBridgeDialog(); - requestBridgeDialog.openDialog( - gSubDialog, - aBridges => { - if (aBridges.length > 0) { - let bridgeStrings = aBridges.join("\n"); - TorSettings.bridges.enabled = true; - TorSettings.bridges.source = TorBridgeSource.BridgeDB; - TorSettings.bridges.bridge_strings = bridgeStrings; - TorSettings.saveToPrefs(); - TorSettings.applySettings().then((result) => { - this._requestBridgeTextarea.value = bridgeStrings; - }); - } - } - ); - return this; - }, - - // pushes bridge settings from UI to tor - onUpdateBridgeSettings() { - let source = this._useBridgeCheckbox.checked - ? parseInt(this._bridgeSelectionRadiogroup.value) - : TorBridgeSource.Invalid; - - switch (source) { - case TorBridgeSource.Invalid: { - TorSettings.bridges.enabled = false; - } - break; - case TorBridgeSource.BuiltIn: { - // if there is a built-in bridge already selected, use that - let bridgeType = this._builtinBridgeMenulist.value; - console.log(`bridge type: ${bridgeType}`); - if (bridgeType) { - TorSettings.bridges.enabled = true; - TorSettings.bridges.source = TorBridgeSource.BuiltIn; - TorSettings.bridges.builtin_type = bridgeType; - } else { - TorSettings.bridges.enabled = false; - } - break; - } - case TorBridgeSource.BridgeDB: { - // if there are bridgedb bridges saved in the text area, use them - let bridgeStrings = this._requestBridgeTextarea.value; - if (bridgeStrings) { - TorSettings.bridges.enabled = true; - TorSettings.bridges.source = TorBridgeSource.BridgeDB; - TorSettings.bridges.bridge_strings = bridgeStrings; - } else { - TorSettings.bridges.enabled = false; - } - break; - } - case TorBridgeSource.UserProvided: { - // if bridges already exist in the text area, use them - let bridgeStrings = this._provideBridgeTextarea.value; - if (bridgeStrings) { - TorSettings.bridges.enabled = true; - TorSettings.bridges.source = TorBridgeSource.UserProvided; - TorSettings.bridges.bridge_strings = bridgeStrings; - } else { - TorSettings.bridges.enabled = false; - } - break; - } - } - TorSettings.saveToPrefs(); - TorSettings.applySettings(); - - return this; - }, - - // callback when proxy is toggled - onToggleProxy(enabled) { - this._useProxyCheckbox.checked = enabled; - let disabled = !enabled; - - this._setElementsDisabled( - [ - this._proxyTypeLabel, - this._proxyTypeMenulist, - this._proxyAddressLabel, - this._proxyAddressTextbox, - this._proxyPortLabel, - this._proxyPortTextbox, - this._proxyUsernameLabel, - this._proxyUsernameTextbox, - this._proxyPasswordLabel, - this._proxyPasswordTextbox, - ], - disabled - ); - this.onSelectProxyType(this._proxyTypeMenulist.value); - return this; - }, - - // callback when proxy type is changed - onSelectProxyType(value) { - if (typeof value === "string") { - value = parseInt(value); - } - - this._proxyTypeMenulist.value = value; - switch (value) { - case TorProxyType.Invalid: { - this._setElementsDisabled( - [ - this._proxyAddressLabel, - this._proxyAddressTextbox, - this._proxyPortLabel, - this._proxyPortTextbox, - this._proxyUsernameLabel, - this._proxyUsernameTextbox, - this._proxyPasswordLabel, - this._proxyPasswordTextbox, - ], - true - ); // DISABLE - - this._proxyAddressTextbox.value = ""; - this._proxyPortTextbox.value = ""; - this._proxyUsernameTextbox.value = ""; - this._proxyPasswordTextbox.value = ""; - break; - } - case TorProxyType.Socks4: { - this._setElementsDisabled( - [ - this._proxyAddressLabel, - this._proxyAddressTextbox, - this._proxyPortLabel, - this._proxyPortTextbox, - ], - false - ); // ENABLE - this._setElementsDisabled( - [ - this._proxyUsernameLabel, - this._proxyUsernameTextbox, - this._proxyPasswordLabel, - this._proxyPasswordTextbox, - ], - true - ); // DISABLE - - this._proxyUsernameTextbox.value = ""; - this._proxyPasswordTextbox.value = ""; - break; - } - case TorProxyType.Socks5: - case TorProxyType.HTTPS: { - this._setElementsDisabled( - [ - this._proxyAddressLabel, - this._proxyAddressTextbox, - this._proxyPortLabel, - this._proxyPortTextbox, - this._proxyUsernameLabel, - this._proxyUsernameTextbox, - this._proxyPasswordLabel, - this._proxyPasswordTextbox, - ], - false - ); // ENABLE - break; - } - } - return this; - }, - - // pushes proxy settings from UI to tor - onUpdateProxySettings() { - const type = this._useProxyCheckbox.checked - ? parseInt(this._proxyTypeMenulist.value) - : TorProxyType.Invalid; - const address = this._proxyAddressTextbox.value; - const port = this._proxyPortTextbox.value; - const username = this._proxyUsernameTextbox.value; - const password = this._proxyPasswordTextbox.value; - - switch (type) { - case TorProxyType.Invalid: - TorSettings.proxy.enabled = false; - break; - case TorProxyType.Socks4: - TorSettings.proxy.enabled = true; - TorSettings.proxy.type = type; - TorSettings.proxy.address = address; - TorSettings.proxy.port = port; - - break; - case TorProxyType.Socks5: - TorSettings.proxy.enabled = true; - TorSettings.proxy.type = type; - TorSettings.proxy.address = address; - TorSettings.proxy.port = port; - TorSettings.proxy.username = username; - TorSettings.proxy.password = password; - break; - case TorProxyType.HTTPS: - TorSettings.proxy.enabled = true; - TorSettings.proxy.type = type; - TorSettings.proxy.address = address; - TorSettings.proxy.port = port; - TorSettings.proxy.username = username; - TorSettings.proxy.password = password; - break; - } - TorSettings.saveToPrefs(); - TorSettings.applySettings(); - - return this; - }, - - // callback when firewall proxy is toggled - onToggleFirewall(enabled) { - this._useFirewallCheckbox.checked = enabled; - let disabled = !enabled; - - this._setElementsDisabled( - [this._allowedPortsLabel, this._allowedPortsTextbox], - disabled - ); - - return this; - }, - - // pushes firewall settings from UI to tor - onUpdateFirewallSettings() { - - let portListString = this._useFirewallCheckbox.checked - ? this._allowedPortsTextbox.value - : ""; - - if (portListString) { - TorSettings.firewall.enabled = true; - TorSettings.firewall.allowed_ports = portListString; - } else { - TorSettings.firewall.enabled = false; - } - TorSettings.saveToPrefs(); - TorSettings.applySettings(); - - return this; - }, - - onViewTorLogs() { - let torLogDialog = new TorLogDialog(); - torLogDialog.openDialog(gSubDialog); - }, - }; - return retval; -})(); /* gTorPane */ diff --git a/browser/components/torpreferences/content/torPane.xhtml b/browser/components/torpreferences/content/torPane.xhtml deleted file mode 100644 index 7c8071f2cf106..0000000000000 --- a/browser/components/torpreferences/content/torPane.xhtml +++ /dev/null @@ -1,157 +0,0 @@ -<!-- Tor panel --> - -<script type="application/javascript" - src="chrome://browser/content/torpreferences/torPane.js"/> -<html:template id="template-paneTor"> - -<!-- Tor Connect Message Box --> -<groupbox data-category="paneTor" hidden="true"> - <html:div id="torPreferences-connectMessageBox" - class="subcategory" - data-category="paneTor" - hidden="true"> - <html:table > - html:tr - html:td - <html:div id="torPreferences-connectMessageBox-icon"/> - </html:td> - <html:td id="torPreferences-connectMessageBox-message"> - </html:td> - html:td - <html:button id="torPreferences-connectMessageBox-button"> - </html:button> - </html:td> - </html:tr> - </html:table> - </html:div> -</groupbox> - -<hbox id="torPreferencesCategory" - class="subcategory" - data-category="paneTor" - hidden="true"> - <html:h1 id="torPreferences-header"/> -</hbox> - -<groupbox data-category="paneTor" - hidden="true"> - <description flex="1"> - <html:span id="torPreferences-description" class="tail-with-learn-more"/> - <label id="torPreferences-learnMore" class="learnMore text-link" is="text-link"/> - </description> -</groupbox> - -<!-- Quickstart --> -<groupbox id="torPreferences-quickstart-group" - data-category="paneTor" - hidden="true"> - <html:h2 id="torPreferences-quickstart-header"/> - <description flex="1"> - <html:span id="torPreferences-quickstart-description"/> - </description> - <checkbox id="torPreferences-quickstart-toggle"/> -</groupbox> - -<!-- Bridges --> -<groupbox id="torPreferences-bridges-group" - data-category="paneTor" - hidden="true"> - <html:h2 id="torPreferences-bridges-header"/> - <description flex="1"> - <html:span id="torPreferences-bridges-description" class="tail-with-learn-more"/> - <label id="torPreferences-bridges-learnMore" class="learnMore text-link" is="text-link"/> - </description> - <checkbox id="torPreferences-bridges-toggle"/> - <radiogroup id="torPreferences-bridges-bridgeSelection"> - <hbox class="indent"> - <radio id="torPreferences-bridges-radioBuiltin"/> - <spacer flex="1"/> - <menulist id="torPreferences-bridges-builtinList" class="torMarginFix"> - <menupopup/> - </menulist> - </hbox> - <vbox class="indent"> - <hbox> - <radio id="torPreferences-bridges-radioRequestBridge"/> - <space flex="1"/> - <button id="torPreferences-bridges-buttonRequestBridge" class="torMarginFix"/> - </hbox> - <html:textarea - id="torPreferences-bridges-textareaRequestBridge" - class="indent torMarginFix" - multiline="true" - rows="3" - readonly="true"/> - </vbox> - <hbox class="indent" flex="1"> - <vbox flex="1"> - <radio id="torPreferences-bridges-radioProvideBridge"/> - <description id="torPreferences-bridges-descriptionProvideBridge" class="indent"/> - <html:textarea - id="torPreferences-bridges-textareaProvideBridge" - class="indent torMarginFix" - multiline="true" - rows="3"/> - </vbox> - </hbox> - </radiogroup> -</groupbox> - -<!-- Advanced --> -<groupbox id="torPreferences-advanced-group" - data-category="paneTor" - hidden="true"> - <html:h2 id="torPreferences-advanced-header"/> - <description flex="1"> - <html:span id="torPreferences-advanced-description" class="tail-with-learn-more"/> - <label id="torPreferences-advanced-learnMore" class="learnMore text-link" is="text-link" style="display:none"/> - </description> - <box id="torPreferences-advanced-grid"> - <!-- Local Proxy --> - <hbox class="torPreferences-advanced-checkbox-container"> - <checkbox id="torPreferences-advanced-toggleProxy"/> - </hbox> - <hbox class="indent" align="center"> - <label id="torPreferences-localProxy-type"/> - </hbox> - <hbox align="center"> - <spacer flex="1"/> - <menulist id="torPreferences-localProxy-builtinList" class="torMarginFix"> - <menupopup/> - </menulist> - </hbox> - <hbox class="indent" align="center"> - <label id="torPreferences-localProxy-address"/> - </hbox> - <hbox align="center"> - <html:input id="torPreferences-localProxy-textboxAddress" type="text" class="torMarginFix"/> - <label id="torPreferences-localProxy-port"/> - <!-- proxy-port-input class style pulled from preferences.css and used in the vanilla proxy setup menu --> - <html:input id="torPreferences-localProxy-textboxPort" class="proxy-port-input torMarginFix" hidespinbuttons="true" type="number" min="0" max="65535" maxlength="5"/> - </hbox> - <hbox class="indent" align="center"> - <label id="torPreferences-localProxy-username"/> - </hbox> - <hbox align="center"> - <html:input id="torPreferences-localProxy-textboxUsername" type="text" class="torMarginFix"/> - <label id="torPreferences-localProxy-password"/> - <html:input id="torPreferences-localProxy-textboxPassword" class="torMarginFix" type="password"/> - </hbox> - <!-- Firewall --> - <hbox class="torPreferences-advanced-checkbox-container"> - <checkbox id="torPreferences-advanced-toggleFirewall"/> - </hbox> - <hbox class="indent" align="center"> - <label id="torPreferences-advanced-allowedPorts"/> - </hbox> - <hbox align="center"> - <html:input id="torPreferences-advanced-textboxAllowedPorts" type="text" class="torMarginFix" value="80,443"/> - </hbox> - </box> - <hbox id="torPreferences-torDaemon-hbox" align="center"> - <label id="torPreferences-torLogs"/> - <spacer flex="1"/> - <button id="torPreferences-buttonTorLogs" class="torMarginFix"/> - </hbox> -</groupbox> -</html:template> \ No newline at end of file diff --git a/browser/components/torpreferences/content/torPreferences.css b/browser/components/torpreferences/content/torPreferences.css index b6eb0a740e5e4..31b6e29d679f3 100644 --- a/browser/components/torpreferences/content/torPreferences.css +++ b/browser/components/torpreferences/content/torPreferences.css @@ -1,9 +1,14 @@ @import url("chrome://branding/content/tor-styles.css");
-#category-tor > .category-icon { +#category-connection > .category-icon { list-style-image: url("chrome://browser/content/torpreferences/torPreferencesIcon.svg"); }
+html:dir(rtl) input[type="checkbox"].toggle-button::before { + /* For some reason, the rule from toggle-button.css is not applied... */ + scale: -1; +} + /* Connect Message Box */
#torPreferences-connectMessageBox { @@ -64,8 +69,7 @@
#torPreferences-connectMessageBox-message { line-height: 16px; - font-size: 1.11em; - padding-left: 8px!important; + padding-inline-start: 8px; }
#torPreferences-connectMessageBox-button { @@ -112,36 +116,302 @@ background-color: var(--purple-90); }
-/* Advanced Settings */ +/* Status */ +#torPreferences-status-box { + display: flex; + align-items: center; +}
-#torPreferences-advanced-grid { +#torPreferences-status-internet-icon, #torPreferences-status-tor-icon { + width: 18px; + height: 18px; + margin-inline-end: 8px; +} + +#torPreferences-status-internet-label, #torPreferences-status-tor-label { + font-weight: bold; +} + +#torPreferences-status-internet-icon { + list-style-image: url("chrome://devtools/skin/images/globe.svg"); +} + +#torPreferences-status-internet-statusIcon.online, +#torPreferences-status-internet-statusIcon.offline, +#torPreferences-status-tor-statusIcon { + margin-inline-start: 12px; + margin-inline-end: 9px; +} + +#torPreferences-status-internet-statusIcon.online, #torPreferences-status-tor-statusIcon.connected { + list-style-image: url("chrome://devtools/skin/images/check.svg"); + -moz-context-properties: fill; + fill: var(--purple-60); +} + +#torPreferences-status-internet-status { + margin-inline-end: 32px; +} + +#torPreferences-status-tor-icon { + list-style-image: url("chrome://browser/skin/onion.svg"); +} + +#torPreferences-status-internet-icon, #torPreferences-status-tor-icon { + -moz-context-properties: fill; + fill: var(--in-content-text-color); +} + +#torPreferences-status-tor-statusIcon, #torPreferences-status-internet-statusIcon.offline { + list-style-image: url("chrome://browser/skin/warning.svg"); +} + +#torPreferences-status-tor-statusIcon.blocked { + -moz-context-properties: fill; + fill: var(--red-60); +} + +/* Bridge settings */ +#torPreferences-bridges-location { + width: 280px; +} + +/* Bridge cards */ +:root { + --bridgeCard-animation-time: 0.25s; +} + +#torPreferences-currentBridges-cards { + /* The padding is needed because the mask-image creates an unexpected result + otherwise... */ + padding: 24px 4px; +} + +#torPreferences-currentBridges-cards.list-collapsed { + mask-image: linear-gradient(rgb(0, 0, 0), rgba(0, 0, 0, 0.1)); +} + +#torPreferences-currentBridges-cards.disabled { + opacity: 0.4; +} + +.torPreferences-bridgeCard { + padding: 16px 12px; + /* define border-radius here because of the transition */ + border-radius: 4px; + transition: margin var(--bridgeCard-animation-time), box-shadow 150ms; +} + +.torPreferences-bridgeCard.expanded { + margin: 12px 0; + background: var(--in-content-box-background); + box-shadow: var(--card-shadow); +} + +.torPreferences-bridgeCard:hover { + background: var(--in-content-box-background); + box-shadow: var(--card-shadow-hover); + cursor: pointer; +} + +.torPreferences-bridgeCard-heading { + display: flex; + align-items: center; +} + +.torPreferences-bridgeCard-id { + font-weight: 700; +} + +.torPreferences-bridgeCard-id .emoji { + margin-inline-start: 4px; + padding: 4px; + font-size: 20px; + border-radius: 4px; + background: var(--in-content-button-background-hover); +} + +.torPreferences-bridgeCard-headingAddr { + /* flex extends the element when needed, but without setting a width (any) the + overflow + ellipses does not work. */ + width: 20px; + flex: 1; + margin: 0 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.expanded .torPreferences-bridgeCard-headingAddr { + display: none; +} + +.torPreferences-bridgeCard-buttons { + display: flex; + align-items: center; + margin-inline-start: auto; + align-self: center; +} + +.torPreferences-bridgeCard-connectedBadge { + display: none; + padding: 8px 12px; + border-radius: 16px; + background: rgba(128, 0, 215, 0.1); + color: var(--purple-60); +} + +.currently-connected .torPreferences-bridgeCard-connectedBadge { + display: flex; +} + +.torPreferences-bridgeCard-connectedIcon { + margin-inline-start: 1px; + margin-inline-end: 7px; + list-style-image: url("chrome://devtools/skin/images/check.svg"); + -moz-context-properties: fill; + fill: var(--purple-60); +} + +.torPreferences-bridgeCard-options { + width: 24px; + min-width: 0; + height: 24px; + min-height: 0; + margin-inline-start: 8px; + padding: 1px; + background-image: url("chrome://global/skin/icons/more.svg"); + background-repeat: no-repeat; + background-position: center center; + fill: currentColor; + -moz-context-properties: fill; +} + +.torPreferences-bridgeCard-qrWrapper { + grid-area: bridge-qr; + display: flex; + flex-direction: column; +} + +.torPreferences-bridgeCard-qr { + width: 126px; + position: relative; +} + +.torPreferences-bridgeCard-qrCode { + width: 112px; + height: 112px; + /* Define these colors, as they will be passed to the QR code library */ + background: var(--in-content-box-background); + color: var(--in-content-text-color); +} + +.torPreferences-bridgeCard-qrOnionBox { + width: 28px; + height: 28px; + position: absolute; + top: 42px; + inset-inline-start: 42px; + background: var(--in-content-box-background); +} + +.torPreferences-bridgeCard-qrOnion { + width: 16px; + height: 16px; + position: absolute; + top: 48px; + inset-inline-start: 48px; + + mask: url("chrome://browser/skin/onion.svg"); + mask-repeat: no-repeat; + mask-size: 16px; + background: var(--in-content-text-color); +} + +.torPreferences-bridgeCard-qr:hover .torPreferences-bridgeCard-qrOnionBox { + background: var(--in-content-text-color); +} + +.torPreferences-bridgeCard-qr:hover .torPreferences-bridgeCard-qrOnion { + mask: url("chrome://global/skin/icons/search-glass.svg"); + background: var(--in-content-box-background); +} + +.torPreferences-bridgeCard-filler { + flex: 1; +} + +.torPreferences-bridgeCard-grid { + height: 0; /* We will set it in JS when expanding it! */ display: grid; - grid-template-columns: auto 1fr; + grid-template-rows: auto 1fr; + grid-template-columns: auto 1fr auto; + grid-template-areas: + 'bridge-qr bridge-share bridge-share' + 'bridge-qr bridge-address bridge-address' + 'bridge-qr bridge-learn-more bridge-copy'; + padding-top: 12px; + visibility: hidden; }
-.torPreferences-advanced-checkbox-container { - grid-column: 1 / 3; +.expanded .torPreferences-bridgeCard-grid { + visibility: visible; }
-#torPreferences-localProxy-textboxAddress, -#torPreferences-localProxy-textboxUsername, -#torPreferences-localProxy-textboxPassword, -#torPreferences-advanced-textboxAllowedPorts { - -moz-box-flex: 1; +.torPreferences-bridgeCard-grid.to-animate { + transition: height var(--bridgeCard-animation-time) ease-out, visibility var(--bridgeCard-animation-time); + overflow: hidden; +} + +.torPreferences-bridgeCard-share { + grid-area: bridge-share; +} + +.torPreferences-bridgeCard-addrBox { + grid-area: bridge-address; + display: flex; + align-items: center; + justify-content: center; + margin: 8px 0; +} + +.torPreferences-bridgeCard-addr { + width: 100%; +} + +.torPreferences-bridgeCard-leranMoreBox { + grid-area: bridge-learn-more; +} + +.torPreferences-bridgeCard-copy { + grid-area: bridge-copy; +} + +#torPreferences-bridgeCard-template { + display: none; +} + +/* Advanced Settings */ +#torPreferences-advanced-grid { + display: grid; + grid-template-columns: 1fr auto; }
-hbox#torPreferences-torDaemon-hbox { - margin-top: 20px; +#torPreferences-advanced-group button { + min-width: 150px; }
-description#torPreferences-requestBridge-description { - /*margin-bottom: 1em;*/ - min-height: 2em; +#torPreferences-advanced-hbox, #torPreferences-torDaemon-hbox { + padding-inline-end: 15px; +} + +h3#torPreferences-requestBridge-header { + margin: 0; }
image#torPreferences-requestBridge-captchaImage { - margin: 1em; - min-height: 125px; + margin: 16px 0 8px 0; + min-height: 140px; }
button#torPreferences-requestBridge-refreshCaptchaButton { @@ -160,6 +430,61 @@ dialog#torPreferences-requestBridge-dialog > hbox { margin-right : 4px; }
+/* Show bridge QR dialog */ +#bridgeQr-container { + position: relative; + height: 300px; +} + +#bridgeQr-target { + position: absolute; + width: 300px; + height: 300px; + left: calc(50% - 150px); + background: var(--in-content-box-background); + color: var(--in-content-text-color); +} + +#bridgeQr-onionBox { + position: absolute; + width: 70px; + height: 70px; + top: 115px; + left: calc(50% - 35px); + background-color: var(--in-content-box-background); +} + +#bridgeQr-onion { + position: absolute; + width: 38px; + height: 38px; + top: 131px; + left: calc(50% - 19px); + mask: url("chrome://browser/skin/onion.svg"); + mask-repeat: no-repeat; + mask-size: 38px; + background: var(--in-content-text-color); +} + +/* Builtin bridge dialog */ +#torPreferences-builtinBridge-header { + margin: 8px 0 10px 0; +} + +#torPreferences-builtinBridge-description { + margin-bottom: 18px; +} + +#torPreferences-builtinBridge-typeSelection { + margin-bottom: 16px; + min-height: 14em; /* Hack: make room for at least 4 lines of content for 3 types + 2 for spacing */ +} + +#torPreferences-builtinBridge-typeSelection radio label { + font-weight: 700; +} + +/* Request bridge dialog */ /* This hbox is hidden by css here by default so that the xul dialog allocates enough screen space for the error message @@ -178,6 +503,33 @@ groupbox#torPreferences-bridges-group textarea { overflow: auto; }
+/* Provide bridge dialog */ +#torPreferences-provideBridge-header { + margin-top: 8px; +} + +/* Connection settings dialog */ +#torPreferences-connection-header { + margin: 4px 0 14px 0; +} + +#torPreferences-connection-grid { + display: grid; + grid-template-columns: auto 1fr; +} + +.torPreferences-connection-checkbox-container { + grid-column: 1 / 3; +} + +#torPreferences-localProxy-textboxAddress, +#torPreferences-localProxy-textboxUsername, +#torPreferences-localProxy-textboxPassword, +#torPreferences-connection-textboxAllowedPorts { + -moz-box-flex: 1; +} + +/* Tor logs dialog */ textarea#torPreferences-torDialog-textarea { -moz-box-flex: 1; font-family: monospace; @@ -186,4 +538,4 @@ textarea#torPreferences-torDialog-textarea { overflow: auto; /* 10 lines */ min-height: 20em; -} \ No newline at end of file +} diff --git a/browser/components/torpreferences/jar.mn b/browser/components/torpreferences/jar.mn index 552c92b2feff6..ed3bb441084c9 100644 --- a/browser/components/torpreferences/jar.mn +++ b/browser/components/torpreferences/jar.mn @@ -1,10 +1,19 @@ browser.jar: + content/browser/torpreferences/bridgeQrDialog.xhtml (content/bridgeQrDialog.xhtml) + content/browser/torpreferences/bridgeQrDialog.jsm (content/bridgeQrDialog.jsm) + content/browser/torpreferences/builtinBridgeDialog.xhtml (content/builtinBridgeDialog.xhtml) + content/browser/torpreferences/builtinBridgeDialog.jsm (content/builtinBridgeDialog.jsm) + content/browser/torpreferences/connectionSettingsDialog.xhtml (content/connectionSettingsDialog.xhtml) + content/browser/torpreferences/connectionSettingsDialog.jsm (content/connectionSettingsDialog.jsm) + content/browser/torpreferences/network.svg (content/network.svg) + content/browser/torpreferences/provideBridgeDialog.xhtml (content/provideBridgeDialog.xhtml) + content/browser/torpreferences/provideBridgeDialog.jsm (content/provideBridgeDialog.jsm) content/browser/torpreferences/requestBridgeDialog.xhtml (content/requestBridgeDialog.xhtml) content/browser/torpreferences/requestBridgeDialog.jsm (content/requestBridgeDialog.jsm) - content/browser/torpreferences/torCategory.inc.xhtml (content/torCategory.inc.xhtml) + content/browser/torpreferences/connectionCategory.inc.xhtml (content/connectionCategory.inc.xhtml) content/browser/torpreferences/torLogDialog.jsm (content/torLogDialog.jsm) content/browser/torpreferences/torLogDialog.xhtml (content/torLogDialog.xhtml) - content/browser/torpreferences/torPane.js (content/torPane.js) - content/browser/torpreferences/torPane.xhtml (content/torPane.xhtml) + content/browser/torpreferences/connectionPane.js (content/connectionPane.js) + content/browser/torpreferences/connectionPane.xhtml (content/connectionPane.xhtml) content/browser/torpreferences/torPreferences.css (content/torPreferences.css) content/browser/torpreferences/torPreferencesIcon.svg (content/torPreferencesIcon.svg)