tor-commits
Threads by month
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
May 2021
- 20 participants
- 1973 discussions
[tor-browser/tor-browser-88.0.1-10.0-1] Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#tor
by sysrqb@torproject.org 04 May '21
by sysrqb@torproject.org 04 May '21
04 May '21
commit 59c4c25b796e86229be4efdd4e6a86ebe20b3236
Author: Richard Pospesel <richard(a)torproject.org>
Date: Mon Sep 16 15:25:39 2019 -0700
Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#tor
This patch adds a new about:preferences#tor page which allows modifying
bridge, proxy, and firewall settings from within Tor Browser. All of the
functionality present in tor-launcher's Network Configuration panel is
present:
- Setting built-in bridges
- Requesting bridges from BridgeDB via moat
- Using user-provided bridges
- Configuring SOCKS4, SOCKS5, and HTTP/HTTPS proxies
- Setting firewall ports
- Viewing and Copying Tor's logs
- The Networking Settings in General preferences has been removed
---
browser/components/moz.build | 1 +
browser/components/preferences/main.inc.xhtml | 55 --
browser/components/preferences/main.js | 14 -
browser/components/preferences/preferences.js | 9 +
browser/components/preferences/preferences.xhtml | 5 +
browser/components/preferences/privacy.js | 1 +
.../torpreferences/content/parseFunctions.jsm | 89 +++
.../torpreferences/content/requestBridgeDialog.jsm | 204 +++++
.../content/requestBridgeDialog.xhtml | 35 +
.../torpreferences/content/torBridgeSettings.jsm | 325 ++++++++
.../torpreferences/content/torCategory.inc.xhtml | 9 +
.../torpreferences/content/torFirewallSettings.jsm | 72 ++
.../torpreferences/content/torLogDialog.jsm | 66 ++
.../torpreferences/content/torLogDialog.xhtml | 23 +
.../components/torpreferences/content/torPane.js | 857 +++++++++++++++++++++
.../torpreferences/content/torPane.xhtml | 123 +++
.../torpreferences/content/torPreferences.css | 77 ++
.../torpreferences/content/torPreferencesIcon.svg | 5 +
.../torpreferences/content/torProxySettings.jsm | 245 ++++++
browser/components/torpreferences/jar.mn | 14 +
browser/components/torpreferences/moz.build | 1 +
browser/modules/BridgeDB.jsm | 110 +++
browser/modules/TorProtocolService.jsm | 212 +++++
browser/modules/moz.build | 2 +
24 files changed, 2485 insertions(+), 69 deletions(-)
diff --git a/browser/components/moz.build b/browser/components/moz.build
index ef09055b990a..e99fa19d896a 100644
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -55,6 +55,7 @@ DIRS += [
"syncedtabs",
"uitour",
"urlbar",
+ "torpreferences",
"translation",
]
diff --git a/browser/components/preferences/main.inc.xhtml b/browser/components/preferences/main.inc.xhtml
index 2d55e165c5ba..78d0cea8b8a2 100644
--- a/browser/components/preferences/main.inc.xhtml
+++ b/browser/components/preferences/main.inc.xhtml
@@ -665,59 +665,4 @@
<label id="cfrFeaturesLearnMore" class="learnMore" data-l10n-id="browsing-cfr-recommendations-learn-more" is="text-link"/>
</hbox>
</groupbox>
-
-<hbox id="networkProxyCategory"
- class="subcategory"
- hidden="true"
- data-category="paneGeneral">
- <html:h1 data-l10n-id="network-settings-title"/>
-</hbox>
-
-<!-- Network Settings-->
-<groupbox id="connectionGroup" data-category="paneGeneral" hidden="true">
- <label class="search-header" hidden="true"><html:h2 data-l10n-id="network-settings-title"/></label>
-
- <hbox align="center">
- <hbox align="center" flex="1">
- <description id="connectionSettingsDescription" control="connectionSettings"/>
- <spacer width="5"/>
- <label id="connectionSettingsLearnMore" class="learnMore" is="text-link"
- data-l10n-id="network-proxy-connection-learn-more">
- </label>
- <separator orient="vertical"/>
- </hbox>
-
- <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
- <hbox>
- <button id="connectionSettings"
- is="highlightable-button"
- class="accessory-button"
- data-l10n-id="network-proxy-connection-settings"
- searchkeywords="doh trr"
- search-l10n-ids="
- connection-window.title,
- connection-proxy-option-no.label,
- connection-proxy-option-auto.label,
- connection-proxy-option-system.label,
- connection-proxy-option-manual.label,
- connection-proxy-http,
- connection-proxy-https,
- connection-proxy-ftp,
- connection-proxy-http-port,
- connection-proxy-socks,
- connection-proxy-socks4,
- connection-proxy-socks5,
- connection-proxy-noproxy,
- connection-proxy-noproxy-desc,
- connection-proxy-http-sharing.label,
- connection-proxy-autotype.label,
- connection-proxy-reload.label,
- connection-proxy-autologin.label,
- connection-proxy-socks-remote-dns.label,
- connection-dns-over-https.label,
- connection-dns-over-https-url-custom.label,
- " />
- </hbox>
- </hbox>
-</groupbox>
</html:template>
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
index b5a192fc274a..6daf5b329878 100644
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -364,15 +364,6 @@ var gMainPane = {
});
this.updatePerformanceSettingsBox({ duringChangeEvent: false });
this.displayUseSystemLocale();
- let connectionSettingsLink = document.getElementById(
- "connectionSettingsLearnMore"
- );
- let connectionSettingsUrl =
- Services.urlFormatter.formatURLPref("app.support.baseURL") +
- "prefs-connection-settings";
- connectionSettingsLink.setAttribute("href", connectionSettingsUrl);
- this.updateProxySettingsUI();
- initializeProxyUI(gMainPane);
if (Services.prefs.getBoolPref("intl.multilingual.enabled")) {
gMainPane.initBrowserLocale();
@@ -506,11 +497,6 @@ var gMainPane = {
"change",
gMainPane.updateHardwareAcceleration.bind(gMainPane)
);
- setEventListener(
- "connectionSettings",
- "command",
- gMainPane.showConnections
- );
setEventListener(
"browserContainersCheckbox",
"command",
diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js
index 91e9e469cea2..a89fddd0306d 100644
--- a/browser/components/preferences/preferences.js
+++ b/browser/components/preferences/preferences.js
@@ -13,6 +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 */
"use strict";
@@ -136,6 +137,14 @@ function init_all() {
register_module("paneSync", gSyncPane);
}
register_module("paneSearchResults", gSearchResultsPane);
+ if (gTorPane.enabled) {
+ document.getElementById("category-tor").hidden = false;
+ register_module("paneTor", gTorPane);
+ } else {
+ // Remove the pane from the DOM so it doesn't get incorrectly included in search results.
+ document.getElementById("template-paneTor").remove();
+ }
+
gSearchResultsPane.init();
gMainPane.preInit();
diff --git a/browser/components/preferences/preferences.xhtml b/browser/components/preferences/preferences.xhtml
index c1322ea10357..fd4098471966 100644
--- a/browser/components/preferences/preferences.xhtml
+++ b/browser/components/preferences/preferences.xhtml
@@ -13,6 +13,7 @@
<?xml-stylesheet href="chrome://browser/skin/preferences/containers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/privacy.css"?>
<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelPreferences.css"?>
+<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?>
<!DOCTYPE html [
<!ENTITY % aboutTorDTD SYSTEM "chrome://torbutton/locale/aboutTor.dtd">
@@ -155,6 +156,9 @@
<image class="category-icon"/>
<label class="category-name" flex="1" data-l10n-id="pane-experimental-title"></label>
</richlistitem>
+
+#include ../torpreferences/content/torCategory.inc.xhtml
+
</richlistbox>
<spacer flex="1"/>
@@ -208,6 +212,7 @@
#include containers.inc.xhtml
#include sync.inc.xhtml
#include experimental.inc.xhtml
+#include ../torpreferences/content/torPane.xhtml
</vbox>
</vbox>
</vbox>
diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js
index 8401d4d101e4..1c5f31934d82 100644
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -80,6 +80,7 @@ XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function() {
}
});
+// TODO: module import via ChromeUtils.defineModuleGetter
XPCOMUtils.defineLazyScriptGetter(
this,
["SecurityLevelPreferences"],
diff --git a/browser/components/torpreferences/content/parseFunctions.jsm b/browser/components/torpreferences/content/parseFunctions.jsm
new file mode 100644
index 000000000000..954759de63a5
--- /dev/null
+++ b/browser/components/torpreferences/content/parseFunctions.jsm
@@ -0,0 +1,89 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "parsePort",
+ "parseAddrPort",
+ "parseUsernamePassword",
+ "parseAddrPortList",
+ "parseBridgeStrings",
+ "parsePortList",
+];
+
+// expects a string representation of an integer from 1 to 65535
+let parsePort = function(aPort) {
+ // ensure port string is a valid positive integer
+ const validIntRegex = /^[0-9]+$/;
+ if (!validIntRegex.test(aPort)) {
+ throw new Error(`Invalid PORT string : '${aPort}'`);
+ }
+
+ // ensure port value is on valid range
+ let port = Number.parseInt(aPort);
+ if (port < 1 || port > 65535) {
+ throw new Error(
+ `Invalid PORT value, needs to be on range [1,65535] : '${port}'`
+ );
+ }
+
+ return port;
+};
+// expects a string in the format: "ADDRESS:PORT"
+let parseAddrPort = function(aAddrColonPort) {
+ let tokens = aAddrColonPort.split(":");
+ if (tokens.length != 2) {
+ throw new Error(`Invalid ADDRESS:PORT string : '${aAddrColonPort}'`);
+ }
+ let address = tokens[0];
+ let port = parsePort(tokens[1]);
+ return [address, port];
+};
+
+// expects a string in the format: "USERNAME:PASSWORD"
+// split on the first colon and any subsequent go into password
+let parseUsernamePassword = function(aUsernameColonPassword) {
+ let colonIndex = aUsernameColonPassword.indexOf(":");
+ if (colonIndex < 0) {
+ // we don't log the contents of the potentially password containing string
+ throw new Error("Invalid USERNAME:PASSWORD string");
+ }
+
+ let username = aUsernameColonPassword.substring(0, colonIndex);
+ let password = aUsernameColonPassword.substring(colonIndex + 1);
+
+ return [username, password];
+};
+
+// expects a string in the format: ADDRESS:PORT,ADDRESS:PORT,...
+// returns array of ports (as ints)
+let parseAddrPortList = function(aAddrPortList) {
+ let addrPorts = aAddrPortList.split(",");
+ // parse ADDRESS:PORT string and only keep the port (second element in returned array)
+ let retval = addrPorts.map(addrPort => parseAddrPort(addrPort)[1]);
+ return retval;
+};
+
+// expects a '/n' or '/r/n' delimited bridge string, which we split and trim
+// each bridge string can also optionally have 'bridge' at the beginning ie:
+// bridge $(type) $(address):$(port) $(certificate)
+// we strip out the 'bridge' prefix here
+let parseBridgeStrings = function(aBridgeStrings) {
+
+ // replace carriage returns ('\r') with new lines ('\n')
+ aBridgeStrings = aBridgeStrings.replace(/\r/g, "\n");
+ // then replace contiguous new lines ('\n') with a single one
+ aBridgeStrings = aBridgeStrings.replace(/[\n]+/g, "\n");
+
+ // split on the newline and for each bridge string: trim, remove starting 'bridge' string
+ // finally discard entries that are empty strings; empty strings could occur if we receive
+ // a new line containing only whitespace
+ let splitStrings = aBridgeStrings.split("\n");
+ return splitStrings.map(val => val.trim().replace(/^bridge\s+/i, ""))
+ .filter(bridgeString => bridgeString != "");
+};
+
+// expecting a ',' delimited list of ints with possible white space between
+// returns an array of ints
+let parsePortList = function(aPortListString) {
+ let splitStrings = aPortListString.split(",");
+ return splitStrings.map(val => parsePort(val.trim()));
+};
diff --git a/browser/components/torpreferences/content/requestBridgeDialog.jsm b/browser/components/torpreferences/content/requestBridgeDialog.jsm
new file mode 100644
index 000000000000..807d46cdfb18
--- /dev/null
+++ b/browser/components/torpreferences/content/requestBridgeDialog.jsm
@@ -0,0 +1,204 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["RequestBridgeDialog"];
+
+const { BridgeDB } = ChromeUtils.import("resource:///modules/BridgeDB.jsm");
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+class RequestBridgeDialog {
+ constructor() {
+ this._dialog = null;
+ this._submitButton = null;
+ this._dialogDescription = null;
+ this._captchaImage = null;
+ this._captchaEntryTextbox = null;
+ this._captchaRefreshButton = null;
+ this._incorrectCaptchaHbox = null;
+ this._incorrectCaptchaLabel = null;
+ this._bridges = [];
+ this._proxyURI = null;
+ }
+
+ static get selectors() {
+ return {
+ submitButton:
+ "accept" /* not really a selector but a key for dialog's getButton */,
+ dialogDescription: "description#torPreferences-requestBridge-description",
+ captchaImage: "image#torPreferences-requestBridge-captchaImage",
+ captchaEntryTextbox: "input#torPreferences-requestBridge-captchaTextbox",
+ refreshCaptchaButton:
+ "button#torPreferences-requestBridge-refreshCaptchaButton",
+ incorrectCaptchaHbox:
+ "hbox#torPreferences-requestBridge-incorrectCaptchaHbox",
+ incorrectCaptchaLabel:
+ "label#torPreferences-requestBridge-incorrectCaptchaError",
+ };
+ }
+
+ _populateXUL(dialog) {
+ const selectors = RequestBridgeDialog.selectors;
+
+ this._dialog = dialog;
+ const dialogWin = dialog.parentElement;
+ dialogWin.setAttribute(
+ "title",
+ TorStrings.settings.requestBridgeDialogTitle
+ );
+ // user may have opened a Request Bridge dialog in another tab, so update the
+ // CAPTCHA image or close out the dialog if we have a bridge list
+ this._dialog.addEventListener("focusin", () => {
+ const uri = BridgeDB.currentCaptchaImage;
+ const bridges = BridgeDB.currentBridges;
+
+ // new captcha image
+ if (uri) {
+ this._setcaptchaImage(uri);
+ } else if (bridges) {
+ this._bridges = bridges;
+ this._submitButton.disabled = false;
+ this._dialog.cancelDialog();
+ }
+ });
+
+ this._submitButton = this._dialog.getButton(selectors.submitButton);
+ this._submitButton.setAttribute("label", TorStrings.settings.submitCaptcha);
+ this._submitButton.disabled = true;
+ this._dialog.addEventListener("dialogaccept", e => {
+ e.preventDefault();
+ this.onSubmitCaptcha();
+ });
+
+ this._dialogDescription = this._dialog.querySelector(
+ selectors.dialogDescription
+ );
+ this._dialogDescription.textContent =
+ TorStrings.settings.contactingBridgeDB;
+
+ this._captchaImage = this._dialog.querySelector(selectors.captchaImage);
+
+ // request captcha from bridge db
+ BridgeDB.requestNewCaptchaImage(this._proxyURI).then(uri => {
+ this._setcaptchaImage(uri);
+ });
+
+ this._captchaEntryTextbox = this._dialog.querySelector(
+ selectors.captchaEntryTextbox
+ );
+ this._captchaEntryTextbox.setAttribute(
+ "placeholder",
+ TorStrings.settings.captchaTextboxPlaceholder
+ );
+ this._captchaEntryTextbox.disabled = true;
+ // disable submit if entry textbox is empty
+ this._captchaEntryTextbox.oninput = () => {
+ this._submitButton.disabled = this._captchaEntryTextbox.value == "";
+ };
+
+ this._captchaRefreshButton = this._dialog.querySelector(
+ selectors.refreshCaptchaButton
+ );
+ this._captchaRefreshButton.disabled = true;
+
+ this._incorrectCaptchaHbox = this._dialog.querySelector(
+ selectors.incorrectCaptchaHbox
+ );
+ this._incorrectCaptchaLabel = this._dialog.querySelector(
+ selectors.incorrectCaptchaLabel
+ );
+ this._incorrectCaptchaLabel.setAttribute(
+ "value",
+ TorStrings.settings.incorrectCaptcha
+ );
+
+ return true;
+ }
+
+ _setcaptchaImage(uri) {
+ if (uri != this._captchaImage.src) {
+ this._captchaImage.src = uri;
+ this._dialogDescription.textContent = TorStrings.settings.solveTheCaptcha;
+ this._setUIDisabled(false);
+ this._captchaEntryTextbox.focus();
+ this._captchaEntryTextbox.select();
+ }
+ }
+
+ _setUIDisabled(disabled) {
+ this._submitButton.disabled = this._captchaGuessIsEmpty() || disabled;
+ this._captchaEntryTextbox.disabled = disabled;
+ this._captchaRefreshButton.disabled = disabled;
+ }
+
+ _captchaGuessIsEmpty() {
+ return this._captchaEntryTextbox.value == "";
+ }
+
+ init(window, dialog) {
+ // defer to later until firefox has populated the dialog with all our elements
+ window.setTimeout(() => {
+ this._populateXUL(dialog);
+ }, 0);
+ }
+
+ close() {
+ BridgeDB.close();
+ }
+
+ /*
+ Event Handlers
+ */
+ onSubmitCaptcha() {
+ let captchaText = this._captchaEntryTextbox.value.trim();
+ // noop if the field is empty
+ if (captchaText == "") {
+ return;
+ }
+
+ // freeze ui while we make request
+ this._setUIDisabled(true);
+ this._incorrectCaptchaHbox.style.visibility = "hidden";
+
+ BridgeDB.submitCaptchaGuess(captchaText)
+ .then(aBridges => {
+ this._bridges = aBridges;
+
+ this._submitButton.disabled = false;
+ // This was successful, but use cancelDialog() to close, since
+ // we intercept the `dialogaccept` event.
+ this._dialog.cancelDialog();
+ })
+ .catch(aError => {
+ this._bridges = [];
+ this._setUIDisabled(false);
+ this._incorrectCaptchaHbox.style.visibility = "visible";
+ });
+ }
+
+ onRefreshCaptcha() {
+ this._setUIDisabled(true);
+ this._captchaImage.src = "";
+ this._dialogDescription.textContent =
+ TorStrings.settings.contactingBridgeDB;
+ this._captchaEntryTextbox.value = "";
+ this._incorrectCaptchaHbox.style.visibility = "hidden";
+
+ BridgeDB.requestNewCaptchaImage(this._proxyURI).then(uri => {
+ this._setcaptchaImage(uri);
+ });
+ }
+
+ openDialog(gSubDialog, aProxyURI, aCloseCallback) {
+ this._proxyURI = aProxyURI;
+ gSubDialog.open(
+ "chrome://browser/content/torpreferences/requestBridgeDialog.xhtml",
+ {
+ features: "resizable=yes",
+ closingCallback: () => {
+ this.close();
+ aCloseCallback(this._bridges);
+ }
+ },
+ this,
+ );
+ }
+}
diff --git a/browser/components/torpreferences/content/requestBridgeDialog.xhtml b/browser/components/torpreferences/content/requestBridgeDialog.xhtml
new file mode 100644
index 000000000000..64c4507807fb
--- /dev/null
+++ b/browser/components/torpreferences/content/requestBridgeDialog.xhtml
@@ -0,0 +1,35 @@
+<?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">
+<dialog id="torPreferences-requestBridge-dialog"
+ buttons="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>
+ <!-- init to transparent 400x125 png -->
+ <image id="torPreferences-requestBridge-captchaImage" flex="1"/>
+ <hbox id="torPreferences-requestBridge-inputHbox">
+ <html:input id="torPreferences-requestBridge-captchaTextbox" type="text" style="-moz-box-flex: 1;"/>
+ <button id="torPreferences-requestBridge-refreshCaptchaButton"
+ image="chrome://browser/skin/reload.svg"
+ oncommand="requestBridgeDialog.onRefreshCaptcha();"/>
+ </hbox>
+ <hbox id="torPreferences-requestBridge-incorrectCaptchaHbox" align="center">
+ <image id="torPreferences-requestBridge-errorIcon" />
+ <label id="torPreferences-requestBridge-incorrectCaptchaError" flex="1"/>
+ </hbox>
+ <script type="application/javascript"><![CDATA[
+ "use strict";
+
+ let requestBridgeDialog = window.arguments[0];
+ let dialog = document.getElementById("torPreferences-requestBridge-dialog");
+ requestBridgeDialog.init(window, dialog);
+ ]]></script>
+</dialog>
+</window>
\ No newline at end of file
diff --git a/browser/components/torpreferences/content/torBridgeSettings.jsm b/browser/components/torpreferences/content/torBridgeSettings.jsm
new file mode 100644
index 000000000000..ceb61d3ec972
--- /dev/null
+++ b/browser/components/torpreferences/content/torBridgeSettings.jsm
@@ -0,0 +1,325 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "TorBridgeSource",
+ "TorBridgeSettings",
+ "makeTorBridgeSettingsNone",
+ "makeTorBridgeSettingsBuiltin",
+ "makeTorBridgeSettingsBridgeDB",
+ "makeTorBridgeSettingsUserProvided",
+];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+const TorBridgeSource = {
+ NONE: "NONE",
+ BUILTIN: "BUILTIN",
+ BRIDGEDB: "BRIDGEDB",
+ USERPROVIDED: "USERPROVIDED",
+};
+
+class TorBridgeSettings {
+ constructor() {
+ this._bridgeSource = TorBridgeSource.NONE;
+ this._selectedDefaultBridgeType = null;
+ this._bridgeStrings = [];
+ }
+
+ get selectedDefaultBridgeType() {
+ if (this._bridgeSource == TorBridgeSource.BUILTIN) {
+ return this._selectedDefaultBridgeType;
+ }
+ return undefined;
+ }
+
+ get bridgeSource() {
+ return this._bridgeSource;
+ }
+
+ // for display
+ get bridgeStrings() {
+ return this._bridgeStrings.join("\n");
+ }
+
+ // raw
+ get bridgeStringsArray() {
+ return this._bridgeStrings;
+ }
+
+ static get defaultBridgeTypes() {
+ if (TorBridgeSettings._defaultBridgeTypes) {
+ return TorBridgeSettings._defaultBridgeTypes;
+ }
+
+ let bridgeListBranch = Services.prefs.getBranch(
+ TorStrings.preferenceBranches.defaultBridge
+ );
+ let bridgePrefs = bridgeListBranch.getChildList("", {});
+
+ // an unordered set for shoving bridge types into
+ let bridgeTypes = new Set();
+ // look for keys ending in ".N" and treat string before that as the bridge type
+ const pattern = /\.[0-9]+$/;
+ for (const key of bridgePrefs) {
+ const offset = key.search(pattern);
+ if (offset != -1) {
+ const bt = key.substring(0, offset);
+ bridgeTypes.add(bt);
+ }
+ }
+
+ // recommended bridge type goes first in the list
+ let recommendedBridgeType = Services.prefs.getCharPref(
+ TorStrings.preferenceKeys.recommendedBridgeType,
+ null
+ );
+
+ let retval = [];
+ if (recommendedBridgeType && bridgeTypes.has(recommendedBridgeType)) {
+ retval.push(recommendedBridgeType);
+ }
+
+ for (const bridgeType of bridgeTypes.values()) {
+ if (bridgeType != recommendedBridgeType) {
+ retval.push(bridgeType);
+ }
+ }
+
+ // cache off
+ TorBridgeSettings._defaultBridgeTypes = retval;
+ return retval;
+ }
+
+ _readDefaultBridges(aBridgeType) {
+ let bridgeBranch = Services.prefs.getBranch(
+ TorStrings.preferenceBranches.defaultBridge
+ );
+ let bridgeBranchPrefs = bridgeBranch.getChildList("", {});
+
+ let retval = [];
+
+ // regex matches against strings ending in ".N" where N is a positive integer
+ let pattern = /\.[0-9]+$/;
+ for (const key of bridgeBranchPrefs) {
+ // verify the location of the match is the correct offset required for aBridgeType
+ // to fit, and that the string begins with aBridgeType
+ if (
+ key.search(pattern) == aBridgeType.length &&
+ key.startsWith(aBridgeType)
+ ) {
+ let bridgeStr = bridgeBranch.getCharPref(key);
+ retval.push(bridgeStr);
+ }
+ }
+
+ // fisher-yates shuffle
+ // shuffle so that Tor Browser users don't all try the built-in bridges in the same order
+ for (let i = retval.length - 1; i > 0; --i) {
+ // number n such that 0.0 <= n < 1.0
+ const n = Math.random();
+ // integer j such that 0 <= j <= i
+ const j = Math.floor(n * (i + 1));
+
+ // swap values at indices i and j
+ const tmp = retval[i];
+ retval[i] = retval[j];
+ retval[j] = tmp;
+ }
+
+ return retval;
+ }
+
+ _readBridgeDBBridges() {
+ let bridgeBranch = Services.prefs.getBranch(
+ `${TorStrings.preferenceBranches.bridgeDBBridges}`
+ );
+ let bridgeBranchPrefs = bridgeBranch.getChildList("", {});
+ // the child prefs do not come in any particular order so sort the keys
+ // so the values can be compared to what we get out off torrc
+ bridgeBranchPrefs.sort();
+
+ // just assume all of the prefs under the parent point to valid bridge string
+ let retval = bridgeBranchPrefs.map(key =>
+ bridgeBranch.getCharPref(key).trim()
+ );
+
+ return retval;
+ }
+
+ _readTorrcBridges() {
+ let bridgeList = TorProtocolService.readStringArraySetting(
+ TorStrings.configKeys.bridgeList
+ );
+
+ let retval = [];
+ for (const line of bridgeList) {
+ let trimmedLine = line.trim();
+ if (trimmedLine) {
+ retval.push(trimmedLine);
+ }
+ }
+
+ return retval;
+ }
+
+ // analagous to initBridgeSettings()
+ readSettings() {
+ // restore to defaults
+ this._bridgeSource = TorBridgeSource.NONE;
+ this._selectedDefaultBridgeType = null;
+ this._bridgeStrings = [];
+
+ // So the way tor-launcher determines the origin of the configured bridges is a bit
+ // weird and depends on inferring our scenario based on some firefox prefs and the
+ // relationship between the saved list of bridges in about:config vs the list saved in torrc
+
+ // first off, if "extensions.torlauncher.default_bridge_type" is set to one of our
+ // builtin default types (obfs4, meek-azure, snowflake, etc) then we provide the
+ // bridges in "extensions.torlauncher.default_bridge.*" (filtered by our default_bridge_type)
+
+ // next, we compare the list of bridges saved in torrc to the bridges stored in the
+ // "extensions.torlauncher.bridgedb_bridge."" branch. If they match *exactly* then we assume
+ // the bridges were retrieved from BridgeDB and use those. If the torrc list is empty then we know
+ // we have no bridge settings
+
+ // finally, if none of the previous conditions are not met, it is assumed the bridges stored in
+ // torrc are user-provided
+
+ // what we should(?) do once we excise tor-launcher entirely is explicitly store an int/enum in
+ // about:config that tells us which scenario we are in so we don't have to guess
+
+ let defaultBridgeType = Services.prefs.getCharPref(
+ TorStrings.preferenceKeys.defaultBridgeType,
+ null
+ );
+
+ // check if source is BUILTIN
+ if (defaultBridgeType) {
+ this._bridgeStrings = this._readDefaultBridges(defaultBridgeType);
+ this._bridgeSource = TorBridgeSource.BUILTIN;
+ this._selectedDefaultBridgeType = defaultBridgeType;
+ return;
+ }
+
+ let torrcBridges = this._readTorrcBridges();
+
+ // no stored bridges means no bridge is in use
+ if (torrcBridges.length == 0) {
+ this._bridgeStrings = [];
+ this._bridgeSource = TorBridgeSource.NONE;
+ return;
+ }
+
+ let bridgedbBridges = this._readBridgeDBBridges();
+
+ // if these two lists are equal then we got our bridges from bridgedb
+ // ie: same element in identical order
+ let arraysEqual = (left, right) => {
+ if (left.length != right.length) {
+ return false;
+ }
+ const length = left.length;
+ for (let i = 0; i < length; ++i) {
+ if (left[i] != right[i]) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ // agreement between prefs and torrc means bridgedb bridges
+ if (arraysEqual(torrcBridges, bridgedbBridges)) {
+ this._bridgeStrings = torrcBridges;
+ this._bridgeSource = TorBridgeSource.BRIDGEDB;
+ return;
+ }
+
+ // otherwise they must be user provided
+ this._bridgeStrings = torrcBridges;
+ this._bridgeSource = TorBridgeSource.USERPROVIDED;
+ }
+
+ writeSettings() {
+ let settingsObject = new Map();
+
+ // init tor bridge settings to null
+ settingsObject.set(TorStrings.configKeys.useBridges, null);
+ settingsObject.set(TorStrings.configKeys.bridgeList, null);
+
+ // clear bridge related firefox prefs
+ Services.prefs.setCharPref(TorStrings.preferenceKeys.defaultBridgeType, "");
+ let bridgeBranch = Services.prefs.getBranch(
+ `${TorStrings.preferenceBranches.bridgeDBBridges}`
+ );
+ let bridgeBranchPrefs = bridgeBranch.getChildList("", {});
+ for (const pref of bridgeBranchPrefs) {
+ Services.prefs.clearUserPref(
+ `${TorStrings.preferenceBranches.bridgeDBBridges}${pref}`
+ );
+ }
+
+ switch (this._bridgeSource) {
+ case TorBridgeSource.BUILTIN:
+ // set builtin bridge type to use in prefs
+ Services.prefs.setCharPref(
+ TorStrings.preferenceKeys.defaultBridgeType,
+ this._selectedDefaultBridgeType
+ );
+ break;
+ case TorBridgeSource.BRIDGEDB:
+ // save bridges off to prefs
+ for (let i = 0; i < this.bridgeStringsArray.length; ++i) {
+ Services.prefs.setCharPref(
+ `${TorStrings.preferenceBranches.bridgeDBBridges}${i}`,
+ this.bridgeStringsArray[i]
+ );
+ }
+ break;
+ }
+
+ // write over our bridge list if bridges are enabled
+ if (this._bridgeSource != TorBridgeSource.NONE) {
+ settingsObject.set(TorStrings.configKeys.useBridges, true);
+ settingsObject.set(
+ TorStrings.configKeys.bridgeList,
+ this.bridgeStringsArray
+ );
+ }
+ TorProtocolService.writeSettings(settingsObject);
+ }
+}
+
+function makeTorBridgeSettingsNone() {
+ return new TorBridgeSettings();
+}
+
+function makeTorBridgeSettingsBuiltin(aBridgeType) {
+ let retval = new TorBridgeSettings();
+ retval._bridgeSource = TorBridgeSource.BUILTIN;
+ retval._selectedDefaultBridgeType = aBridgeType;
+ retval._bridgeStrings = retval._readDefaultBridges(aBridgeType);
+
+ return retval;
+}
+
+function makeTorBridgeSettingsBridgeDB(aBridges) {
+ let retval = new TorBridgeSettings();
+ retval._bridgeSource = TorBridgeSource.BRIDGEDB;
+ retval._selectedDefaultBridgeType = null;
+ retval._bridgeStrings = aBridges;
+
+ return retval;
+}
+
+function makeTorBridgeSettingsUserProvided(aBridges) {
+ let retval = new TorBridgeSettings();
+ retval._bridgeSource = TorBridgeSource.USERPROVIDED;
+ retval._selectedDefaultBridgeType = null;
+ retval._bridgeStrings = aBridges;
+
+ return retval;
+}
diff --git a/browser/components/torpreferences/content/torCategory.inc.xhtml b/browser/components/torpreferences/content/torCategory.inc.xhtml
new file mode 100644
index 000000000000..abe56200f571
--- /dev/null
+++ b/browser/components/torpreferences/content/torCategory.inc.xhtml
@@ -0,0 +1,9 @@
+<richlistitem id="category-tor"
+ class="category"
+ value="paneTor"
+ helpTopic="prefs-tor"
+ align="center"
+ hidden="true">
+ <image class="category-icon"/>
+ <label id="torPreferences-labelCategory" class="category-name" flex="1" value="Tor"/>
+</richlistitem>
diff --git a/browser/components/torpreferences/content/torFirewallSettings.jsm b/browser/components/torpreferences/content/torFirewallSettings.jsm
new file mode 100644
index 000000000000..e77f18ef2fae
--- /dev/null
+++ b/browser/components/torpreferences/content/torFirewallSettings.jsm
@@ -0,0 +1,72 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "TorFirewallSettings",
+ "makeTorFirewallSettingsNone",
+ "makeTorFirewallSettingsCustom",
+];
+
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+const { parseAddrPortList } = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/parseFunctions.jsm"
+);
+
+class TorFirewallSettings {
+ constructor() {
+ this._allowedPorts = [];
+ }
+
+ get portsConfigurationString() {
+ let portStrings = this._allowedPorts.map(port => `*:${port}`);
+ return portStrings.join(",");
+ }
+
+ get commaSeparatedListString() {
+ return this._allowedPorts.join(",");
+ }
+
+ get hasPorts() {
+ return this._allowedPorts.length > 0;
+ }
+
+ readSettings() {
+ let addressPortList = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.reachableAddresses
+ );
+
+ let allowedPorts = [];
+ if (addressPortList) {
+ allowedPorts = parseAddrPortList(addressPortList);
+ }
+ this._allowedPorts = allowedPorts;
+ }
+
+ writeSettings() {
+ let settingsObject = new Map();
+
+ // init to null so Tor daemon resets if no ports
+ settingsObject.set(TorStrings.configKeys.reachableAddresses, null);
+
+ if (this._allowedPorts.length > 0) {
+ settingsObject.set(
+ TorStrings.configKeys.reachableAddresses,
+ this.portsConfigurationString
+ );
+ }
+
+ TorProtocolService.writeSettings(settingsObject);
+ }
+}
+
+function makeTorFirewallSettingsNone() {
+ return new TorFirewallSettings();
+}
+
+function makeTorFirewallSettingsCustom(aPortsList) {
+ let retval = new TorFirewallSettings();
+ retval._allowedPorts = aPortsList;
+ return retval;
+}
diff --git a/browser/components/torpreferences/content/torLogDialog.jsm b/browser/components/torpreferences/content/torLogDialog.jsm
new file mode 100644
index 000000000000..ecc684d878c2
--- /dev/null
+++ b/browser/components/torpreferences/content/torLogDialog.jsm
@@ -0,0 +1,66 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["TorLogDialog"];
+
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+class TorLogDialog {
+ constructor() {
+ this._dialog = null;
+ this._logTextarea = null;
+ this._copyLogButton = null;
+ }
+
+ static get selectors() {
+ return {
+ copyLogButton: "extra1",
+ logTextarea: "textarea#torPreferences-torDialog-textarea",
+ };
+ }
+
+ _populateXUL(aDialog) {
+ this._dialog = aDialog;
+ const dialogWin = this._dialog.parentElement;
+ dialogWin.setAttribute("title", TorStrings.settings.torLogDialogTitle);
+
+ this._logTextarea = this._dialog.querySelector(
+ TorLogDialog.selectors.logTextarea
+ );
+
+ this._copyLogButton = this._dialog.getButton(
+ TorLogDialog.selectors.copyLogButton
+ );
+ this._copyLogButton.setAttribute("label", TorStrings.settings.copyLog);
+ this._copyLogButton.addEventListener("command", () => {
+ this.copyTorLog();
+ });
+
+ this._logTextarea.value = TorProtocolService.getLog();
+ }
+
+ init(window, aDialog) {
+ // defer to later until firefox has populated the dialog with all our elements
+ window.setTimeout(() => {
+ this._populateXUL(aDialog);
+ }, 0);
+ }
+
+ copyTorLog() {
+ // Copy tor log messages to the system clipboard.
+ let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
+ Ci.nsIClipboardHelper
+ );
+ clipboard.copyString(this._logTextarea.value);
+ }
+
+ openDialog(gSubDialog) {
+ gSubDialog.open(
+ "chrome://browser/content/torpreferences/torLogDialog.xhtml",
+ { features: "resizable=yes" },
+ this
+ );
+ }
+}
diff --git a/browser/components/torpreferences/content/torLogDialog.xhtml b/browser/components/torpreferences/content/torLogDialog.xhtml
new file mode 100644
index 000000000000..9c17f8132978
--- /dev/null
+++ b/browser/components/torpreferences/content/torLogDialog.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">
+<dialog id="torPreferences-torLog-dialog"
+ buttons="accept,extra1">
+ <html:textarea
+ id="torPreferences-torDialog-textarea"
+ multiline="true"
+ readonly="true"/>
+ <script type="application/javascript"><![CDATA[
+ "use strict";
+
+ let torLogDialog = window.arguments[0];
+ let dialog = document.getElementById("torPreferences-torLog-dialog");
+ torLogDialog.init(window, dialog);
+ ]]></script>
+</dialog>
+</window>
\ No newline at end of file
diff --git a/browser/components/torpreferences/content/torPane.js b/browser/components/torpreferences/content/torPane.js
new file mode 100644
index 000000000000..49054b5dac6a
--- /dev/null
+++ b/browser/components/torpreferences/content/torPane.js
@@ -0,0 +1,857 @@
+"use strict";
+
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+
+const {
+ TorBridgeSource,
+ TorBridgeSettings,
+ makeTorBridgeSettingsNone,
+ makeTorBridgeSettingsBuiltin,
+ makeTorBridgeSettingsBridgeDB,
+ makeTorBridgeSettingsUserProvided,
+} = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/torBridgeSettings.jsm"
+);
+
+const {
+ TorProxyType,
+ TorProxySettings,
+ makeTorProxySettingsNone,
+ makeTorProxySettingsSocks4,
+ makeTorProxySettingsSocks5,
+ makeTorProxySettingsHTTPS,
+} = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/torProxySettings.jsm"
+);
+const {
+ TorFirewallSettings,
+ makeTorFirewallSettingsNone,
+ makeTorFirewallSettingsCustom,
+} = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/torFirewallSettings.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"
+);
+
+const { parsePort, parseBridgeStrings, parsePortList } = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/parseFunctions.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",
+ },
+ torPreferences: {
+ header: "h1#torPreferences-header",
+ description: "span#torPreferences-description",
+ learnMore: "label#torPreferences-learnMore",
+ },
+ 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
+ _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");
+
+ // 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
+ );
+ }
+
+ // 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
+ );
+
+ // load bridge settings
+ let torBridgeSettings = new TorBridgeSettings();
+ torBridgeSettings.readSettings();
+
+ // populate the bridge list
+ for (let currentBridge of TorBridgeSettings.defaultBridgeTypes) {
+ let menuEntry = document.createXULElement("menuitem");
+ menuEntry.setAttribute("value", currentBridge);
+ menuEntry.setAttribute("label", currentBridge);
+ this._builtinBridgeMenulist
+ .querySelector("menupopup")
+ .appendChild(menuEntry);
+ }
+
+ this.onSelectBridgeOption(torBridgeSettings.bridgeSource);
+ this.onToggleBridge(
+ torBridgeSettings.bridgeSource != TorBridgeSource.NONE
+ );
+ switch (torBridgeSettings.bridgeSource) {
+ case TorBridgeSource.NONE:
+ break;
+ case TorBridgeSource.BUILTIN:
+ this._builtinBridgeMenulist.value =
+ torBridgeSettings.selectedDefaultBridgeType;
+ break;
+ case TorBridgeSource.BRIDGEDB:
+ this._requestBridgeTextarea.value = torBridgeSettings.bridgeStrings;
+ break;
+ case TorBridgeSource.USERPROVIDED:
+ this._provideBridgeTextarea.value = torBridgeSettings.bridgeStrings;
+ break;
+ }
+
+ this._bridgeSettings = torBridgeSettings;
+
+ // load proxy settings
+ let torProxySettings = new TorProxySettings();
+ torProxySettings.readSettings();
+
+ if (torProxySettings.type != TorProxyType.NONE) {
+ this.onToggleProxy(true);
+ this.onSelectProxyType(torProxySettings.type);
+ this._proxyAddressTextbox.value = torProxySettings.address;
+ this._proxyPortTextbox.value = torProxySettings.port;
+ this._proxyUsernameTextbox.value = torProxySettings.username;
+ this._proxyPasswordTextbox.value = torProxySettings.password;
+ }
+
+ this._proxySettings = torProxySettings;
+
+ // load firewall settings
+ let torFirewallSettings = new TorFirewallSettings();
+ torFirewallSettings.readSettings();
+
+ if (torFirewallSettings.hasPorts) {
+ this.onToggleFirewall(true);
+ this._allowedPortsTextbox.value =
+ torFirewallSettings.commaSeparatedListString;
+ }
+
+ this._firewallSettings = torFirewallSettings;
+ },
+
+ init() {
+ this._populateXUL();
+ },
+
+ // whether the page should be present in about:preferences
+ get enabled() {
+ return TorProtocolService.ownsTorDaemon;
+ },
+
+ //
+ // Callbacks
+ //
+
+ // 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.NONE);
+ }
+ return this;
+ },
+
+ // callback when a bridge option is selected
+ onSelectBridgeOption(source) {
+ // disable all of the bridge elements under radio buttons
+ this._setElementsDisabled(
+ [
+ this._builtinBridgeMenulist,
+ this._requestBridgeButton,
+ this._requestBridgeTextarea,
+ this._provideBridgeTextarea,
+ ],
+ true
+ );
+
+ if (source != TorBridgeSource.NONE) {
+ 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,
+ this._proxySettings.proxyURI,
+ aBridges => {
+ if (aBridges.length > 0) {
+ let bridgeSettings = makeTorBridgeSettingsBridgeDB(aBridges);
+ bridgeSettings.writeSettings();
+ this._bridgeSettings = bridgeSettings;
+
+ this._requestBridgeTextarea.value = bridgeSettings.bridgeStrings;
+ }
+ }
+ );
+ return this;
+ },
+
+ // pushes bridge settings from UI to tor
+ onUpdateBridgeSettings() {
+ let bridgeSettings = null;
+
+ let source = this._useBridgeCheckbox.checked
+ ? this._bridgeSelectionRadiogroup.value
+ : TorBridgeSource.NONE;
+ switch (source) {
+ case TorBridgeSource.NONE: {
+ bridgeSettings = makeTorBridgeSettingsNone();
+ break;
+ }
+ case TorBridgeSource.BUILTIN: {
+ // if there is a built-in bridge already selected, use that
+ let bridgeType = this._builtinBridgeMenulist.value;
+ if (bridgeType) {
+ bridgeSettings = makeTorBridgeSettingsBuiltin(bridgeType);
+ } else {
+ bridgeSettings = makeTorBridgeSettingsNone();
+ }
+ break;
+ }
+ case TorBridgeSource.BRIDGEDB: {
+ // if there are bridgedb bridges saved in the text area, use them
+ let bridgeStrings = this._requestBridgeTextarea.value;
+ if (bridgeStrings) {
+ let bridgeStringList = parseBridgeStrings(bridgeStrings);
+ bridgeSettings = makeTorBridgeSettingsBridgeDB(bridgeStringList);
+ } else {
+ bridgeSettings = makeTorBridgeSettingsNone();
+ }
+ break;
+ }
+ case TorBridgeSource.USERPROVIDED: {
+ // if bridges already exist in the text area, use them
+ let bridgeStrings = this._provideBridgeTextarea.value;
+ if (bridgeStrings) {
+ let bridgeStringList = parseBridgeStrings(bridgeStrings);
+ bridgeSettings = makeTorBridgeSettingsUserProvided(
+ bridgeStringList
+ );
+ } else {
+ bridgeSettings = makeTorBridgeSettingsNone();
+ }
+ break;
+ }
+ }
+ bridgeSettings.writeSettings();
+ this._bridgeSettings = bridgeSettings;
+ 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 (value == "") {
+ value = TorProxyType.NONE;
+ }
+ this._proxyTypeMenulist.value = value;
+ switch (value) {
+ case TorProxyType.NONE: {
+ 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 proxyType = this._useProxyCheckbox.checked
+ ? this._proxyTypeMenulist.value
+ : TorProxyType.NONE;
+ const addressString = this._proxyAddressTextbox.value;
+ const portString = this._proxyPortTextbox.value;
+ const usernameString = this._proxyUsernameTextbox.value;
+ const passwordString = this._proxyPasswordTextbox.value;
+
+ let proxySettings = null;
+
+ switch (proxyType) {
+ case TorProxyType.NONE:
+ proxySettings = makeTorProxySettingsNone();
+ break;
+ case TorProxyType.SOCKS4:
+ proxySettings = makeTorProxySettingsSocks4(
+ addressString,
+ parsePort(portString)
+ );
+ break;
+ case TorProxyType.SOCKS5:
+ proxySettings = makeTorProxySettingsSocks5(
+ addressString,
+ parsePort(portString),
+ usernameString,
+ passwordString
+ );
+ break;
+ case TorProxyType.HTTPS:
+ proxySettings = makeTorProxySettingsHTTPS(
+ addressString,
+ parsePort(portString),
+ usernameString,
+ passwordString
+ );
+ break;
+ }
+
+ proxySettings.writeSettings();
+ this._proxySettings = proxySettings;
+ 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
+ : "";
+ let firewallSettings = null;
+
+ if (portListString) {
+ firewallSettings = makeTorFirewallSettingsCustom(
+ parsePortList(portListString)
+ );
+ } else {
+ firewallSettings = makeTorFirewallSettingsNone();
+ }
+
+ firewallSettings.writeSettings();
+ this._firewallSettings = firewallSettings;
+ 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
new file mode 100644
index 000000000000..3c966b2b3726
--- /dev/null
+++ b/browser/components/torpreferences/content/torPane.xhtml
@@ -0,0 +1,123 @@
+<!-- Tor panel -->
+
+<script type="application/javascript"
+ src="chrome://browser/content/torpreferences/torPane.js"/>
+<html:template id="template-paneTor">
+<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>
+
+<!-- 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
new file mode 100644
index 000000000000..4dac2c457823
--- /dev/null
+++ b/browser/components/torpreferences/content/torPreferences.css
@@ -0,0 +1,77 @@
+#category-tor > .category-icon {
+ list-style-image: url("chrome://browser/content/torpreferences/torPreferencesIcon.svg");
+}
+
+#torPreferences-advanced-grid {
+ display: grid;
+ grid-template-columns: auto 1fr;
+}
+
+.torPreferences-advanced-checkbox-container {
+ grid-column: 1 / 3;
+}
+
+#torPreferences-localProxy-textboxAddress,
+#torPreferences-localProxy-textboxUsername,
+#torPreferences-localProxy-textboxPassword,
+#torPreferences-advanced-textboxAllowedPorts {
+ -moz-box-flex: 1;
+}
+
+hbox#torPreferences-torDaemon-hbox {
+ margin-top: 20px;
+}
+
+description#torPreferences-requestBridge-description {
+ /*margin-bottom: 1em;*/
+ min-height: 2em;
+}
+
+image#torPreferences-requestBridge-captchaImage {
+ margin: 1em;
+ min-height: 125px;
+}
+
+button#torPreferences-requestBridge-refreshCaptchaButton {
+ min-width: initial;
+}
+
+dialog#torPreferences-requestBridge-dialog > hbox {
+ margin-bottom: 1em;
+}
+
+/*
+ Various elements that really should be lining up don't because they have inconsistent margins
+*/
+.torMarginFix {
+ margin-left : 4px;
+ margin-right : 4px;
+}
+
+/*
+ This hbox is hidden by css here by default so that the
+ xul dialog allocates enough screen space for the error message
+ element, otherwise it gets cut off since dialog's overflow is hidden
+*/
+hbox#torPreferences-requestBridge-incorrectCaptchaHbox {
+ visibility: hidden;
+}
+
+image#torPreferences-requestBridge-errorIcon {
+ list-style-image: url("chrome://browser/skin/warning.svg");
+}
+
+groupbox#torPreferences-bridges-group textarea {
+ white-space: pre;
+ overflow: auto;
+}
+
+textarea#torPreferences-torDialog-textarea {
+ -moz-box-flex: 1;
+ font-family: monospace;
+ font-size: 0.8em;
+ white-space: pre;
+ overflow: auto;
+ /* 10 lines */
+ min-height: 20em;
+}
\ No newline at end of file
diff --git a/browser/components/torpreferences/content/torPreferencesIcon.svg b/browser/components/torpreferences/content/torPreferencesIcon.svg
new file mode 100644
index 000000000000..d7895f1107c5
--- /dev/null
+++ b/browser/components/torpreferences/content/torPreferencesIcon.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <g fill="context-fill" fill-opacity="context-fill-opacity" fill-rule="nonzero">
+ <path d="M12.0246161,21.8174863 L12.0246161,20.3628098 C16.6324777,20.3495038 20.3634751,16.6108555 20.3634751,11.9996673 C20.3634751,7.38881189 16.6324777,3.65016355 12.0246161,3.63685757 L12.0246161,2.18218107 C17.4358264,2.1958197 21.8178189,6.58546322 21.8178189,11.9996673 C21.8178189,17.4142042 17.4358264,21.8041803 12.0246161,21.8174863 L12.0246161,21.8174863 Z M12.0246161,16.7259522 C14.623607,16.7123136 16.7272828,14.6023175 16.7272828,11.9996673 C16.7272828,9.39734991 14.623607,7.28735377 12.0246161,7.27371516 L12.0246161,5.81937131 C15.4272884,5.8326773 18.1819593,8.59400123 18.1819593,11.9996673 C18.1819593,15.4056661 15.4272884,18.1669901 12.0246161,18.1802961 L12.0246161,16.7259522 Z M12.0246161,9.45556355 C13.4187503,9.46886953 14.5454344,10.6022066 14.5454344,11.9996673 C14.5454344,13.3974608 13.4187503,14.5307978 12.0246161,14.5441038 L12.0246161,9.45556355 Z M0,11.9996673 C0,18.6273771 5.37229031,24 12,24 C18.6273771,24 24,18.6273771 24,11.9996673 C24,5.37229031
18.6273771,0 12,0 C5.37229031,0 0,5.37229031 0,11.9996673 Z"/>
+ </g>
+</svg>
\ No newline at end of file
diff --git a/browser/components/torpreferences/content/torProxySettings.jsm b/browser/components/torpreferences/content/torProxySettings.jsm
new file mode 100644
index 000000000000..98bb5e8d5cbf
--- /dev/null
+++ b/browser/components/torpreferences/content/torProxySettings.jsm
@@ -0,0 +1,245 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "TorProxyType",
+ "TorProxySettings",
+ "makeTorProxySettingsNone",
+ "makeTorProxySettingsSocks4",
+ "makeTorProxySettingsSocks5",
+ "makeTorProxySettingsHTTPS",
+];
+
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+const { parseAddrPort, parseUsernamePassword } = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/parseFunctions.jsm"
+);
+
+const TorProxyType = {
+ NONE: "NONE",
+ SOCKS4: "SOCKS4",
+ SOCKS5: "SOCKS5",
+ HTTPS: "HTTPS",
+};
+
+class TorProxySettings {
+ constructor() {
+ this._proxyType = TorProxyType.NONE;
+ this._proxyAddress = undefined;
+ this._proxyPort = undefined;
+ this._proxyUsername = undefined;
+ this._proxyPassword = undefined;
+ }
+
+ get type() {
+ return this._proxyType;
+ }
+ get address() {
+ return this._proxyAddress;
+ }
+ get port() {
+ return this._proxyPort;
+ }
+ get username() {
+ return this._proxyUsername;
+ }
+ get password() {
+ return this._proxyPassword;
+ }
+ get proxyURI() {
+ switch (this._proxyType) {
+ case TorProxyType.SOCKS4:
+ return `socks4a://${this._proxyAddress}:${this._proxyPort}`;
+ case TorProxyType.SOCKS5:
+ if (this._proxyUsername) {
+ return `socks5://${this._proxyUsername}:${this._proxyPassword}@${
+ this._proxyAddress
+ }:${this._proxyPort}`;
+ }
+ return `socks5://${this._proxyAddress}:${this._proxyPort}`;
+ case TorProxyType.HTTPS:
+ if (this._proxyUsername) {
+ return `http://${this._proxyUsername}:${this._proxyPassword}@${
+ this._proxyAddress
+ }:${this._proxyPort}`;
+ }
+ return `http://${this._proxyAddress}:${this._proxyPort}`;
+ }
+ return undefined;
+ }
+
+ // attempts to read proxy settings from Tor daemon
+ readSettings() {
+ // SOCKS4
+ {
+ let addressPort = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.socks4Proxy
+ );
+ if (addressPort) {
+ // address+port
+ let [proxyAddress, proxyPort] = parseAddrPort(addressPort);
+
+ this._proxyType = TorProxyType.SOCKS4;
+ this._proxyAddress = proxyAddress;
+ this._proxyPort = proxyPort;
+ this._proxyUsername = "";
+ this._proxyPassword = "";
+
+ return;
+ }
+ }
+
+ // SOCKS5
+ {
+ let addressPort = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.socks5Proxy
+ );
+
+ if (addressPort) {
+ // address+port
+ let [proxyAddress, proxyPort] = parseAddrPort(addressPort);
+ // username
+ let proxyUsername = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.socks5ProxyUsername
+ );
+ // password
+ let proxyPassword = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.socks5ProxyPassword
+ );
+
+ this._proxyType = TorProxyType.SOCKS5;
+ this._proxyAddress = proxyAddress;
+ this._proxyPort = proxyPort;
+ this._proxyUsername = proxyUsername;
+ this._proxyPassword = proxyPassword;
+
+ return;
+ }
+ }
+
+ // HTTP
+ {
+ let addressPort = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.httpsProxy
+ );
+
+ if (addressPort) {
+ // address+port
+ let [proxyAddress, proxyPort] = parseAddrPort(addressPort);
+
+ // username:password
+ let proxyAuthenticator = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.httpsProxyAuthenticator
+ );
+
+ let [proxyUsername, proxyPassword] = ["", ""];
+ if (proxyAuthenticator) {
+ [proxyUsername, proxyPassword] = parseUsernamePassword(
+ proxyAuthenticator
+ );
+ }
+
+ this._proxyType = TorProxyType.HTTPS;
+ this._proxyAddress = proxyAddress;
+ this._proxyPort = proxyPort;
+ this._proxyUsername = proxyUsername;
+ this._proxyPassword = proxyPassword;
+ }
+ }
+ // no proxy settings
+ } /* TorProxySettings::ReadFromTor() */
+
+ // attempts to write proxy settings to Tor daemon
+ // throws on error
+ writeSettings() {
+ let settingsObject = new Map();
+
+ // init proxy related settings to null so Tor daemon resets them
+ settingsObject.set(TorStrings.configKeys.socks4Proxy, null);
+ settingsObject.set(TorStrings.configKeys.socks5Proxy, null);
+ settingsObject.set(TorStrings.configKeys.socks5ProxyUsername, null);
+ settingsObject.set(TorStrings.configKeys.socks5ProxyPassword, null);
+ settingsObject.set(TorStrings.configKeys.httpsProxy, null);
+ settingsObject.set(TorStrings.configKeys.httpsProxyAuthenticator, null);
+
+ switch (this._proxyType) {
+ case TorProxyType.SOCKS4:
+ settingsObject.set(
+ TorStrings.configKeys.socks4Proxy,
+ `${this._proxyAddress}:${this._proxyPort}`
+ );
+ break;
+ case TorProxyType.SOCKS5:
+ settingsObject.set(
+ TorStrings.configKeys.socks5Proxy,
+ `${this._proxyAddress}:${this._proxyPort}`
+ );
+ settingsObject.set(
+ TorStrings.configKeys.socks5ProxyUsername,
+ this._proxyUsername
+ );
+ settingsObject.set(
+ TorStrings.configKeys.socks5ProxyPassword,
+ this._proxyPassword
+ );
+ break;
+ case TorProxyType.HTTPS:
+ settingsObject.set(
+ TorStrings.configKeys.httpsProxy,
+ `${this._proxyAddress}:${this._proxyPort}`
+ );
+ settingsObject.set(
+ TorStrings.configKeys.httpsProxyAuthenticator,
+ `${this._proxyUsername}:${this._proxyPassword}`
+ );
+ break;
+ }
+
+ TorProtocolService.writeSettings(settingsObject);
+ } /* TorProxySettings::WriteToTor() */
+}
+
+// factory methods for our various supported proxies
+function makeTorProxySettingsNone() {
+ return new TorProxySettings();
+}
+
+function makeTorProxySettingsSocks4(aProxyAddress, aProxyPort) {
+ let retval = new TorProxySettings();
+ retval._proxyType = TorProxyType.SOCKS4;
+ retval._proxyAddress = aProxyAddress;
+ retval._proxyPort = aProxyPort;
+ return retval;
+}
+
+function makeTorProxySettingsSocks5(
+ aProxyAddress,
+ aProxyPort,
+ aProxyUsername,
+ aProxyPassword
+) {
+ let retval = new TorProxySettings();
+ retval._proxyType = TorProxyType.SOCKS5;
+ retval._proxyAddress = aProxyAddress;
+ retval._proxyPort = aProxyPort;
+ retval._proxyUsername = aProxyUsername;
+ retval._proxyPassword = aProxyPassword;
+ return retval;
+}
+
+function makeTorProxySettingsHTTPS(
+ aProxyAddress,
+ aProxyPort,
+ aProxyUsername,
+ aProxyPassword
+) {
+ let retval = new TorProxySettings();
+ retval._proxyType = TorProxyType.HTTPS;
+ retval._proxyAddress = aProxyAddress;
+ retval._proxyPort = aProxyPort;
+ retval._proxyUsername = aProxyUsername;
+ retval._proxyPassword = aProxyPassword;
+ return retval;
+}
diff --git a/browser/components/torpreferences/jar.mn b/browser/components/torpreferences/jar.mn
new file mode 100644
index 000000000000..857bc9ee3eac
--- /dev/null
+++ b/browser/components/torpreferences/jar.mn
@@ -0,0 +1,14 @@
+browser.jar:
+ content/browser/torpreferences/parseFunctions.jsm (content/parseFunctions.jsm)
+ content/browser/torpreferences/requestBridgeDialog.xhtml (content/requestBridgeDialog.xhtml)
+ content/browser/torpreferences/requestBridgeDialog.jsm (content/requestBridgeDialog.jsm)
+ content/browser/torpreferences/torBridgeSettings.jsm (content/torBridgeSettings.jsm)
+ content/browser/torpreferences/torCategory.inc.xhtml (content/torCategory.inc.xhtml)
+ content/browser/torpreferences/torFirewallSettings.jsm (content/torFirewallSettings.jsm)
+ 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/torPreferences.css (content/torPreferences.css)
+ content/browser/torpreferences/torPreferencesIcon.svg (content/torPreferencesIcon.svg)
+ content/browser/torpreferences/torProxySettings.jsm (content/torProxySettings.jsm)
diff --git a/browser/components/torpreferences/moz.build b/browser/components/torpreferences/moz.build
new file mode 100644
index 000000000000..2661ad7cb9f3
--- /dev/null
+++ b/browser/components/torpreferences/moz.build
@@ -0,0 +1 @@
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/browser/modules/BridgeDB.jsm b/browser/modules/BridgeDB.jsm
new file mode 100644
index 000000000000..2caa26b4e2e0
--- /dev/null
+++ b/browser/modules/BridgeDB.jsm
@@ -0,0 +1,110 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["BridgeDB"];
+
+const { TorLauncherBridgeDB } = ChromeUtils.import(
+ "resource://torlauncher/modules/tl-bridgedb.jsm"
+);
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+var BridgeDB = {
+ _moatRequestor: null,
+ _currentCaptchaInfo: null,
+ _bridges: null,
+
+ get currentCaptchaImage() {
+ if (this._currentCaptchaInfo) {
+ return this._currentCaptchaInfo.captchaImage;
+ }
+ return null;
+ },
+
+ get currentBridges() {
+ return this._bridges;
+ },
+
+ submitCaptchaGuess(aCaptchaSolution) {
+ if (this._moatRequestor && this._currentCaptchaInfo) {
+ return this._moatRequestor
+ .finishFetch(
+ this._currentCaptchaInfo.transport,
+ this._currentCaptchaInfo.challenge,
+ aCaptchaSolution
+ )
+ .then(aBridgeInfo => {
+ this._moatRequestor.close();
+ this._moatRequestor = null;
+ this._currentCaptchaInfo = null;
+ this._bridges = aBridgeInfo.bridges;
+ // array of bridge strings
+ return this._bridges;
+ });
+ }
+
+ return new Promise((aResponse, aReject) => {
+ aReject(new Error("Invalid _moatRequestor or _currentCaptchaInfo"));
+ });
+ },
+
+ requestNewCaptchaImage(aProxyURI) {
+ // close and clear out existing state on captcha request
+ this.close();
+
+ let transportPlugins = TorProtocolService.readStringArraySetting(
+ TorStrings.configKeys.clientTransportPlugin
+ );
+
+ let meekClientPath;
+ let meekTransport; // We support both "meek" and "meek_lite".
+ let meekClientArgs;
+ // TODO: shouldn't this early out once meek settings are found?
+ for (const line of transportPlugins) {
+ // Parse each ClientTransportPlugin line and look for the meek or
+ // meek_lite transport. This code works a lot like the Tor daemon's
+ // parse_transport_line() function.
+ let tokens = line.split(" ");
+ if (tokens.length > 2 && tokens[1] == "exec") {
+ let transportArray = tokens[0].split(",").map(aStr => aStr.trim());
+ let transport = transportArray.find(
+ aTransport => aTransport === "meek"
+ );
+ if (!transport) {
+ transport = transportArray.find(
+ aTransport => aTransport === "meek_lite"
+ );
+ }
+ if (transport) {
+ meekTransport = transport;
+ meekClientPath = tokens[2];
+ meekClientArgs = tokens.slice(3);
+ }
+ }
+ }
+
+ this._moatRequestor = TorLauncherBridgeDB.createMoatRequestor();
+
+ return this._moatRequestor
+ .init(aProxyURI, meekTransport, meekClientPath, meekClientArgs)
+ .then(() => {
+ // TODO: get this from TorLauncherUtil
+ let bridgeType = "obfs4";
+ return this._moatRequestor.fetchBridges([bridgeType]);
+ })
+ .then(aCaptchaInfo => {
+ // cache off the current captcha info as the challenge is needed for response
+ this._currentCaptchaInfo = aCaptchaInfo;
+ return aCaptchaInfo.captchaImage;
+ });
+ },
+
+ close() {
+ if (this._moatRequestor) {
+ this._moatRequestor.close();
+ this._moatRequestor = null;
+ }
+ this._currentCaptchaInfo = null;
+ },
+};
diff --git a/browser/modules/TorProtocolService.jsm b/browser/modules/TorProtocolService.jsm
new file mode 100644
index 000000000000..b4e6ed9a3253
--- /dev/null
+++ b/browser/modules/TorProtocolService.jsm
@@ -0,0 +1,212 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["TorProtocolService"];
+
+const { TorLauncherUtil } = ChromeUtils.import(
+ "resource://torlauncher/modules/tl-util.jsm"
+);
+
+var TorProtocolService = {
+ _tlps: Cc["@torproject.org/torlauncher-protocol-service;1"].getService(
+ Ci.nsISupports
+ ).wrappedJSObject,
+
+ // maintain a map of tor settings set by Tor Browser so that we don't
+ // repeatedly set the same key/values over and over
+ // this map contains string keys to primitive or array values
+ _settingsCache: new Map(),
+
+ _typeof(aValue) {
+ switch (typeof aValue) {
+ case "boolean":
+ return "boolean";
+ case "string":
+ return "string";
+ case "object":
+ if (aValue == null) {
+ return "null";
+ } else if (Array.isArray(aValue)) {
+ return "array";
+ }
+ return "object";
+ }
+ return "unknown";
+ },
+
+ _assertValidSettingKey(aSetting) {
+ // ensure the 'key' is a string
+ if (typeof aSetting != "string") {
+ throw new Error(
+ `Expected setting of type string but received ${typeof aSetting}`
+ );
+ }
+ },
+
+ _assertValidSetting(aSetting, aValue) {
+ this._assertValidSettingKey(aSetting);
+
+ const valueType = this._typeof(aValue);
+ switch (valueType) {
+ case "boolean":
+ case "string":
+ case "null":
+ return;
+ case "array":
+ for (const element of aValue) {
+ if (typeof element != "string") {
+ throw new Error(
+ `Setting '${aSetting}' array contains value of invalid type '${typeof element}'`
+ );
+ }
+ }
+ return;
+ default:
+ throw new Error(
+ `Invalid object type received for setting '${aSetting}'`
+ );
+ }
+ },
+
+ // takes a Map containing tor settings
+ // throws on error
+ writeSettings(aSettingsObj) {
+ // only write settings that have changed
+ let newSettings = new Map();
+ for (const [setting, value] of aSettingsObj) {
+ let saveSetting = false;
+
+ // make sure we have valid data here
+ this._assertValidSetting(setting, value);
+
+ if (!this._settingsCache.has(setting)) {
+ // no cached setting, so write
+ saveSetting = true;
+ } else {
+ const cachedValue = this._settingsCache.get(setting);
+ if (value != cachedValue) {
+ // compare arrays member-wise
+ if (Array.isArray(value) && Array.isArray(cachedValue)) {
+ if (value.length != cachedValue.length) {
+ saveSetting = true;
+ } else {
+ const arrayLength = value.length;
+ for (let i = 0; i < arrayLength; ++i) {
+ if (value[i] != cachedValue[i]) {
+ saveSetting = true;
+ break;
+ }
+ }
+ }
+ } else {
+ // some other different values
+ saveSetting = true;
+ }
+ }
+ }
+
+ if (saveSetting) {
+ newSettings.set(setting, value);
+ }
+ }
+
+ // only write if new setting to save
+ if (newSettings.size > 0) {
+ // convert settingsObject map to js object for torlauncher-protocol-service
+ let settingsObject = {};
+ for (const [setting, value] of newSettings) {
+ settingsObject[setting] = value;
+ }
+
+ let errorObject = {};
+ if (!this._tlps.TorSetConfWithReply(settingsObject, errorObject)) {
+ throw new Error(errorObject.details);
+ }
+
+ // save settings to cache after successfully writing to Tor
+ for (const [setting, value] of newSettings) {
+ this._settingsCache.set(setting, value);
+ }
+ }
+ },
+
+ _readSetting(aSetting) {
+ this._assertValidSettingKey(aSetting);
+ let reply = this._tlps.TorGetConf(aSetting);
+ if (this._tlps.TorCommandSucceeded(reply)) {
+ return reply.lineArray;
+ }
+ throw new Error(reply.lineArray.join("\n"));
+ },
+
+ _readBoolSetting(aSetting) {
+ let lineArray = this._readSetting(aSetting);
+ if (lineArray.length != 1) {
+ throw new Error(
+ `Expected an array with length 1 but received array of length ${
+ lineArray.length
+ }`
+ );
+ }
+
+ let retval = lineArray[0];
+ switch (retval) {
+ case "0":
+ return false;
+ case "1":
+ return true;
+ default:
+ throw new Error(`Expected boolean (1 or 0) but received '${retval}'`);
+ }
+ },
+
+ _readStringSetting(aSetting) {
+ let lineArray = this._readSetting(aSetting);
+ if (lineArray.length != 1) {
+ throw new Error(
+ `Expected an array with length 1 but received array of length ${
+ lineArray.length
+ }`
+ );
+ }
+ return lineArray[0];
+ },
+
+ _readStringArraySetting(aSetting) {
+ let lineArray = this._readSetting(aSetting);
+ return lineArray;
+ },
+
+ readBoolSetting(aSetting) {
+ let value = this._readBoolSetting(aSetting);
+ this._settingsCache.set(aSetting, value);
+ return value;
+ },
+
+ readStringSetting(aSetting) {
+ let value = this._readStringSetting(aSetting);
+ this._settingsCache.set(aSetting, value);
+ return value;
+ },
+
+ readStringArraySetting(aSetting) {
+ let value = this._readStringArraySetting(aSetting);
+ this._settingsCache.set(aSetting, value);
+ return value;
+ },
+
+ // writes current tor settings to disk
+ flushSettings() {
+ this._tlps.TorSendCommand("SAVECONF");
+ },
+
+ getLog() {
+ let countObj = { value: 0 };
+ let torLog = this._tlps.TorGetLog(countObj);
+ return torLog;
+ },
+
+ // true if we launched and control tor, false if using system tor
+ get ownsTorDaemon() {
+ return TorLauncherUtil.shouldStartAndOwnTor;
+ },
+};
diff --git a/browser/modules/moz.build b/browser/modules/moz.build
index 3774c12f4ef0..1f7c6bc4c67e 100644
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -128,6 +128,7 @@ EXTRA_JS_MODULES += [
"AboutNewTab.jsm",
"AppUpdater.jsm",
"AsyncTabSwitcher.jsm",
+ "BridgeDB.jsm",
"BrowserUIUtils.jsm",
"BrowserUsageTelemetry.jsm",
"BrowserWindowTracker.jsm",
@@ -152,6 +153,7 @@ EXTRA_JS_MODULES += [
"TabsList.jsm",
"TabUnloader.jsm",
"ThemeVariableMap.jsm",
+ "TorProtocolService.jsm",
"TorStrings.jsm",
"TransientPrefs.jsm",
"webrtcUI.jsm",
1
0
[tor-browser/tor-browser-88.0.1-10.0-1] Bug 32220: Improve the letterboxing experience
by sysrqb@torproject.org 04 May '21
by sysrqb@torproject.org 04 May '21
04 May '21
commit 1f141197099e33df6a6c903473a317e4a9ff86a1
Author: Richard Pospesel <richard(a)torproject.org>
Date: Mon Oct 28 17:42:17 2019 -0700
Bug 32220: Improve the letterboxing experience
CSS and JS changes to alter the UX surrounding letterboxing. The
browser element containing page content is now anchored to the bottom
of the toolbar, and the remaining letterbox margin is the same color
as the firefox chrome. The letterbox margin and border are tied to
the currently selected theme.
Also adds a 'needsLetterbox' property to tabbrowser.xml to fix a race
condition present when using the 'isEmpty' property. Using 'isEmpty'
as a proxy for 'needsLetterbox' resulted in over-zealous/unnecessary
letterboxing of about:blank tabs.
---
browser/base/content/browser.css | 8 ++
browser/base/content/tabbrowser-tab.js | 9 +++
browser/themes/shared/tabs.inc.css | 6 ++
.../components/resistfingerprinting/RFPHelper.jsm | 94 +++++++++++++++++++---
4 files changed, 105 insertions(+), 12 deletions(-)
diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css
index e6879a99a7e3..b2c881a4997e 100644
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -99,6 +99,14 @@ body {
display: none;
}
+
+.browserStack > browser.letterboxing {
+ border-color: var(--chrome-content-separator-color);
+ border-style: solid;
+ border-width : 1px;
+ border-top: none;
+}
+
%ifdef MENUBAR_CAN_AUTOHIDE
#toolbar-menubar[autohide="true"] {
overflow: hidden;
diff --git a/browser/base/content/tabbrowser-tab.js b/browser/base/content/tabbrowser-tab.js
index 448878d9855f..ccc30cae1e3a 100644
--- a/browser/base/content/tabbrowser-tab.js
+++ b/browser/base/content/tabbrowser-tab.js
@@ -257,6 +257,15 @@
return true;
}
+ get needsLetterbox() {
+ let browser = this.linkedBrowser;
+ if (isBlankPageURL(browser.currentURI.spec)) {
+ return false;
+ }
+
+ return true;
+ }
+
get lastAccessed() {
return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
}
diff --git a/browser/themes/shared/tabs.inc.css b/browser/themes/shared/tabs.inc.css
index 9b4e72e3a9e9..81785a8b4c5f 100644
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -46,6 +46,12 @@
background-color: #f9f9fa;
}
+/* extend down the toolbar's colors when letterboxing is enabled*/
+#tabbrowser-tabpanels.letterboxing {
+ background-color: var(--toolbar-bgcolor);
+ background-image: var(--toolbar-bgimage);
+}
+
:root[privatebrowsingmode=temporary] #tabbrowser-tabpanels {
/* Value for --in-content-page-background in aboutPrivateBrowsing.css */
background-color: #25003e;
diff --git a/toolkit/components/resistfingerprinting/RFPHelper.jsm b/toolkit/components/resistfingerprinting/RFPHelper.jsm
index 166ad21e9013..9520d8720631 100644
--- a/toolkit/components/resistfingerprinting/RFPHelper.jsm
+++ b/toolkit/components/resistfingerprinting/RFPHelper.jsm
@@ -40,6 +40,7 @@ class _RFPHelper {
// ============================================================================
constructor() {
this._initialized = false;
+ this._borderDimensions = null;
}
init() {
@@ -361,6 +362,24 @@ class _RFPHelper {
});
}
+ getBorderDimensions(aBrowser) {
+ if (this._borderDimensions) {
+ return this._borderDimensions;
+ }
+
+ const win = aBrowser.ownerGlobal;
+ const browserStyle = win.getComputedStyle(aBrowser);
+
+ this._borderDimensions = {
+ top : parseInt(browserStyle.borderTopWidth),
+ right: parseInt(browserStyle.borderRightWidth),
+ bottom : parseInt(browserStyle.borderBottomWidth),
+ left : parseInt(browserStyle.borderLeftWidth),
+ };
+
+ return this._borderDimensions;
+ }
+
_addOrClearContentMargin(aBrowser) {
let tab = aBrowser.getTabBrowser().getTabForBrowser(aBrowser);
@@ -369,9 +388,13 @@ class _RFPHelper {
return;
}
+ // we add the letterboxing class even if the content does not need letterboxing
+ // in which case margins are set such that the borders are hidden
+ aBrowser.classList.add("letterboxing");
+
// We should apply no margin around an empty tab or a tab with system
// principal.
- if (tab.isEmpty || aBrowser.contentPrincipal.isSystemPrincipal) {
+ if (!tab.needsLetterbox || aBrowser.contentPrincipal.isSystemPrincipal) {
this._clearContentViewMargin(aBrowser);
} else {
this._roundContentView(aBrowser);
@@ -539,10 +562,29 @@ class _RFPHelper {
// Calculating the margins around the browser element in order to round the
// content viewport. We will use a 200x100 stepping if the dimension set
// is not given.
- let margins = calcMargins(containerWidth, containerHeight);
+
+ const borderDimensions = this.getBorderDimensions(aBrowser);
+ const marginDims = calcMargins(containerWidth, containerHeight - borderDimensions.top);
+
+ let margins = {
+ top : 0,
+ right : 0,
+ bottom : 0,
+ left : 0,
+ };
+
+ // snap browser element to top
+ margins.top = 0;
+ // and leave 'double' margin at the bottom
+ margins.bottom = 2 * marginDims.height - borderDimensions.bottom;
+ // identical margins left and right
+ margins.right = marginDims.width - borderDimensions.right;
+ margins.left = marginDims.width - borderDimensions.left;
+
+ const marginStyleString = `${margins.top}px ${margins.right}px ${margins.bottom}px ${margins.left}px`;
// If the size of the content is already quantized, we do nothing.
- if (aBrowser.style.margin == `${margins.height}px ${margins.width}px`) {
+ if (aBrowser.style.margin === marginStyleString) {
log("_roundContentView[" + logId + "] is_rounded == true");
if (this._isLetterboxingTesting) {
log(
@@ -563,19 +605,35 @@ class _RFPHelper {
"_roundContentView[" +
logId +
"] setting margins to " +
- margins.width +
- " x " +
- margins.height
+ marginStyleString
);
- // One cannot (easily) control the color of a margin unfortunately.
- // An initial attempt to use a border instead of a margin resulted
- // in offset event dispatching; so for now we use a colorless margin.
- aBrowser.style.margin = `${margins.height}px ${margins.width}px`;
+
+ // The margin background color is determined by the background color of the
+ // window's tabpanels#tabbrowser-tabpanels element
+ aBrowser.style.margin = marginStyleString;
});
}
_clearContentViewMargin(aBrowser) {
+ const borderDimensions = this.getBorderDimensions(aBrowser);
+ // set the margins such that the browser elements border is visible up top, but
+ // are rendered off-screen on the remaining sides
+ let margins = {
+ top : 0,
+ right : -borderDimensions.right,
+ bottom : -borderDimensions.bottom,
+ left : -borderDimensions.left,
+ };
+ const marginStyleString = `${margins.top}px ${margins.right}px ${margins.bottom}px ${margins.left}px`;
+
+ aBrowser.ownerGlobal.requestAnimationFrame(() => {
+ aBrowser.style.margin = marginStyleString;
+ });
+ }
+
+ _removeLetterboxing(aBrowser) {
aBrowser.ownerGlobal.requestAnimationFrame(() => {
+ aBrowser.classList.remove("letterboxing");
aBrowser.style.margin = "";
});
}
@@ -593,6 +651,11 @@ class _RFPHelper {
aWindow.gBrowser.addTabsProgressListener(this);
aWindow.addEventListener("TabOpen", this);
+ const tabPanel = aWindow.document.getElementById("tabbrowser-tabpanels");
+ if (tabPanel) {
+ tabPanel.classList.add("letterboxing");
+ }
+
// Rounding the content viewport.
this._updateMarginsForTabsInWindow(aWindow);
}
@@ -616,10 +679,17 @@ class _RFPHelper {
tabBrowser.removeTabsProgressListener(this);
aWindow.removeEventListener("TabOpen", this);
- // Clear all margins and tooltip for all browsers.
+ // revert tabpanel's background colors to default
+ const tabPanel = aWindow.document.getElementById("tabbrowser-tabpanels");
+ if (tabPanel) {
+ tabPanel.classList.remove("letterboxing");
+ }
+
+ // and revert each browser element to default,
+ // restore default margins and remove letterboxing class
for (let tab of tabBrowser.tabs) {
let browser = tab.linkedBrowser;
- this._clearContentViewMargin(browser);
+ this._removeLetterboxing(browser);
}
}
1
0
[tor-browser/tor-browser-88.0.1-10.0-1] Bug 24796 - Comment out excess permissions from GeckoView
by sysrqb@torproject.org 04 May '21
by sysrqb@torproject.org 04 May '21
04 May '21
commit 92b7f2473718ecde93796299fc2d72bd20d7f660
Author: Matthew Finkel <Matthew.Finkel(a)gmail.com>
Date: Wed Apr 11 17:52:59 2018 +0000
Bug 24796 - Comment out excess permissions from GeckoView
The GeckoView AndroidManifest.xml is not preprocessed unlike Fennec's
manifest, so we can't use the ifdef preprocessor guards around the
permissions we do not want. Commenting the permissions is the
next-best-thing.
---
.../android/geckoview/src/main/AndroidManifest.xml | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/mobile/android/geckoview/src/main/AndroidManifest.xml b/mobile/android/geckoview/src/main/AndroidManifest.xml
index a76b6a4754b6..7a2f30708fc3 100644
--- a/mobile/android/geckoview/src/main/AndroidManifest.xml
+++ b/mobile/android/geckoview/src/main/AndroidManifest.xml
@@ -6,20 +6,32 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.mozilla.geckoview">
+<!--#ifdef MOZ_ANDROID_NETWORK_STATE-->
+ <!--
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ -->
+<!--#endif-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+<!--#ifdef MOZ_ANDROID_LOCATION-->
+ <!--
<uses-feature
android:name="android.hardware.location"
android:required="false"/>
<uses-feature
android:name="android.hardware.location.gps"
android:required="false"/>
+ -->
+<!--#endif-->
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false"/>
+<!--#ifdef MOZ_WEBRTC-->
+ <!-- TODO preprocess AndroidManifest.xml so that we can
+ conditionally include WebRTC permissions based on MOZ_WEBRTC. -->
+ <!--
<uses-feature
android:name="android.hardware.camera"
android:required="false"/>
@@ -28,14 +40,16 @@
android:required="false"/>
<uses-feature
- android:name="android.hardware.audio.low_latency"
+ android:name="android.hardware.camera.any"
android:required="false"/>
<uses-feature
- android:name="android.hardware.microphone"
+ android:name="android.hardware.audio.low_latency"
android:required="false"/>
<uses-feature
- android:name="android.hardware.camera.any"
+ android:name="android.hardware.microphone"
android:required="false"/>
+ -->
+<!--#endif-->
<!-- GeckoView requires OpenGL ES 2.0 -->
<uses-feature
1
0
[tor-browser/tor-browser-88.0.1-10.0-1] Orfox: Centralized proxy applied to AbstractCommunicator and BaseResources.
by sysrqb@torproject.org 04 May '21
by sysrqb@torproject.org 04 May '21
04 May '21
commit 1ca049688e8d452ce8ce93c5f3b580c701359656
Author: Amogh Pradeep <amoghbl1(a)gmail.com>
Date: Fri Jun 12 02:07:45 2015 -0400
Orfox: Centralized proxy applied to AbstractCommunicator and BaseResources.
See Bug 1357997 for partial uplift.
Also:
Bug 28051 - Use our Orbot for proxying our connections
Bug 31144 - ESR68 Network Code Review
---
.../java/org/mozilla/gecko/util/ProxySelector.java | 25 +++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java
index dbd07a069de1..800c7cf96de8 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java
@@ -29,6 +29,10 @@ import java.net.URLConnection;
import java.util.List;
public class ProxySelector {
+ private static final String TOR_PROXY_ADDRESS = "127.0.0.1";
+ private static final int TOR_SOCKS_PROXY_PORT = 9150;
+ private static final int TOR_HTTP_PROXY_PORT = 8218;
+
public static URLConnection openConnectionWithProxy(final URI uri) throws IOException {
final java.net.ProxySelector ps = java.net.ProxySelector.getDefault();
Proxy proxy = Proxy.NO_PROXY;
@@ -39,7 +43,26 @@ public class ProxySelector {
}
}
- return uri.toURL().openConnection(proxy);
+ /* Ignore the proxy we found from the VM, only use Tor. We can probably
+ * safely use the logic in this class in the future. */
+ return uri.toURL().openConnection(getProxy());
+ }
+
+ public static Proxy getProxy() {
+ // TODO make configurable
+ return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(TOR_PROXY_ADDRESS, TOR_SOCKS_PROXY_PORT));
+ }
+
+ public static String getProxyHostAddress() {
+ return TOR_PROXY_ADDRESS;
+ }
+
+ public static int getSocksProxyPort() {
+ return TOR_SOCKS_PROXY_PORT;
+ }
+
+ public static int getHttpProxyPort() {
+ return TOR_HTTP_PROXY_PORT;
}
public ProxySelector() {
1
0
[tor-browser/tor-browser-88.0.1-10.0-1] Bug 21952: Implement Onion-Location
by sysrqb@torproject.org 04 May '21
by sysrqb@torproject.org 04 May '21
04 May '21
commit 371dac1620a38ceaef69561d1c7426c310200a9a
Author: Alex Catarineu <acat(a)torproject.org>
Date: Thu Mar 5 22:16:39 2020 +0100
Bug 21952: Implement Onion-Location
Whenever a valid Onion-Location HTTP header (or corresponding HTML
<meta> http-equiv attribute) is found in a document load, we either
redirect to it (if the user opted-in via preference) or notify the
presence of an onionsite alternative with a badge in the urlbar.
---
browser/base/content/browser.js | 12 ++
browser/base/content/browser.xhtml | 3 +
browser/components/BrowserGlue.jsm | 13 ++
.../onionservices/OnionLocationChild.jsm | 39 +++++
.../onionservices/OnionLocationParent.jsm | 168 +++++++++++++++++++++
.../content/onionlocation-notification-icons.css | 5 +
.../onionservices/content/onionlocation-urlbar.css | 27 ++++
.../content/onionlocation-urlbar.inc.xhtml | 10 ++
.../onionservices/content/onionlocation.svg | 3 +
.../content/onionlocationPreferences.inc.xhtml | 11 ++
.../content/onionlocationPreferences.js | 31 ++++
browser/components/onionservices/jar.mn | 2 +
browser/components/onionservices/moz.build | 2 +
browser/components/preferences/privacy.inc.xhtml | 2 +
browser/components/preferences/privacy.js | 17 +++
browser/themes/shared/notification-icons.inc.css | 2 +
browser/themes/shared/urlbar-searchbar.inc.css | 2 +
dom/base/Document.cpp | 34 ++++-
dom/base/Document.h | 2 +
dom/webidl/Document.webidl | 8 +
modules/libpref/init/StaticPrefList.yaml | 5 +
xpcom/ds/StaticAtoms.py | 1 +
22 files changed, 398 insertions(+), 1 deletion(-)
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 17f5403be28f..5e45fa7e025a 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -49,6 +49,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
NetUtil: "resource://gre/modules/NetUtil.jsm",
NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.jsm",
+ OnionLocationParent: "resource:///modules/OnionLocationParent.jsm",
PageActions: "resource:///modules/PageActions.jsm",
PageThumbs: "resource://gre/modules/PageThumbs.jsm",
PanelMultiView: "resource:///modules/PanelMultiView.jsm",
@@ -5355,6 +5356,7 @@ var XULBrowserWindow = {
CFRPageActions.updatePageActions(gBrowser.selectedBrowser);
AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);
+ OnionLocationParent.updateOnionLocationBadge(gBrowser.selectedBrowser);
if (!gMultiProcessBrowser) {
// Bug 1108553 - Cannot rotate images with e10s
@@ -5845,6 +5847,16 @@ var CombinedStopReload = {
var TabsProgressListener = {
onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // Clear OnionLocation UI
+ if (
+ aStateFlags & Ci.nsIWebProgressListener.STATE_START &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aRequest &&
+ aWebProgress.isTopLevel
+ ) {
+ OnionLocationParent.onStateChange(aBrowser);
+ }
+
// Collect telemetry data about tab load times.
if (
aWebProgress.isTopLevel &&
diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml
index 1cb6b1015ce4..1662546f2115 100644
--- a/browser/base/content/browser.xhtml
+++ b/browser/base/content/browser.xhtml
@@ -2045,6 +2045,9 @@
onclick="FullZoom.reset(); FullZoom.resetScalingZoom();"
tooltip="dynamic-shortcut-tooltip"
hidden="true"/>
+
+#include ../../components/onionservices/content/onionlocation-urlbar.inc.xhtml
+
<box id="pageActionSeparator" class="urlbar-page-action"/>
<image id="pageActionButton"
class="urlbar-icon urlbar-page-action"
diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm
index b36091408eaa..6752cce1566d 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -544,6 +544,19 @@ let JSWINDOWACTORS = {
allFrames: true,
},
+ OnionLocation: {
+ parent: {
+ moduleURI: "resource:///modules/OnionLocationParent.jsm",
+ },
+ child: {
+ moduleURI: "resource:///modules/OnionLocationChild.jsm",
+ events: {
+ pageshow: { mozSystemGroup: true },
+ },
+ },
+ messageManagerGroups: ["browsers"],
+ },
+
PageInfo: {
child: {
moduleURI: "resource:///actors/PageInfoChild.jsm",
diff --git a/browser/components/onionservices/OnionLocationChild.jsm b/browser/components/onionservices/OnionLocationChild.jsm
new file mode 100644
index 000000000000..9e00054ac56c
--- /dev/null
+++ b/browser/components/onionservices/OnionLocationChild.jsm
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["OnionLocationChild"];
+
+class OnionLocationChild extends JSWindowActorChild {
+ handleEvent(event) {
+ this.onPageShow(event);
+ }
+
+ onPageShow(event) {
+ if (event.target != this.document) {
+ return;
+ }
+ const onionLocationURI = this.document.onionLocationURI;
+ if (onionLocationURI) {
+ this.sendAsyncMessage("OnionLocation:Set");
+ }
+ }
+
+ receiveMessage(aMessage) {
+ if (aMessage.name == "OnionLocation:Refresh") {
+ const doc = this.document;
+ const docShell = this.docShell;
+ const onionLocationURI = doc.onionLocationURI;
+ const refreshURI = docShell.QueryInterface(Ci.nsIRefreshURI);
+ if (onionLocationURI && refreshURI) {
+ refreshURI.refreshURI(
+ onionLocationURI,
+ doc.nodePrincipal,
+ 0,
+ false,
+ true
+ );
+ }
+ }
+ }
+}
diff --git a/browser/components/onionservices/OnionLocationParent.jsm b/browser/components/onionservices/OnionLocationParent.jsm
new file mode 100644
index 000000000000..f6250e554862
--- /dev/null
+++ b/browser/components/onionservices/OnionLocationParent.jsm
@@ -0,0 +1,168 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["OnionLocationParent"];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+// Prefs
+const NOTIFICATION_PREF = "privacy.prioritizeonions.showNotification";
+const PRIORITIZE_ONIONS_PREF = "privacy.prioritizeonions.enabled";
+
+// Element IDs
+const ONIONLOCATION_BOX_ID = "onion-location-box";
+const ONIONLOCATION_BUTTON_ID = "onion-location-button";
+const ONIONLOCATION_LABEL_ID = "onion-label";
+
+// Notification IDs
+const NOTIFICATION_ID = "onion-location";
+const NOTIFICATION_ANCHOR_ID = "onionlocation";
+
+// Strings
+const STRING_ONION_AVAILABLE = TorStrings.onionLocation.onionAvailable;
+const NOTIFICATION_CANCEL_LABEL = TorStrings.onionLocation.notNow;
+const NOTIFICATION_CANCEL_ACCESSKEY = TorStrings.onionLocation.notNowAccessKey;
+const NOTIFICATION_OK_LABEL = TorStrings.onionLocation.alwaysPrioritize;
+const NOTIFICATION_OK_ACCESSKEY =
+ TorStrings.onionLocation.alwaysPrioritizeAccessKey;
+const NOTIFICATION_TITLE = TorStrings.onionLocation.tryThis;
+const NOTIFICATION_DESCRIPTION = TorStrings.onionLocation.description;
+const NOTIFICATION_LEARN_MORE_URL = TorStrings.onionLocation.learnMoreURL;
+
+class OnionLocationParent extends JSWindowActorParent {
+ // Listeners are added in BrowserGlue.jsm
+ receiveMessage(aMsg) {
+ switch (aMsg.name) {
+ case "OnionLocation:Set":
+ let browser = this.browsingContext.embedderElement;
+ OnionLocationParent.setOnionLocation(browser);
+ break;
+ }
+ }
+
+ static buttonClick(event) {
+ if (event.button !== 0) {
+ return;
+ }
+ const win = event.target.ownerGlobal;
+ if (win.gBrowser) {
+ const browser = win.gBrowser.selectedBrowser;
+ OnionLocationParent.redirect(browser);
+ }
+ }
+
+ static redirect(browser) {
+ let windowGlobal = browser.browsingContext.currentWindowGlobal;
+ let actor = windowGlobal.getActor("OnionLocation");
+ if (actor) {
+ actor.sendAsyncMessage("OnionLocation:Refresh", {});
+ OnionLocationParent.setDisabled(browser);
+ }
+ }
+
+ static onStateChange(browser) {
+ delete browser._onionLocation;
+ OnionLocationParent.hideNotification(browser);
+ }
+
+ static setOnionLocation(browser) {
+ browser._onionLocation = true;
+ let tabBrowser = browser.getTabBrowser();
+ if (tabBrowser && browser === tabBrowser.selectedBrowser) {
+ OnionLocationParent.updateOnionLocationBadge(browser);
+ }
+ }
+
+ static hideNotification(browser) {
+ const win = browser.ownerGlobal;
+ if (browser._onionLocationPrompt) {
+ win.PopupNotifications.remove(browser._onionLocationPrompt);
+ }
+ }
+
+ static showNotification(browser) {
+ const mustShow = Services.prefs.getBoolPref(NOTIFICATION_PREF, true);
+ if (!mustShow) {
+ return;
+ }
+
+ const win = browser.ownerGlobal;
+ Services.prefs.setBoolPref(NOTIFICATION_PREF, false);
+
+ const mainAction = {
+ label: NOTIFICATION_OK_LABEL,
+ accessKey: NOTIFICATION_OK_ACCESSKEY,
+ callback() {
+ Services.prefs.setBoolPref(PRIORITIZE_ONIONS_PREF, true);
+ OnionLocationParent.redirect(browser);
+ win.openPreferences("privacy-onionservices");
+ },
+ };
+
+ const cancelAction = {
+ label: NOTIFICATION_CANCEL_LABEL,
+ accessKey: NOTIFICATION_CANCEL_ACCESSKEY,
+ callback: () => {},
+ };
+
+ const options = {
+ autofocus: true,
+ persistent: true,
+ removeOnDismissal: false,
+ eventCallback(aTopic) {
+ if (aTopic === "removed") {
+ delete browser._onionLocationPrompt;
+ delete browser.onionpopupnotificationanchor;
+ }
+ },
+ learnMoreURL: NOTIFICATION_LEARN_MORE_URL,
+ displayURI: {
+ hostPort: NOTIFICATION_TITLE, // This is hacky, but allows us to have a title without extra markup/css.
+ },
+ hideClose: true,
+ popupIconClass: "onionlocation-notification-icon",
+ };
+
+ // A hacky way of setting the popup anchor outside the usual url bar icon box
+ // onionlocationpopupnotificationanchor comes from `${ANCHOR_ID}popupnotificationanchor`
+ // From https://searchfox.org/mozilla-esr68/rev/080f9ed47742644d2ff84f7aa0b10aea5c4…
+ browser.onionlocationpopupnotificationanchor = win.document.getElementById(
+ ONIONLOCATION_BUTTON_ID
+ );
+
+ browser._onionLocationPrompt = win.PopupNotifications.show(
+ browser,
+ NOTIFICATION_ID,
+ NOTIFICATION_DESCRIPTION,
+ NOTIFICATION_ANCHOR_ID,
+ mainAction,
+ [cancelAction],
+ options
+ );
+ }
+
+ static setEnabled(browser) {
+ const win = browser.ownerGlobal;
+ const label = win.document.getElementById(ONIONLOCATION_LABEL_ID);
+ label.textContent = STRING_ONION_AVAILABLE;
+ const elem = win.document.getElementById(ONIONLOCATION_BOX_ID);
+ elem.removeAttribute("hidden");
+ }
+
+ static setDisabled(browser) {
+ const win = browser.ownerGlobal;
+ const elem = win.document.getElementById(ONIONLOCATION_BOX_ID);
+ elem.setAttribute("hidden", true);
+ }
+
+ static updateOnionLocationBadge(browser) {
+ if (browser._onionLocation) {
+ OnionLocationParent.setEnabled(browser);
+ OnionLocationParent.showNotification(browser);
+ } else {
+ OnionLocationParent.setDisabled(browser);
+ }
+ }
+}
diff --git a/browser/components/onionservices/content/onionlocation-notification-icons.css b/browser/components/onionservices/content/onionlocation-notification-icons.css
new file mode 100644
index 000000000000..7c8a6d892c6f
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocation-notification-icons.css
@@ -0,0 +1,5 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+
+.onionlocation-notification-icon {
+ display: none;
+}
\ No newline at end of file
diff --git a/browser/components/onionservices/content/onionlocation-urlbar.css b/browser/components/onionservices/content/onionlocation-urlbar.css
new file mode 100644
index 000000000000..91cad5f178d1
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocation-urlbar.css
@@ -0,0 +1,27 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+
+#onion-location-button {
+ list-style-image: url(chrome://browser/content/onionservices/onionlocation.svg);
+}
+
+#onion-location-box {
+ border-radius: 3px;
+ background-color: #6200A4;
+ padding-left: 5px;
+ padding-right: 5px;
+ color: white;
+ -moz-context-properties: fill;
+ fill: white;
+}
+
+#onion-location-box:hover {
+ background-color: #0060DF !important;
+}
+
+toolbar[brighttext] #onion-location-box {
+ background-color: #9400ff;
+}
+
+toolbar[brighttext] #onion-location-box:hover {
+ background-color: #0060DF !important;
+}
diff --git a/browser/components/onionservices/content/onionlocation-urlbar.inc.xhtml b/browser/components/onionservices/content/onionlocation-urlbar.inc.xhtml
new file mode 100644
index 000000000000..b612a4236f3c
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocation-urlbar.inc.xhtml
@@ -0,0 +1,10 @@
+# Copyright (c) 2020, The Tor Project, Inc.
+
+<hbox id="onion-location-box"
+ class="urlbar-icon-wrapper urlbar-page-action"
+ role="button"
+ hidden="true"
+ onclick="OnionLocationParent.buttonClick(event);">
+ <image id="onion-location-button" role="presentation"/>
+ <hbox id="onion-label-container"><label id="onion-label"/></hbox>
+</hbox>
diff --git a/browser/components/onionservices/content/onionlocation.svg b/browser/components/onionservices/content/onionlocation.svg
new file mode 100644
index 000000000000..37f40ac1812f
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocation.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <path fill="context-fill" fill-opacity="context-fill-opacity" d="m8.016411 14.54499v-0.969784c3.071908-0.0089 5.559239-2.501304 5.559239-5.575429 0-3.073903-2.487331-5.566336-5.559239-5.575206v-0.9697843c3.607473 0.00909 6.528802 2.935521 6.528802 6.544991 0 3.609691-2.921329 6.536342-6.528802 6.545213zm0-3.394356c1.732661-0.0091 3.135111-1.415756 3.135111-3.150857 0-1.734878-1.402451-3.141542-3.135111-3.150634v-0.9695626c2.268448 0.00887 4.104895 1.849753 4.104895 4.120197 0 2.270666-1.836447 4.111549-4.104895 4.120419zm0-4.846926c0.9294227 0.00887 1.680545 0.7644289 1.680545 1.696069 0 0.9318627-0.7511226 1.687421-1.680545 1.696291zm-8.016411 1.696069c0 4.418473 3.581527 8.000222 8 8.000222 4.418251 0 8-3.581749 8-8.000222 0-4.418251-3.581749-7.999778-8-7.999778-4.418473 0-8 3.581527-8 7.999778z" />
+</svg>
\ No newline at end of file
diff --git a/browser/components/onionservices/content/onionlocationPreferences.inc.xhtml b/browser/components/onionservices/content/onionlocationPreferences.inc.xhtml
new file mode 100644
index 000000000000..c285f403f99b
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocationPreferences.inc.xhtml
@@ -0,0 +1,11 @@
+# Copyright (c) 2020, The Tor Project, Inc.
+
+<groupbox id="onionServicesGroup" data-category="panePrivacy" data-subcategory="onionservices" hidden="true">
+ <label><html:h2 id="onionServicesTitle"></html:h2></label>
+ <label><label class="tail-with-learn-more" id="prioritizeOnionsDesc"></label><label
+ class="learnMore" is="text-link" id="onionServicesLearnMore"></label></label>
+ <radiogroup id="prioritizeOnionsRadioGroup" aria-labelledby="prioritizeOnionsDesc" preference="privacy.prioritizeonions.enabled">
+ <radio id="onionServicesRadioAlways" value="true"/>
+ <radio id="onionServicesRadioAsk" value="false"/>
+ </radiogroup>
+</groupbox>
diff --git a/browser/components/onionservices/content/onionlocationPreferences.js b/browser/components/onionservices/content/onionlocationPreferences.js
new file mode 100644
index 000000000000..aa569b54721c
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocationPreferences.js
@@ -0,0 +1,31 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "TorStrings",
+ "resource:///modules/TorStrings.jsm"
+);
+
+const OnionLocationPreferences = {
+ init() {
+ document.getElementById("onionServicesTitle").textContent =
+ TorStrings.onionLocation.onionServicesTitle;
+ document.getElementById("prioritizeOnionsDesc").textContent =
+ TorStrings.onionLocation.prioritizeOnionsDescription;
+ const learnMore = document.getElementById("onionServicesLearnMore");
+ learnMore.textContent = TorStrings.onionLocation.learnMore;
+ learnMore.href = TorStrings.onionLocation.learnMoreURL;
+ document.getElementById("onionServicesRadioAlways").label =
+ TorStrings.onionLocation.always;
+ document.getElementById("onionServicesRadioAsk").label =
+ TorStrings.onionLocation.askEverytime;
+ },
+};
+
+Object.defineProperty(this, "OnionLocationPreferences", {
+ value: OnionLocationPreferences,
+ enumerable: true,
+ writable: false,
+});
diff --git a/browser/components/onionservices/jar.mn b/browser/components/onionservices/jar.mn
index 9d6ce88d1841..f45b16dc5d29 100644
--- a/browser/components/onionservices/jar.mn
+++ b/browser/components/onionservices/jar.mn
@@ -7,3 +7,5 @@ browser.jar:
content/browser/onionservices/onionservices.css (content/onionservices.css)
content/browser/onionservices/savedKeysDialog.js (content/savedKeysDialog.js)
content/browser/onionservices/savedKeysDialog.xhtml (content/savedKeysDialog.xhtml)
+ content/browser/onionservices/onionlocationPreferences.js (content/onionlocationPreferences.js)
+ content/browser/onionservices/onionlocation.svg (content/onionlocation.svg)
diff --git a/browser/components/onionservices/moz.build b/browser/components/onionservices/moz.build
index 815685322024..8027233d65a6 100644
--- a/browser/components/onionservices/moz.build
+++ b/browser/components/onionservices/moz.build
@@ -4,4 +4,6 @@ EXTRA_JS_MODULES += [
"ExtensionMessaging.jsm",
"HttpsEverywhereControl.jsm",
"OnionAliasStore.jsm",
+ "OnionLocationChild.jsm",
+ "OnionLocationParent.jsm",
]
diff --git a/browser/components/preferences/privacy.inc.xhtml b/browser/components/preferences/privacy.inc.xhtml
index cb39d058438d..abaa1561dd47 100644
--- a/browser/components/preferences/privacy.inc.xhtml
+++ b/browser/components/preferences/privacy.inc.xhtml
@@ -14,6 +14,8 @@
<html:h1 data-l10n-id="privacy-header"/>
</hbox>
+#include ../onionservices/content/onionlocationPreferences.inc.xhtml
+
<!-- Tracking / Content Blocking -->
<groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" aria-describedby="contentBlockingDescription">
<label id="contentBlockingHeader"><html:h2 data-l10n-id="content-blocking-enhanced-tracking-protection"/></label>
diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js
index 6b3f05877e5c..7c2ff42e4808 100644
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -93,6 +93,12 @@ XPCOMUtils.defineLazyScriptGetter(
"chrome://browser/content/securitylevel/securityLevel.js"
);
+XPCOMUtils.defineLazyScriptGetter(
+ this,
+ ["OnionLocationPreferences"],
+ "chrome://browser/content/onionservices/onionlocationPreferences.js"
+);
+
XPCOMUtils.defineLazyServiceGetter(
this,
"listManager",
@@ -169,6 +175,9 @@ Preferences.addAll([
// Do not track
{ id: "privacy.donottrackheader.enabled", type: "bool" },
+ // Onion Location
+ { id: "privacy.prioritizeonions.enabled", type: "bool" },
+
// Media
{ id: "media.autoplay.default", type: "int" },
@@ -330,6 +339,13 @@ var gPrivacyPane = {
window.addEventListener("unload", unload);
},
+ /**
+ * Show the OnionLocation preferences UI
+ */
+ _initOnionLocation() {
+ OnionLocationPreferences.init();
+ },
+
/**
* Whether the prompt to restart Firefox should appear when changing the autostart pref.
*/
@@ -531,6 +547,7 @@ var gPrivacyPane = {
this._initTrackingProtectionExtensionControl();
OnionServicesAuthPreferences.init();
this._initSecurityLevel();
+ this._initOnionLocation();
Services.telemetry.setEventRecordingEnabled("pwmgr", true);
diff --git a/browser/themes/shared/notification-icons.inc.css b/browser/themes/shared/notification-icons.inc.css
index d51699544504..e2d7693111a9 100644
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -462,3 +462,5 @@
background: #FFE900 url(chrome://browser/skin/notification-icons/update.svg) no-repeat center;
border-radius: 50%;
}
+
+%include ../../components/onionservices/content/onionlocation-notification-icons.css
\ No newline at end of file
diff --git a/browser/themes/shared/urlbar-searchbar.inc.css b/browser/themes/shared/urlbar-searchbar.inc.css
index e9fde8b8d0db..28d792e78507 100644
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -1061,3 +1061,5 @@ moz-input-box > menupopup .context-menu-add-engine > .menu-iconic-left::after {
.searchbar-engine-one-off-add-engine[image]:hover > .button-box > .button-icon {
opacity: 1;
}
+
+%include ../../components/onionservices/content/onionlocation-urlbar.css
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
index 93baac5ba849..bdeb00029728 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -2796,6 +2796,7 @@ void Document::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
// mDocumentURI.
mDocumentBaseURI = nullptr;
mChromeXHRDocBaseURI = nullptr;
+ mOnionLocationURI = nullptr;
// Check if the current document is the top-level DevTools document.
// For inner DevTools frames, mIsDevToolsDocument will be set when
@@ -6328,6 +6329,22 @@ void Document::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const {
}
}
+static bool IsValidOnionLocation(nsIURI* aDocumentURI,
+ nsIURI* aOnionLocationURI) {
+ bool isHttpish;
+ nsAutoCString host;
+ return aDocumentURI && aOnionLocationURI &&
+ NS_SUCCEEDED(aDocumentURI->SchemeIs("https", &isHttpish)) &&
+ isHttpish && NS_SUCCEEDED(aDocumentURI->GetAsciiHost(host)) &&
+ !StringEndsWith(host, ".onion"_ns) &&
+ ((NS_SUCCEEDED(aOnionLocationURI->SchemeIs("http", &isHttpish)) &&
+ isHttpish) ||
+ (NS_SUCCEEDED(aOnionLocationURI->SchemeIs("https", &isHttpish)) &&
+ isHttpish)) &&
+ NS_SUCCEEDED(aOnionLocationURI->GetAsciiHost(host)) &&
+ StringEndsWith(host, ".onion"_ns);
+}
+
void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
if (!aHeaderField) {
NS_ERROR("null headerField");
@@ -6402,6 +6419,21 @@ void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
aHeaderField == nsGkAtoms::handheldFriendly) {
mViewportType = Unknown;
}
+
+ if (aHeaderField == nsGkAtoms::headerOnionLocation && !aData.IsEmpty()) {
+ nsCOMPtr<nsIURI> onionURI;
+ if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(onionURI), aData)) &&
+ IsValidOnionLocation(Document::GetDocumentURI(), onionURI)) {
+ if (StaticPrefs::privacy_prioritizeonions_enabled()) {
+ nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
+ if (refresher) {
+ refresher->RefreshURI(onionURI, NodePrincipal(), 0, false, true);
+ }
+ } else {
+ mOnionLocationURI = onionURI;
+ }
+ }
+ }
}
void Document::TryChannelCharset(nsIChannel* aChannel, int32_t& aCharsetSource,
@@ -10490,7 +10522,7 @@ void Document::RetrieveRelevantHeaders(nsIChannel* aChannel) {
static const char* const headers[] = {
"default-style", "content-style-type", "content-language",
"content-disposition", "refresh", "x-dns-prefetch-control",
- "x-frame-options",
+ "x-frame-options", "onion-location",
// add more http headers if you need
// XXXbz don't add content-location support without reading bug
// 238654 and its dependencies/dups first.
diff --git a/dom/base/Document.h b/dom/base/Document.h
index 87807e615c68..211570d03e4f 100644
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3391,6 +3391,7 @@ class Document : public nsINode,
void ReleaseCapture() const;
void MozSetImageElement(const nsAString& aImageElementId, Element* aElement);
nsIURI* GetDocumentURIObject() const;
+ nsIURI* GetOnionLocationURI() const { return mOnionLocationURI; }
// Not const because all the fullscreen goop is not const
const char* GetFullscreenError(CallerType);
bool FullscreenEnabled(CallerType aCallerType) {
@@ -4331,6 +4332,7 @@ class Document : public nsINode,
nsCOMPtr<nsIURI> mChromeXHRDocURI;
nsCOMPtr<nsIURI> mDocumentBaseURI;
nsCOMPtr<nsIURI> mChromeXHRDocBaseURI;
+ nsCOMPtr<nsIURI> mOnionLocationURI;
// The base domain of the document for third-party checks.
nsCString mBaseDomain;
diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl
index c77147bc3e85..6ee7b38bac84 100644
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -716,3 +716,11 @@ partial interface Document {
[ChromeOnly]
void setNotifyFormOrPasswordRemoved(boolean aShouldNotify);
};
+
+/**
+ * Extension to allows chrome JS to know whether the document has a valid
+ * Onion-Location that we could redirect to.
+ */
+partial interface Document {
+ [ChromeOnly] readonly attribute URI? onionLocationURI;
+};
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
index 31ac2ae6a394..27c85ed47a9d 100644
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -9748,6 +9748,11 @@
value: false
mirror: always
+- name: privacy.prioritizeonions.enabled
+ type: RelaxedAtomicBool
+ value: false
+ mirror: always
+
#---------------------------------------------------------------------------
# Prefs starting with "prompts."
#---------------------------------------------------------------------------
diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
index cc06e2f41102..9e5ace3e7a43 100644
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -817,6 +817,7 @@ STATIC_ATOMS = [
Atom("oninputsourceschange", "oninputsourceschange"),
Atom("oninstall", "oninstall"),
Atom("oninvalid", "oninvalid"),
+ Atom("headerOnionLocation", "onion-location"),
Atom("onkeydown", "onkeydown"),
Atom("onkeypress", "onkeypress"),
Atom("onkeyup", "onkeyup"),
1
0
[tor-browser/tor-browser-88.0.1-10.0-1] Bug 33852: Clean up about:logins (LockWise) to avoid mentioning sync, etc.
by sysrqb@torproject.org 04 May '21
by sysrqb@torproject.org 04 May '21
04 May '21
commit db171bac582451d13812fefd6b3d31c7e81eeed9
Author: Kathy Brade <brade(a)pearlcrescent.com>
Date: Tue Jul 14 11:15:07 2020 -0400
Bug 33852: Clean up about:logins (LockWise) to avoid mentioning sync, etc.
Hide elements on about:logins that mention sync, "Firefox LockWise", and
Mozilla's LockWise mobile apps.
Disable the "Create New Login" button when security.nocertdb is true.
---
browser/components/aboutlogins/AboutLoginsParent.jsm | 2 ++
browser/components/aboutlogins/content/aboutLogins.css | 8 +++++++-
browser/components/aboutlogins/content/aboutLogins.js | 6 ++++++
.../aboutlogins/content/components/fxaccounts-button.css | 5 +++++
.../components/aboutlogins/content/components/menu-button.css | 10 ++++++++++
5 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/browser/components/aboutlogins/AboutLoginsParent.jsm b/browser/components/aboutlogins/AboutLoginsParent.jsm
index 008d90daad28..7a46da7df882 100644
--- a/browser/components/aboutlogins/AboutLoginsParent.jsm
+++ b/browser/components/aboutlogins/AboutLoginsParent.jsm
@@ -65,6 +65,7 @@ const PASSWORD_SYNC_NOTIFICATION_ID = "enable-password-sync";
const SHOW_PASSWORD_SYNC_NOTIFICATION_PREF =
"signon.management.page.showPasswordSyncNotification";
+const NOCERTDB_PREF = "security.nocertdb";
// about:logins will always use the privileged content process,
// even if it is disabled for other consumers such as about:newtab.
@@ -280,6 +281,7 @@ class AboutLoginsParent extends JSWindowActorParent {
importVisible:
Services.policies.isAllowed("profileImport") &&
AppConstants.platform != "linux",
+ canCreateLogins: !Services.prefs.getBoolPref(NOCERTDB_PREF, false),
});
await AboutLogins._sendAllLoginRelatedObjects(
diff --git a/browser/components/aboutlogins/content/aboutLogins.css b/browser/components/aboutlogins/content/aboutLogins.css
index 79d46c336cc9..d3dd02f80b89 100644
--- a/browser/components/aboutlogins/content/aboutLogins.css
+++ b/browser/components/aboutlogins/content/aboutLogins.css
@@ -69,6 +69,11 @@ login-item {
grid-area: login;
}
+/* Do not promote Mozilla Sync in Tor Browser. */
+login-intro {
+ display: none !important;
+}
+
#branding-logo {
flex-basis: var(--sidebar-width);
flex-shrink: 0;
@@ -83,7 +88,8 @@ login-item {
}
}
-:root:not(.official-branding) #branding-logo {
+/* Hide "Firefox LockWise" branding in Tor Browser. */
+#branding-logo {
visibility: hidden;
}
diff --git a/browser/components/aboutlogins/content/aboutLogins.js b/browser/components/aboutlogins/content/aboutLogins.js
index a7449f8343b3..f288fb6988fb 100644
--- a/browser/components/aboutlogins/content/aboutLogins.js
+++ b/browser/components/aboutlogins/content/aboutLogins.js
@@ -22,6 +22,9 @@ const gElements = {
".menuitem-remove-all-logins"
);
},
+ get createNewLoginButton() {
+ return this.loginList.shadowRoot.querySelector(".create-login-button");
+ },
};
let numberOfLogins = 0;
@@ -111,6 +114,9 @@ window.addEventListener("AboutLoginsChromeToContent", event => {
gElements.loginList.setSortDirection(event.detail.value.selectedSort);
document.documentElement.classList.add("initialized");
gElements.loginList.classList.add("initialized");
+ if (!event.detail.value.canCreateLogins) {
+ gElements.createNewLoginButton.disabled = true;
+ }
break;
}
case "ShowLoginItemError": {
diff --git a/browser/components/aboutlogins/content/components/fxaccounts-button.css b/browser/components/aboutlogins/content/components/fxaccounts-button.css
index e63192a98e3a..c59eda2dec56 100644
--- a/browser/components/aboutlogins/content/components/fxaccounts-button.css
+++ b/browser/components/aboutlogins/content/components/fxaccounts-button.css
@@ -8,6 +8,11 @@
align-items: center;
}
+/* Do not promote Mozilla Sync in Tor Browser. */
+.logged-out-view {
+ display: none !important;
+}
+
.fxaccounts-extra-text {
/* Only show at most 3 lines of text to limit the
text from overflowing the header. */
diff --git a/browser/components/aboutlogins/content/components/menu-button.css b/browser/components/aboutlogins/content/components/menu-button.css
index d5d08b9df58e..788d9899de77 100644
--- a/browser/components/aboutlogins/content/components/menu-button.css
+++ b/browser/components/aboutlogins/content/components/menu-button.css
@@ -90,3 +90,13 @@
.menuitem-preferences {
background-image: url("chrome://global/skin/icons/settings.svg");
}
+
+/*
+ * Do not promote LockWise mobile apps in Tor Browser: hide the menu items
+ * and the separator line that precedes them.
+ */
+.menuitem-mobile-android,
+.menuitem-mobile-ios,
+button[data-event-name="AboutLoginsGetHelp"] + hr {
+ display: none !important;
+}
1
0
[tor-browser/tor-browser-88.0.1-10.0-1] Bug 32418: Allow updates to be disabled via an enterprise policy.
by sysrqb@torproject.org 04 May '21
by sysrqb@torproject.org 04 May '21
04 May '21
commit 23425d895a29d3a9eed05d4ebfa6e86dcff7b5a4
Author: Kathy Brade <brade(a)pearlcrescent.com>
Date: Thu Apr 16 17:07:09 2020 -0400
Bug 32418: Allow updates to be disabled via an enterprise policy.
Restrict the Enterprise Policies mechanism to only consult a
policies.json file (avoiding the Windows Registry and macOS's
file system attributes).
Add a few disabledByPolicy() checks to the update service to
avoid extraneous (and potentially confusing) log messages when
updates are disabled by policy.
Sample content for distribution/policies.json:
{
"policies": {
"DisableAppUpdate": true
}
}
On Linux, avoid reading policies from /etc/firefox/policies/policies.json
---
.../enterprisepolicies/EnterprisePoliciesParent.jsm | 14 ++++++++++++--
toolkit/components/enterprisepolicies/moz.build | 3 +++
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.jsm b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.jsm
index 775144e8a649..9fe916236cbc 100644
--- a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.jsm
+++ b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.jsm
@@ -4,6 +4,10 @@
var EXPORTED_SYMBOLS = ["EnterprisePoliciesManager"];
+// To ensure that policies intended for Firefox or another browser will not
+// be used, Tor Browser only looks for policies in ${InstallDir}/distribution
+#define AVOID_SYSTEM_POLICIES MOZ_PROXY_BYPASS_PROTECTION
+
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
@@ -13,9 +17,11 @@ const { AppConstants } = ChromeUtils.import(
);
XPCOMUtils.defineLazyModuleGetters(this, {
+#ifndef AVOID_SYSTEM_POLICIES
WindowsGPOParser: "resource://gre/modules/policies/WindowsGPOParser.jsm",
macOSPoliciesParser:
"resource://gre/modules/policies/macOSPoliciesParser.jsm",
+#endif
Policies: "resource:///modules/policies/Policies.jsm",
JsonSchemaValidator:
"resource://gre/modules/components-utils/JsonSchemaValidator.jsm",
@@ -140,11 +146,13 @@ EnterprisePoliciesManager.prototype = {
_chooseProvider() {
let platformProvider = null;
+#ifndef AVOID_SYSTEM_POLICIES
if (AppConstants.platform == "win") {
platformProvider = new WindowsGPOPoliciesProvider();
} else if (AppConstants.platform == "macosx") {
platformProvider = new macOSPoliciesProvider();
}
+#endif
let jsonProvider = new JSONPoliciesProvider();
if (platformProvider && platformProvider.hasPolicies) {
if (jsonProvider.hasPolicies) {
@@ -491,7 +499,7 @@ class JSONPoliciesProvider {
_getConfigurationFile() {
let configFile = null;
-
+#ifndef AVOID_SYSTEM_POLICIES
if (AppConstants.platform == "linux") {
let systemConfigFile = Cc["@mozilla.org/file/local;1"].createInstance(
Ci.nsIFile
@@ -504,7 +512,7 @@ class JSONPoliciesProvider {
return systemConfigFile;
}
}
-
+#endif
try {
let perUserPath = Services.prefs.getBoolPref(PREF_PER_USER_DIR, false);
if (perUserPath) {
@@ -585,6 +593,7 @@ class JSONPoliciesProvider {
}
}
+#ifndef AVOID_SYSTEM_POLICIES
class WindowsGPOPoliciesProvider {
constructor() {
this._policies = null;
@@ -686,3 +695,4 @@ class CombinedProvider {
return false;
}
}
+#endif
diff --git a/toolkit/components/enterprisepolicies/moz.build b/toolkit/components/enterprisepolicies/moz.build
index 09d2046e1bd7..3f685d3fbbd6 100644
--- a/toolkit/components/enterprisepolicies/moz.build
+++ b/toolkit/components/enterprisepolicies/moz.build
@@ -19,6 +19,9 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] != "android":
EXTRA_JS_MODULES += [
"EnterprisePolicies.jsm",
"EnterprisePoliciesContent.jsm",
+ ]
+
+ EXTRA_PP_JS_MODULES += [
"EnterprisePoliciesParent.jsm",
]
1
0
[tor-browser/tor-browser-88.0.1-10.0-1] Bug 40091: Load HTTPS Everywhere as a builtin addon in desktop
by sysrqb@torproject.org 04 May '21
by sysrqb@torproject.org 04 May '21
04 May '21
commit e68f5e6bde62334ed311279bdfdf24beef0f6d9b
Author: Alex Catarineu <acat(a)torproject.org>
Date: Fri Sep 4 12:34:35 2020 +0200
Bug 40091: Load HTTPS Everywhere as a builtin addon in desktop
This loads HTTPS Everywhere as a builtin addon from a hardcoded
resource:// URI in desktop. It also ensures that the non-builtin
HTTPS Everywhere addon is always uninstalled on browser startup.
The reason of making this desktop-only is that there are some issues
when installing a builtin extension from geckoview side, making
the extension not available on first startup. So, at least for
now we handle the Fenix case separately. See #40118 for a followup
for investigating these.
---
browser/components/BrowserGlue.jsm | 37 ++++++++++++++++++++++
toolkit/components/extensions/Extension.jsm | 10 ++++--
.../mozapps/extensions/internal/XPIProvider.jsm | 13 ++++++++
3 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm
index d6e3303b0e2b..5056af96c6a4 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -42,6 +42,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
DownloadsViewableInternally:
"resource:///modules/DownloadsViewableInternally.jsm",
E10SUtils: "resource://gre/modules/E10SUtils.jsm",
+ ExtensionData: "resource://gre/modules/Extension.jsm",
ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
ExperimentAPI: "resource://nimbus/ExperimentAPI.jsm",
FeatureGate: "resource://featuregates/FeatureGate.jsm",
@@ -118,6 +119,13 @@ XPCOMUtils.defineLazyServiceGetter(
"nsIPushService"
);
+XPCOMUtils.defineLazyServiceGetters(this, {
+ resProto: [
+ "@mozilla.org/network/protocol;1?name=resource",
+ "nsISubstitutingProtocolHandler",
+ ],
+});
+
const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state";
/**
@@ -1414,6 +1422,35 @@ BrowserGlue.prototype = {
"resource://builtin-themes/alpenglow/"
);
+ // Install https-everywhere builtin addon if needed.
+ (async () => {
+ const HTTPS_EVERYWHERE_ID = "https-everywhere-eff(a)eff.org";
+ const HTTPS_EVERYWHERE_BUILTIN_URL =
+ "resource://torbutton/content/extensions/https-everywhere/";
+ // This does something similar as GeckoViewWebExtension.jsm: it tries
+ // to load the manifest to retrieve the version of the builtin and
+ // compares it to the currently installed one to see whether we need
+ // to install or not. Here we delegate that to
+ // AddonManager.maybeInstallBuiltinAddon.
+ try {
+ const resolvedURI = Services.io.newURI(
+ resProto.resolveURI(Services.io.newURI(HTTPS_EVERYWHERE_BUILTIN_URL))
+ );
+ const extensionData = new ExtensionData(resolvedURI);
+ const manifest = await extensionData.loadManifest();
+
+ await AddonManager.maybeInstallBuiltinAddon(
+ HTTPS_EVERYWHERE_ID,
+ manifest.version,
+ HTTPS_EVERYWHERE_BUILTIN_URL
+ );
+ } catch (e) {
+ const log = Log.repository.getLogger("HttpsEverywhereBuiltinLoader");
+ log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
+ log.error("Could not install https-everywhere extension", e);
+ }
+ })();
+
if (AppConstants.MOZ_NORMANDY) {
Normandy.init();
}
diff --git a/toolkit/components/extensions/Extension.jsm b/toolkit/components/extensions/Extension.jsm
index ae2bf4ce646f..fb390131c172 100644
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -212,6 +212,7 @@ const LOGGER_ID_BASE = "addons.webextension.";
const UUID_MAP_PREF = "extensions.webextensions.uuids";
const LEAVE_STORAGE_PREF = "extensions.webextensions.keepStorageOnUninstall";
const LEAVE_UUID_PREF = "extensions.webextensions.keepUuidOnUninstall";
+const PERSISTENT_EXTENSIONS = new Set(["https-everywhere-eff(a)eff.org"]);
const COMMENT_REGEXP = new RegExp(
String.raw`
@@ -358,7 +359,8 @@ var ExtensionAddonObserver = {
);
}
- if (!Services.prefs.getBoolPref(LEAVE_STORAGE_PREF, false)) {
+ if (!Services.prefs.getBoolPref(LEAVE_STORAGE_PREF, false) &&
+ !PERSISTENT_EXTENSIONS.has(addon.id)) {
// Clear browser.storage.local backends.
AsyncShutdown.profileChangeTeardown.addBlocker(
`Clear Extension Storage ${addon.id} (File Backend)`,
@@ -406,7 +408,8 @@ var ExtensionAddonObserver = {
ExtensionPermissions.removeAll(addon.id);
- if (!Services.prefs.getBoolPref(LEAVE_UUID_PREF, false)) {
+ if (!Services.prefs.getBoolPref(LEAVE_UUID_PREF, false) &&
+ !PERSISTENT_EXTENSIONS.has(addon.id)) {
// Clear the entry in the UUID map
UUIDMap.remove(addon.id);
}
@@ -2594,7 +2597,8 @@ class Extension extends ExtensionData {
);
} else if (
this.startupReason === "ADDON_INSTALL" &&
- !Services.prefs.getBoolPref(LEAVE_STORAGE_PREF, false)
+ !Services.prefs.getBoolPref(LEAVE_STORAGE_PREF, false) &&
+ !PERSISTENT_EXTENSIONS.has(this.id)
) {
// If the extension has been just installed, set it as migrated,
// because there will not be any data to migrate.
diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
index 11a3497a03d1..74a26c28e34e 100644
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -1501,6 +1501,19 @@ var XPIStates = {
continue;
}
+ // Uninstall HTTPS Everywhere if it is installed in the user profile.
+ if (
+ id === "https-everywhere-eff(a)eff.org" &&
+ loc.name === KEY_APP_PROFILE
+ ) {
+ logger.debug(
+ "Uninstalling the HTTPS Everywhere extension from user profile."
+ );
+ loc.installer.uninstallAddon(id);
+ changed = true;
+ continue;
+ }
+
let xpiState = loc.get(id);
if (!xpiState) {
// If the location is not supported for sideloading, skip new
1
0
[tor-browser/tor-browser-88.0.1-10.0-1] Bug 40073: Disable remote Public Suffix List fetching
by sysrqb@torproject.org 04 May '21
by sysrqb@torproject.org 04 May '21
04 May '21
commit d6f2cf6a76778394342cb348d0e321798fbb3e96
Author: Alex Catarineu <acat(a)torproject.org>
Date: Thu Aug 13 11:05:03 2020 +0200
Bug 40073: Disable remote Public Suffix List fetching
In https://bugzilla.mozilla.org/show_bug.cgi?id=1563246 Firefox implemented
fetching the Public Suffix List via RemoteSettings and replacing the default
one at runtime, which we do not want.
---
browser/components/BrowserGlue.jsm | 5 -----
1 file changed, 5 deletions(-)
diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm
index 6752cce1566d..d6e3303b0e2b 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -67,7 +67,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
PluralForm: "resource://gre/modules/PluralForm.jsm",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
- PublicSuffixList: "resource://gre/modules/netwerk-dns/PublicSuffixList.jsm",
RemoteSettings: "resource://services-settings/remote-settings.js",
RemoteSecuritySettings:
"resource://gre/modules/psm/RemoteSecuritySettings.jsm",
@@ -2658,10 +2657,6 @@ BrowserGlue.prototype = {
this._addBreachesSyncHandler();
},
- () => {
- PublicSuffixList.init();
- },
-
() => {
RemoteSecuritySettings.init();
},
1
0
[tor-browser/tor-browser-88.0.1-10.0-1] TB3: Tor Browser's official .mozconfigs.
by sysrqb@torproject.org 04 May '21
by sysrqb@torproject.org 04 May '21
04 May '21
commit affb99cad7da9ef93592e7996e53daa6c5dbb933
Author: Mike Perry <mikeperry-git(a)torproject.org>
Date: Mon May 6 15:51:06 2013 -0700
TB3: Tor Browser's official .mozconfigs.
Also:
Bug #9829.1: new .mozconfig file for the new cross-compiler and ESR24
Changes needed to build Mac in 64bit
Bug 10715: Enable Webgl for mingw-w64 again.
Disable ICU when cross-compiling; clean-up.
Bug 15773: Enable ICU on OS X
Bug 15990: Don't build the sandbox with mingw-w64
Bug 12761: Switch to ESR 38 for OS X
Updating .mozconfig-asan
Bug 12516: Compile hardenend Tor Browser with -fwrapv
Bug 18331: Switch to Mozilla's toolchain for building Tor Browser for OS X
Bug 17858: Cannot create incremental MARs for hardened builds.
Define HOST_CFLAGS, etc. to avoid compiling programs such as mbsdiff
(which is part of mar-tools and is not distributed to end-users) with
ASan.
Bug 13419: Add back ICU for Windows
Bug 21239: Use GTK2 for ESR52 Linux builds
Bug 23025: Add hardening flags for macOS
Bug 24478: Enable debug assertions and tests in our ASan builds
--enable-proxy-bypass-protection
Bug 27597: ASan build option in tor-browser-build is broken
Bug 27623 - Export MOZILLA_OFFICIAL during desktop builds
This fixes a problem where some preferences had the wrong default value.
Also see bug 27472 where we made a similar fix for Android.
Bug 30463: Explicitly disable MOZ_TELEMETRY_REPORTING
Bug 31450: Set proper BINDGEN_CFLAGS for ASan builds
Add an --enable-tor-browser-data-outside-app-dir configure option
Add --with-tor-browser-version configure option
Bug 21849: Don't allow SSL key logging.
Bug 31457: disable per-installation profiles
The dedicated profiles (per-installation) feature does not interact
well with our bundled profiles on Linux and Windows, and it also causes
multiple profiles to be created on macOS under TorBrowser-Data.
Bug 31935: Disable profile downgrade protection.
Since Tor Browser does not support more than one profile, disable
the prompt and associated code that offers to create one when a
version downgrade situation is detected.
Bug 32493: Disable MOZ_SERVICES_HEALTHREPORT
Bug 25741 - TBA: Disable features at compile-time
MOZ_NATIVE_DEVICES for casting and the media player
MOZ_TELEMETRY_REPORTING for telemetry
MOZ_DATA_REPORTING for all data reporting preferences (crashreport, telemetry, geo)
Bug 25741 - TBA: Add default configure options in dedicated file
Define MOZ_ANDROID_NETWORK_STATE and MOZ_ANDROID_LOCATION
Bug 29859: Disable HLS support for now
Add --disable-tor-launcher build option
Add --enable-tor-browser-update build option
Bug 33734: Set MOZ_NORMANDY to False
Bug 33851: Omit Parental Controls.
Bug 40061: Omit the Windows default browser agent from the build
Bug 40252: Add --enable-rust-simd to our tor-browser mozconfig files
---
.mozconfig | 39 ++++++++++++++++++++++++
.mozconfig-android | 36 ++++++++++++++++++++++
.mozconfig-asan | 44 +++++++++++++++++++++++++++
.mozconfig-mac | 56 +++++++++++++++++++++++++++++++++++
.mozconfig-mingw | 31 +++++++++++++++++++
browser/base/moz.build | 3 ++
browser/installer/Makefile.in | 8 +++++
browser/moz.configure | 8 ++---
build/moz.configure/old.configure | 5 ++++
mobile/android/confvars.sh | 9 ++++++
mobile/android/geckoview/build.gradle | 1 +
mobile/android/moz.configure | 22 ++++++++++++--
mobile/android/torbrowser.configure | 30 +++++++++++++++++++
old-configure.in | 49 ++++++++++++++++++++++++++++++
security/moz.build | 2 +-
security/nss/lib/ssl/Makefile | 2 +-
toolkit/modules/AppConstants.jsm | 15 ++++++++++
toolkit/modules/moz.build | 3 ++
18 files changed, 355 insertions(+), 8 deletions(-)
diff --git a/.mozconfig b/.mozconfig
new file mode 100755
index 000000000000..18cd1f9b6487
--- /dev/null
+++ b/.mozconfig
@@ -0,0 +1,39 @@
+. $topsrcdir/browser/config/mozconfig
+
+# This mozconfig file is not used in official Tor Browser builds.
+# It is only intended to be used when doing incremental Linux builds
+# during development. The platform-specific mozconfig configuration
+# files used in official Tor Browser releases can be found in the
+# tor-browser-build repo:
+# https://gitweb.torproject.org/builders/tor-browser-build.git/
+# under:
+# tor-browser-build/projects/firefox/mozconfig-$OS-$ARCH
+
+mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-@CONFIG_GUESS@
+mk_add_options MOZ_APP_DISPLAYNAME="Tor Browser"
+export MOZILLA_OFFICIAL=1
+
+ac_add_options --enable-optimize
+ac_add_options --enable-rust-simd
+ac_add_options --enable-official-branding
+
+# Let's support GTK3 for ESR60
+ac_add_options --enable-default-toolkit=cairo-gtk3
+
+ac_add_options --disable-strip
+ac_add_options --disable-install-strip
+ac_add_options --disable-tests
+ac_add_options --disable-debug
+ac_add_options --disable-crashreporter
+ac_add_options --disable-webrtc
+ac_add_options --disable-parental-controls
+# Let's make sure no preference is enabling either Adobe's or Google's CDM.
+ac_add_options --disable-eme
+ac_add_options --enable-proxy-bypass-protection
+
+# Disable telemetry
+ac_add_options MOZ_TELEMETRY_REPORTING=
+
+ac_add_options --disable-tor-launcher
+ac_add_options --with-tor-browser-version=dev-build
+ac_add_options --disable-tor-browser-update
diff --git a/.mozconfig-android b/.mozconfig-android
new file mode 100755
index 000000000000..50015ec615ef
--- /dev/null
+++ b/.mozconfig-android
@@ -0,0 +1,36 @@
+mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-arm-linux-androideabi
+mk_add_options MOZ_APP_DISPLAYNAME="Tor Browser"
+export MOZILLA_OFFICIAL=1
+
+ac_add_options --enable-optimize
+ac_add_options --enable-rust-simd
+ac_add_options --enable-official-branding
+
+# Android
+ac_add_options --enable-application=mobile/android
+ac_add_options --target=arm-linux-androideabi
+ac_add_options --with-android-ndk="$NDK_BASE" #Enter the android ndk location(ndk r17b)
+ac_add_options --with-android-sdk="$SDK_BASE" #Enter the android sdk location
+ac_add_options --with-branding=mobile/android/branding/alpha
+
+# Use Mozilla's Clang blobs
+CC="$HOME/.mozbuild/clang/bin/clang"
+CXX="$HOME/.mozbuild/clang/bin/clang++"
+
+#enable ccache to set amount of cache assigned for build.
+ac_add_options --with-ccache
+
+ac_add_options --enable-strip
+ac_add_options --disable-tests
+ac_add_options --disable-debug
+ac_add_options --disable-rust-debug
+
+ac_add_options --disable-updater
+ac_add_options --disable-crashreporter
+ac_add_options --disable-webrtc
+ac_add_options --disable-parental-controls
+
+ac_add_options --enable-proxy-bypass-protection
+
+# Disable telemetry
+ac_add_options MOZ_TELEMETRY_REPORTING=
diff --git a/.mozconfig-asan b/.mozconfig-asan
new file mode 100644
index 000000000000..bad7ea022c9f
--- /dev/null
+++ b/.mozconfig-asan
@@ -0,0 +1,44 @@
+. $topsrcdir/browser/config/mozconfig
+
+export CFLAGS="-fsanitize=address -Dxmalloc=myxmalloc"
+export CXXFLAGS="-fsanitize=address -Dxmalloc=myxmalloc"
+# We need to add -ldl explicitely due to bug 1213698
+export LDFLAGS="-fsanitize=address -ldl"
+
+# Define HOST_CFLAGS, etc. to avoid compiling programs such as mbsdiff
+# (which is part of mar-tools and is not distributed to end-users) with
+# ASan. See bug 17858.
+export HOST_CFLAGS=""
+export HOST_CXXFLAGS=""
+export HOST_LDFLAGS="-ldl"
+
+mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-@CONFIG_GUESS@
+mk_add_options MOZ_APP_DISPLAYNAME="Tor Browser"
+export MOZILLA_OFFICIAL=1
+export BINDGEN_CFLAGS='--gcc-toolchain=/var/tmp/dist/gcc'
+
+ac_add_options --enable-address-sanitizer
+ac_add_options --disable-jemalloc
+ac_add_options --disable-elf-hack
+
+ac_add_options --enable-optimize
+ac_add_options --enable-rust-simd
+ac_add_options --enable-official-branding
+
+# Let's support GTK3 for ESR60
+ac_add_options --enable-default-toolkit=cairo-gtk3
+
+ac_add_options --enable-tor-browser-update
+
+ac_add_options --disable-strip
+ac_add_options --disable-install-strip
+ac_add_options --enable-tests
+ac_add_options --enable-debug
+ac_add_options --disable-crashreporter
+ac_add_options --disable-webrtc
+ac_add_options --disable-parental-controls
+ac_add_options --disable-eme
+ac_add_options --enable-proxy-bypass-protection
+
+# Disable telemetry
+ac_add_options MOZ_TELEMETRY_REPORTING=
diff --git a/.mozconfig-mac b/.mozconfig-mac
new file mode 100644
index 000000000000..26e2b6b92fdb
--- /dev/null
+++ b/.mozconfig-mac
@@ -0,0 +1,56 @@
+# ld needs libLTO.so from llvm
+mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/clang/lib"
+
+CROSS_CCTOOLS_PATH=$topsrcdir/cctools
+CROSS_SYSROOT=$topsrcdir/MacOSX10.7.sdk
+CROSS_PRIVATE_FRAMEWORKS=$CROSS_SYSROOT/System/Library/PrivateFrameworks
+HARDENING_FLAGS="-Werror=format -Werror=format-security -fstack-protector-strong -D_FORTIFY_SOURCE=2"
+FLAGS="-target x86_64-apple-darwin10 -mlinker-version=136 -B $CROSS_CCTOOLS_PATH/bin -isysroot $CROSS_SYSROOT $HARDENING_FLAGS"
+
+export CC="$topsrcdir/clang/bin/clang $FLAGS"
+export CXX="$topsrcdir/clang/bin/clang++ $FLAGS"
+export CPP="$topsrcdir/clang/bin/clang $FLAGS -E"
+export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
+export LDFLAGS="-Wl,-syslibroot,$CROSS_SYSROOT -Wl,-dead_strip -Wl,-pie"
+export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin10-
+#TODO: bug 1184202 - would be nice if these could be detected with TOOLCHAIN_PREFIX automatically
+export AR=${TOOLCHAIN_PREFIX}ar
+export RANLIB=${TOOLCHAIN_PREFIX}ranlib
+export STRIP=${TOOLCHAIN_PREFIX}strip
+export OTOOL=${TOOLCHAIN_PREFIX}otool
+export DSYMUTIL=$topsrcdir/clang/bin/llvm-dsymutil
+
+export HOST_CC="$topsrcdir/clang/bin/clang"
+export HOST_CXX="$topsrcdir/clang/bin/clang++"
+export HOST_CPP="$topsrcdir/clang/bin/clang -E"
+export HOST_CFLAGS="-g"
+export HOST_CXXFLAGS="-g"
+export HOST_LDFLAGS="-g"
+
+ac_add_options --target=x86_64-apple-darwin
+ac_add_options --with-macos-private-frameworks=$CROSS_PRIVATE_FRAMEWORKS
+
+mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-macos
+mk_add_options MOZ_APP_DISPLAYNAME="Tor Browser"
+export MOZILLA_OFFICIAL=1
+
+ac_add_options --enable-application=browser
+ac_add_options --enable-strip
+ac_add_options --enable-official-branding
+ac_add_options --enable-optimize
+ac_add_options --enable-rust-simd
+ac_add_options --disable-debug
+
+ac_add_options --enable-tor-browser-data-outside-app-dir
+ac_add_options --enable-tor-browser-update
+
+ac_add_options --disable-crashreporter
+ac_add_options --disable-webrtc
+ac_add_options --disable-parental-controls
+ac_add_options --disable-tests
+# Let's make sure no preference is enabling either Adobe's or Google's CDM.
+ac_add_options --disable-eme
+ac_add_options --enable-proxy-bypass-protection
+
+# Disable telemetry
+ac_add_options MOZ_TELEMETRY_REPORTING=
diff --git a/.mozconfig-mingw b/.mozconfig-mingw
new file mode 100644
index 000000000000..3ec6ff18a3e9
--- /dev/null
+++ b/.mozconfig-mingw
@@ -0,0 +1,31 @@
+CROSS_COMPILE=1
+
+ac_add_options --enable-application=browser
+ac_add_options --target=i686-w64-mingw32
+ac_add_options --with-toolchain-prefix=i686-w64-mingw32-
+ac_add_options --enable-default-toolkit=cairo-windows
+mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-mingw
+mk_add_options MOZ_APP_DISPLAYNAME="Tor Browser"
+export MOZILLA_OFFICIAL=1
+
+ac_add_options --disable-debug
+ac_add_options --enable-optimize
+ac_add_options --enable-rust-simd
+ac_add_options --enable-strip
+ac_add_options --enable-official-branding
+
+ac_add_options --enable-tor-browser-update
+ac_add_options --disable-bits-download
+
+# Let's make sure no preference is enabling either Adobe's or Google's CDM.
+ac_add_options --disable-eme
+ac_add_options --disable-crashreporter
+ac_add_options --disable-maintenance-service
+ac_add_options --disable-webrtc
+ac_add_options --disable-parental-controls
+ac_add_options --disable-tests
+ac_add_options --enable-proxy-bypass-protection
+
+# Disable telemetry
+ac_add_options MOZ_TELEMETRY_REPORTING=
+ac_add_options --disable-default-browser-agent
diff --git a/browser/base/moz.build b/browser/base/moz.build
index c97c964da0e6..fa33da3865b2 100644
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -80,6 +80,9 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("windows", "gtk", "cocoa"):
if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("windows", "gtk"):
DEFINES["MENUBAR_CAN_AUTOHIDE"] = 1
+if CONFIG["TOR_BROWSER_UPDATE"]:
+ DEFINES["TOR_BROWSER_UPDATE"] = 1
+
JAR_MANIFESTS += ["jar.mn"]
GeneratedFile(
diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in
index ad725c5114b0..4fa715669e8b 100644
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -82,6 +82,14 @@ endif
endif
endif
+ifdef TOR_BROWSER_DISABLE_TOR_LAUNCHER
+DEFINES += -DTOR_BROWSER_DISABLE_TOR_LAUNCHER
+endif
+
+ifdef TOR_BROWSER_UPDATE
+DEFINES += -DTOR_BROWSER_UPDATE
+endif
+
ifneq (,$(filter WINNT Darwin Android,$(OS_TARGET)))
DEFINES += -DMOZ_SHARED_MOZGLUE=1
endif
diff --git a/browser/moz.configure b/browser/moz.configure
index 8653bcbb165d..5a0b722b915e 100644
--- a/browser/moz.configure
+++ b/browser/moz.configure
@@ -5,11 +5,11 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
imply_option("MOZ_PLACES", True)
-imply_option("MOZ_SERVICES_HEALTHREPORT", True)
+imply_option("MOZ_SERVICES_HEALTHREPORT", False)
imply_option("MOZ_SERVICES_SYNC", True)
-imply_option("MOZ_DEDICATED_PROFILES", True)
-imply_option("MOZ_BLOCK_PROFILE_DOWNGRADE", True)
-imply_option("MOZ_NORMANDY", True)
+imply_option("MOZ_DEDICATED_PROFILES", False)
+imply_option("MOZ_BLOCK_PROFILE_DOWNGRADE", False)
+imply_option("MOZ_NORMANDY", False)
with only_when(target_is_linux & compile_environment):
option(env="MOZ_NO_PIE_COMPAT", help="Enable non-PIE wrapper")
diff --git a/build/moz.configure/old.configure b/build/moz.configure/old.configure
index 35ab75df3a14..95f4200d0973 100644
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -119,6 +119,11 @@ def old_configure_options(*options):
"--with-user-appdir",
"--x-includes",
"--x-libraries",
+ # Tor additions.
+ "--with-tor-browser-version",
+ "--enable-tor-browser-update",
+ "--enable-tor-browser-data-outside-app-dir",
+ "--enable-tor-launcher",
)
def prepare_configure_options(host, target, all_options, *options):
# old-configure only supports the options listed in @old_configure_options
diff --git a/mobile/android/confvars.sh b/mobile/android/confvars.sh
index 70e13c85b258..b2670451ed91 100644
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -29,6 +29,15 @@ MOZ_ANDROID_BROWSER_INTENT_CLASS=org.mozilla.gecko.BrowserApp
MOZ_NO_SMART_CARDS=1
+# Adds MIME-type support for raw video
MOZ_RAW=1
MOZ_APP_ID={aa3c5121-dab2-40e2-81ca-7ea25febc110}
+
+### Tor Browser for Android ###
+
+# Disable telemetry at compile-time
+unset MOZ_TELEMETRY_REPORTING
+
+# Disable data reporting at compile-time
+unset MOZ_DATA_REPORTING
diff --git a/mobile/android/geckoview/build.gradle b/mobile/android/geckoview/build.gradle
index 28841eb020d6..e50671c35dc4 100644
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -93,6 +93,7 @@ android {
buildConfigField 'String', "MOZ_APP_DISPLAYNAME", "\"${mozconfig.substs.MOZ_APP_DISPLAYNAME}\"";
buildConfigField 'String', "MOZ_APP_UA_NAME", "\"${mozconfig.substs.MOZ_APP_UA_NAME}\"";
buildConfigField 'String', "MOZ_UPDATE_CHANNEL", "\"${mozconfig.substs.MOZ_UPDATE_CHANNEL}\"";
+ buildConfigField 'String', "TOR_BROWSER_VERSION", "\"${mozconfig.substs.TOR_BROWSER_VERSION}\"";
// MOZILLA_VERSION is oddly quoted from autoconf, but we don't have to handle it specially in Gradle.
buildConfigField 'String', "MOZILLA_VERSION", "\"${mozconfig.substs.MOZILLA_VERSION}\"";
diff --git a/mobile/android/moz.configure b/mobile/android/moz.configure
index a1d3f4e65c48..70822bdfcafd 100644
--- a/mobile/android/moz.configure
+++ b/mobile/android/moz.configure
@@ -13,7 +13,7 @@ project_flag(
project_flag(
"MOZ_ANDROID_HLS_SUPPORT",
help="Enable HLS (HTTP Live Streaming) support (currently using the ExoPlayer library)",
- default=True,
+ default=False,
)
option(
@@ -62,10 +62,14 @@ def fennec_nightly(nightly):
imply_option("MOZ_NORMANDY", False)
-imply_option("MOZ_SERVICES_HEALTHREPORT", True)
imply_option("MOZ_ANDROID_HISTORY", True)
imply_option("--enable-small-chunk-size", True)
+# Comment this so we can imply |False| in torbrowser.configure
+# The Build system doesn't allow multiple imply_option()
+# calls with the same key.
+# imply_option("MOZ_SERVICES_HEALTHREPORT", True)
+
@depends(target)
def check_target(target):
@@ -81,6 +85,8 @@ def check_target(target):
)
+include("torbrowser.configure")
+
include("../../toolkit/moz.configure")
include("../../build/moz.configure/android-sdk.configure")
include("../../build/moz.configure/java.configure")
@@ -98,3 +104,15 @@ set_config(
"MOZ_ANDROID_FAT_AAR_ARCHITECTURES",
depends("MOZ_ANDROID_FAT_AAR_ARCHITECTURES")(lambda x: x),
)
+
+project_flag(
+ "MOZ_ANDROID_NETWORK_STATE",
+ help="Include permission for accessing WiFi/network state on Android",
+ default=False,
+)
+
+project_flag(
+ "MOZ_ANDROID_LOCATION",
+ help="Include permission for accessing fine and course-grain Location on Android",
+ default=False,
+)
diff --git a/mobile/android/torbrowser.configure b/mobile/android/torbrowser.configure
new file mode 100644
index 000000000000..bcb725cae121
--- /dev/null
+++ b/mobile/android/torbrowser.configure
@@ -0,0 +1,30 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Set Tor Browser default config
+
+imply_option("MOZ_ANDROID_EXCLUDE_FONTS", False)
+
+# Disable uploading crash reports and dump files to an external server
+# This is still configured in old-configure. Uncomment when this moves
+# to the python config
+# imply_option("MOZ_CRASHREPORTER", False)
+
+# Disable uploading information about the browser configuration and
+# performance to an external server
+imply_option("MOZ_SERVICES_HEALTHREPORT", False)
+
+# Disable creating telemetry and data reports that are uploaded to an
+# external server
+# These aren't actually configure options. These are disabled in
+# confvars.sh, but they look like configure options so we'll document
+# them here, as well.
+# XXX: no confvars.sh here
+# imply_option("MOZ_TELEMETRY_REPORTING", False)
+# imply_option("MOZ_DATA_REPORTING", False)
+
+imply_option("MOZ_ANDROID_NETWORK_STATE", False)
+imply_option("MOZ_ANDROID_LOCATION", False)
diff --git a/old-configure.in b/old-configure.in
index a05ac13cd110..2b1faf6c0b82 100644
--- a/old-configure.in
+++ b/old-configure.in
@@ -1887,6 +1887,55 @@ if test -n "$MOZ_UPDATER"; then
AC_DEFINE(MOZ_UPDATER)
fi
+dnl ========================================================
+dnl Tor additions
+dnl ========================================================
+MOZ_ARG_WITH_STRING(tor-browser-version,
+[ --with-tor-browser-version=VERSION
+ Set Tor Browser version, e.g., 7.0a1],
+ TOR_BROWSER_VERSION="$withval")
+
+if test -z "$TOR_BROWSER_VERSION"; then
+ AC_MSG_ERROR([--with-tor-browser-version is required for Tor Browser.])
+fi
+
+MOZ_ARG_ENABLE_BOOL(tor-browser-update,
+[ --enable-tor-browser-update
+ Enable Tor Browser update],
+ TOR_BROWSER_UPDATE=1,
+ TOR_BROWSER_UPDATE= )
+
+if test -n "$TOR_BROWSER_UPDATE"; then
+ AC_DEFINE(TOR_BROWSER_UPDATE)
+fi
+
+MOZ_ARG_ENABLE_BOOL(tor-browser-data-outside-app-dir,
+[ --enable-tor-browser-data-outside-app-dir
+ Enable Tor Browser data outside of app directory],
+ TOR_BROWSER_DATA_OUTSIDE_APP_DIR=1,
+ TOR_BROWSER_DATA_OUTSIDE_APP_DIR= )
+
+if test -n "$TOR_BROWSER_DATA_OUTSIDE_APP_DIR"; then
+ AC_DEFINE(TOR_BROWSER_DATA_OUTSIDE_APP_DIR)
+fi
+
+AC_DEFINE_UNQUOTED(TOR_BROWSER_VERSION,$TOR_BROWSER_VERSION)
+AC_DEFINE_UNQUOTED(TOR_BROWSER_VERSION_QUOTED,"$TOR_BROWSER_VERSION")
+AC_SUBST(TOR_BROWSER_UPDATE)
+AC_SUBST(TOR_BROWSER_DATA_OUTSIDE_APP_DIR)
+
+MOZ_ARG_DISABLE_BOOL(tor-launcher,
+[ --disable-tor-launcher
+ Do not include Tor Launcher],
+ TOR_BROWSER_DISABLE_TOR_LAUNCHER=1,
+ TOR_BROWSER_DISABLE_TOR_LAUNCHER=)
+
+if test -n "$TOR_BROWSER_DISABLE_TOR_LAUNCHER"; then
+ AC_DEFINE(TOR_BROWSER_DISABLE_TOR_LAUNCHER)
+fi
+
+AC_SUBST(TOR_BROWSER_DISABLE_TOR_LAUNCHER)
+
dnl ========================================================
dnl parental controls (for Windows Vista)
dnl ========================================================
diff --git a/security/moz.build b/security/moz.build
index 18e50f9dcc37..8d0427525487 100644
--- a/security/moz.build
+++ b/security/moz.build
@@ -85,7 +85,7 @@ gyp_vars["nss_dist_obj_dir"] = "$PRODUCT_DIR/dist/bin"
gyp_vars["disable_tests"] = 1
gyp_vars["disable_dbm"] = 1
gyp_vars["disable_libpkix"] = 1
-gyp_vars["enable_sslkeylogfile"] = 1
+gyp_vars["enable_sslkeylogfile"] = 0
# pkg-config won't reliably find zlib on our builders, so just force it.
# System zlib is only used for modutil and signtool unless
# SSL zlib is enabled, which we are disabling immediately below this.
diff --git a/security/nss/lib/ssl/Makefile b/security/nss/lib/ssl/Makefile
index 8a8b06f4b508..90571bb3e256 100644
--- a/security/nss/lib/ssl/Makefile
+++ b/security/nss/lib/ssl/Makefile
@@ -41,7 +41,7 @@ endif
# Enable key logging by default in debug builds, but not opt builds.
# Logging still needs to be enabled at runtime through env vars.
-NSS_ALLOW_SSLKEYLOGFILE ?= $(if $(BUILD_OPT),0,1)
+NSS_ALLOW_SSLKEYLOGFILE ?= 0
ifeq (1,$(NSS_ALLOW_SSLKEYLOGFILE))
DEFINES += -DNSS_ALLOW_SSLKEYLOGFILE=1
endif
diff --git a/toolkit/modules/AppConstants.jsm b/toolkit/modules/AppConstants.jsm
index 49f54246430c..41ed947d0526 100644
--- a/toolkit/modules/AppConstants.jsm
+++ b/toolkit/modules/AppConstants.jsm
@@ -343,6 +343,14 @@ this.AppConstants = Object.freeze({
MOZ_WIDGET_TOOLKIT: "@MOZ_WIDGET_TOOLKIT@",
ANDROID_PACKAGE_NAME: "@ANDROID_PACKAGE_NAME@",
+ TOR_BROWSER_VERSION: "@TOR_BROWSER_VERSION@",
+ TOR_BROWSER_DATA_OUTSIDE_APP_DIR:
+#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR
+ true,
+#else
+ false,
+#endif
+
DEBUG_JS_MODULES: "@DEBUG_JS_MODULES@",
MOZ_BING_API_CLIENTID: "@MOZ_BING_API_CLIENTID@",
@@ -427,4 +435,11 @@ this.AppConstants = Object.freeze({
#else
false,
#endif
+
+ TOR_BROWSER_UPDATE:
+#ifdef TOR_BROWSER_UPDATE
+ true,
+#else
+ false,
+#endif
});
diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build
index 46d53cdc09fc..b34e9e1df874 100644
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -307,6 +307,9 @@ for var in (
if CONFIG[var]:
DEFINES[var] = True
+if CONFIG["TOR_BROWSER_UPDATE"]:
+ DEFINES["TOR_BROWSER_UPDATE"] = 1
+
JAR_MANIFESTS += ["jar.mn"]
DEFINES["TOPOBJDIR"] = TOPOBJDIR
1
0