tbb-commits
Threads by month
- ----- 2025 -----
- 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
- 1 participants
- 18563 discussions

[tor-browser-build/master] Bug 31932: Copy snowflake's license to the bundled data
by gk@torproject.org 15 Oct '19
by gk@torproject.org 15 Oct '19
15 Oct '19
commit 9b0d98a51abdbad31d131b768e09e9a0c98b3f15
Author: Arlo Breault <arlolra(a)gmail.com>
Date: Fri Oct 11 20:07:14 2019 -0400
Bug 31932: Copy snowflake's license to the bundled data
---
projects/snowflake/build | 2 +-
.../Licenses/PluggableTransports/LICENSE.SNOWFLAKE | 32 ++++++++++++++++++++++
2 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/projects/snowflake/build b/projects/snowflake/build
index 5309318..76546d8 100644
--- a/projects/snowflake/build
+++ b/projects/snowflake/build
@@ -21,7 +21,7 @@ go build -ldflags '-s'
cp -a client[% IF c("var/windows") %].exe[% END %] $PTDIR/snowflake-client[% IF c("var/windows") %].exe[% END %]
cd ..
-cp -a README.md LICENSE $DOCSDIR
+cp -a README.md $DOCSDIR
cd $distdir
[% c('tar', {
diff --git a/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE.SNOWFLAKE b/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE.SNOWFLAKE
new file mode 100644
index 0000000..f700c98
--- /dev/null
+++ b/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE.SNOWFLAKE
@@ -0,0 +1,32 @@
+ This file contains the license for "Snowflake"
+ a free software project which provides a WebRTC pluggable transport.
+
+================================================================================
+Copyright (c) 2016, Serene Han, Arlo Breault
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+ * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+================================================================================
1
0

[tor-browser-build/master] Use branch -3 for nightly builds from now on
by gk@torproject.org 13 Oct '19
by gk@torproject.org 13 Oct '19
13 Oct '19
commit e440b661281a35f3be31cd2165b19ab1dda17ca9
Author: Georg Koppen <gk(a)torproject.org>
Date: Sun Oct 13 19:08:14 2019 +0000
Use branch -3 for nightly builds from now on
---
projects/firefox/config | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/projects/firefox/config b/projects/firefox/config
index ac2ceb2..4370f01 100644
--- a/projects/firefox/config
+++ b/projects/firefox/config
@@ -51,7 +51,7 @@ targets:
branding_directory: '[% IF c("var/android") %]mobile/android[% ELSE %]browser[% END %]/branding/official'
nightly:
- git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-2'
+ git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-3'
tag_gpg_id: 0
var:
torbrowser_update_channel: default
1
0

[tor-browser-build/master] Pick up latest browser changes for build2
by gk@torproject.org 12 Oct '19
by gk@torproject.org 12 Oct '19
12 Oct '19
commit dd7cf4d480a74f416795c449e75ed7d2935956a3
Author: Georg Koppen <gk(a)torproject.org>
Date: Sat Oct 12 19:37:49 2019 +0000
Pick up latest browser changes for build2
---
projects/firefox/config | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/projects/firefox/config b/projects/firefox/config
index 55de6de..ac2ceb2 100644
--- a/projects/firefox/config
+++ b/projects/firefox/config
@@ -1,7 +1,7 @@
# vim: filetype=yaml sw=2
version: '[% c("abbrev") %]'
filename: 'firefox-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %]'
-git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-2-build4'
+git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-2-build5'
tag_gpg_id: 1
git_url: https://git.torproject.org/tor-browser.git
git_submodule: 1
1
0
commit 7a6350089a20d864b338bff35d13672479991b26
Author: Georg Koppen <gk(a)torproject.org>
Date: Sat Oct 12 19:38:15 2019 +0000
9.0a8-build2
---
rbm.conf | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rbm.conf b/rbm.conf
index eae7d86..3ba38e4 100644
--- a/rbm.conf
+++ b/rbm.conf
@@ -25,7 +25,7 @@ buildconf:
var:
torbrowser_version: '9.0a8'
- torbrowser_build: 'build1'
+ torbrowser_build: 'build2'
torbrowser_incremental_from:
- 9.0a7
project_name: tor-browser
1
0

[tor-browser/tor-browser-68.1.0esr-9.0-2] fixup! TB4: Tor Browser's Firefox preference overrides.
by gk@torproject.org 12 Oct '19
by gk@torproject.org 12 Oct '19
12 Oct '19
commit 5e45bc82579d4d712e0f34cb58ad62f1030127ca
Author: Alex Catarineu <acat(a)torproject.org>
Date: Thu Oct 3 16:45:57 2019 +0200
fixup! TB4: Tor Browser's Firefox preference overrides.
Bug 19417: reenable asmjs
---
browser/app/profile/000-tor-browser.js | 3 ---
1 file changed, 3 deletions(-)
diff --git a/browser/app/profile/000-tor-browser.js b/browser/app/profile/000-tor-browser.js
index c146a83e26fe..78b252846689 100644
--- a/browser/app/profile/000-tor-browser.js
+++ b/browser/app/profile/000-tor-browser.js
@@ -245,9 +245,6 @@ pref("browser.policies.testing.disallowEnterprise", true);
// Security slider
pref("svg.in-content.enabled", true);
pref("mathml.disabled", false);
-// Until we address at least the linkability concerns in #19417 let's disable
-// asmjs.
-pref("javascript.options.asmjs", false);
// Unless we audit wasm properly in #21549 let's disable it.
pref("javascript.options.wasm", false);
// Mozilla keeps still finding critical bugs in Graphite code. Disable it for
1
0
commit c2ba1e9d235da2f0fe5a9ab23162d37f1e2721f5
Author: Georg Koppen <gk(a)torproject.org>
Date: Sat Oct 12 13:24:22 2019 +0000
Release prep for 9.0a8
Changelog update and versions bump
---
projects/firefox/config | 2 +-
.../tor-browser/Bundle-Data/Docs/ChangeLog.txt | 51 ++++++++++++++++++++++
projects/tor-launcher/config | 2 +-
projects/tor/config | 2 +-
rbm.conf | 4 +-
5 files changed, 56 insertions(+), 5 deletions(-)
diff --git a/projects/firefox/config b/projects/firefox/config
index 63d2652..55de6de 100644
--- a/projects/firefox/config
+++ b/projects/firefox/config
@@ -1,7 +1,7 @@
# vim: filetype=yaml sw=2
version: '[% c("abbrev") %]'
filename: 'firefox-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %]'
-git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-2-build3'
+git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-2-build4'
tag_gpg_id: 1
git_url: https://git.torproject.org/tor-browser.git
git_submodule: 1
diff --git a/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt b/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt
index 519e228..56a7725 100644
--- a/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt
+++ b/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt
@@ -1,3 +1,54 @@
+Tor Browser 9.0a8 -- October 14 2019
+ * All Platforms
+ * Bug 13543: Spoof smooth and powerEfficient for Media Capabilities
+ * Bug 28196: about:preferences is not properly translated anymore
+ * Bug 19417: Disable asmjs on safer and safest security levels
+ * Bug 30463: Explicitly disable MOZ_TELEMETRY_REPORTING
+ * Bug 31935: Disable profile downgrade protection
+ * Bug 31811: Backport fix for bug 1554805
+ * Bug 16285: Disable DRM/EME on Android and drop Adobe CDM
+ * Bug 31602: Remove Pocket indicators in UI and disable it
+ * Bug 31914: Fix eslint linter error
+ * Translations update
+ * Windows + OS X + Linux
+ * Update Tor to 0.4.2.2-alpha
+ * Update Tor Launcher to 0.2.19.5
+ * Bug 31286: New strings for about:preferences#tor
+ * Translations update
+ * Bug 31286: Provide network settings on about:preferences#tor
+ * Bug 31886: Fix ko bundle bustage
+ * Bug 31768: Update onboarding for Tor Browser 9
+ * Bug 27511: Add new identity button to toolbar
+ * Bug 31778: Support dark-theme for the Circuit Display UI
+ * Bug 31910: Replace meek_lite with meek in circuit display
+ * Bug 30504: Deal with New Identity related browser console errors
+ * Bug 31929: Don't escape DTD entity in ar
+ * Bug 31747: Some onboarding UI is always shown in English
+ * Bug 32041: Replace = with real hamburguer icon ≡
+ * Windows
+ * Bug 31942: Re-enable signature check for language packs
+ * Bug 29013: Enable stack protection for Firefox on Windows
+ * OS X
+ * Bug 31607: App menu items stop working on macOS
+ * Bug 31955: On macOS avoid throwing inside nonBrowserWindowStartup()
+ * Linux
+ * Bug 31942: Re-enable signature check for language packs
+ * Bug 31968: Don't fail if /proc/cpuinfo is not readable
+ * Bug 24755: Stop using a heredoc in start-tor-browser
+ * Bug 31550: Put curly quotes inside single quotes
+ * Android
+ * Bug 31822: Security slider is not really visible on Android anymore
+ * Build System
+ * All Platforms
+ * Bug 31293: Make sure the lo interface inside the containers is up
+ * Windows
+ * Bug 29013: Enable stack protection support for Firefox on Windows
+ * Android
+ * Bug 31564: Make Android bundles based on ESR 68 reproducible
+ * Bug 31981: Remove require-api.patch
+ * Bug 31979: TOPL: Sort dependency list
+ * Bug 30665: Remove unnecessary build patches for Firefox
+
Tor Browser 9.0a7 -- October 1 2019
* All platforms
* Bug 30304: Browser locale can be obtained via DTD strings
diff --git a/projects/tor-launcher/config b/projects/tor-launcher/config
index 6b9a3cf..3171be7 100644
--- a/projects/tor-launcher/config
+++ b/projects/tor-launcher/config
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 0.2.19.4
+version: 0.2.19.5
git_url: https://git.torproject.org/tor-launcher.git
git_hash: '[% c("version") %]'
gpg_keyring: torbutton.gpg
diff --git a/projects/tor/config b/projects/tor/config
index 7bec267..ae2ef16 100644
--- a/projects/tor/config
+++ b/projects/tor/config
@@ -1,6 +1,6 @@
# vim: filetype=yaml sw=2
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %]'
-version: 0.4.2.1-alpha
+version: 0.4.2.2-alpha
git_hash: 'tor-[% c("version") %]'
git_url: https://git.torproject.org/tor.git
git_submodule: 1
diff --git a/rbm.conf b/rbm.conf
index 303f0bb..eae7d86 100644
--- a/rbm.conf
+++ b/rbm.conf
@@ -24,10 +24,10 @@ buildconf:
git_signtag_opt: '-s'
var:
- torbrowser_version: '9.0a7'
+ torbrowser_version: '9.0a8'
torbrowser_build: 'build1'
torbrowser_incremental_from:
- - 9.0a6
+ - 9.0a7
project_name: tor-browser
multi_lingual: 0
build_mar: 1
1
0

[tor-browser/tor-browser-68.1.0esr-9.0-2] Update Torbutton to latest master for 9.0a8
by gk@torproject.org 12 Oct '19
by gk@torproject.org 12 Oct '19
12 Oct '19
commit 661fc1772b12fb7745224f97a3c6bd79ce78fc13
Author: Georg Koppen <gk(a)torproject.org>
Date: Sat Oct 12 13:20:07 2019 +0000
Update Torbutton to latest master for 9.0a8
---
toolkit/torproject/torbutton | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/toolkit/torproject/torbutton b/toolkit/torproject/torbutton
index e54639aab0c6..a1fe61ca5e14 160000
--- a/toolkit/torproject/torbutton
+++ b/toolkit/torproject/torbutton
@@ -1 +1 @@
-Subproject commit e54639aab0c60274c14809cd9129b534bb79f92c
+Subproject commit a1fe61ca5e14e166617c516f2af449b043a3ef1c
1
0

[tor-browser/tor-browser-68.1.0esr-9.0-2] fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#tor
by gk@torproject.org 12 Oct '19
by gk@torproject.org 12 Oct '19
12 Oct '19
commit 109c1defa853ec6364c66a72b3554ea05304dd3f
Author: Georg Koppen <gk(a)torproject.org>
Date: Sat Oct 12 13:17:35 2019 +0000
fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#tor
---
browser/modules/BridgeDB.jsm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/browser/modules/BridgeDB.jsm b/browser/modules/BridgeDB.jsm
index 16bf02e6c688..2caa26b4e2e0 100644
--- a/browser/modules/BridgeDB.jsm
+++ b/browser/modules/BridgeDB.jsm
@@ -1,4 +1,4 @@
-"use strict;";
+"use strict";
var EXPORTED_SYMBOLS = ["BridgeDB"];
1
0

[tor-browser/tor-browser-68.1.0esr-9.0-2] Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#tor
by gk@torproject.org 12 Oct '19
by gk@torproject.org 12 Oct '19
12 Oct '19
commit bd4082f2f1db8a1f1c135d6d1689242f7c659a19
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
In addition the following changes have been made:
- The Networking Settings in General preferences has been removed
- TorStrings has been removed from the SecurityLevel component and
moved into a common shared module also used by about:preferences#tor
---
browser/components/moz.build | 1 +
browser/components/preferences/in-content/main.js | 15 -
browser/components/preferences/in-content/main.xul | 56 --
.../preferences/in-content/preferences.js | 2 +
.../preferences/in-content/preferences.xul | 5 +
.../components/preferences/in-content/privacy.js | 1 +
.../securitylevel/content/securityLevel.js | 155 +---
.../torpreferences/content/parseFunctions.jsm | 76 ++
.../torpreferences/content/requestBridgeDialog.jsm | 220 ++++++
.../torpreferences/content/requestBridgeDialog.xul | 35 +
.../torpreferences/content/torBridgeSettings.jsm | 325 +++++++++
.../torpreferences/content/torCategory.inc.xul | 8 +
.../torpreferences/content/torFirewallSettings.jsm | 72 ++
.../torpreferences/content/torLogDialog.jsm | 65 ++
.../torpreferences/content/torLogDialog.xul | 22 +
.../components/torpreferences/content/torPane.js | 802 +++++++++++++++++++++
.../components/torpreferences/content/torPane.xul | 119 +++
.../torpreferences/content/torPreferences.css | 63 ++
.../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 | 203 ++++++
browser/modules/TorStrings.jsm | 326 +++++++++
browser/modules/moz.build | 3 +
26 files changed, 2755 insertions(+), 194 deletions(-)
diff --git a/browser/components/moz.build b/browser/components/moz.build
index 111794a7532c..c0c9629cac65 100644
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -56,6 +56,7 @@ DIRS += [
'syncedtabs',
'uitour',
'urlbar',
+ 'torpreferences',
'translation',
]
diff --git a/browser/components/preferences/in-content/main.js b/browser/components/preferences/in-content/main.js
index 845ef2f61e30..98c73c5ac119 100644
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -365,16 +365,6 @@ var gMainPane = {
});
this.updatePerformanceSettingsBox({ duringChangeEvent: false });
- 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();
}
@@ -464,11 +454,6 @@ var gMainPane = {
gMainPane.updateHardwareAcceleration.bind(gMainPane)
);
setEventListener(
- "connectionSettings",
- "command",
- gMainPane.showConnections
- );
- setEventListener(
"browserContainersCheckbox",
"command",
gMainPane.checkBrowserContainers
diff --git a/browser/components/preferences/in-content/main.xul b/browser/components/preferences/in-content/main.xul
index 83d64f26a62d..85a219ff783b 100644
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -669,60 +669,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"
- icon="network"
- 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-ssl,
- 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-share.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/in-content/preferences.js b/browser/components/preferences/in-content/preferences.js
index d2851f20d2ca..b5b5f87af459 100644
--- a/browser/components/preferences/in-content/preferences.js
+++ b/browser/components/preferences/in-content/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 */
/* global MozXULElement */
"use strict";
@@ -92,6 +93,7 @@ function init_all() {
document.getElementById("template-paneSync").remove();
}
register_module("paneSearchResults", gSearchResultsPane);
+ register_module("paneTor", gTorPane);
gSearchResultsPane.init();
gMainPane.preInit();
diff --git a/browser/components/preferences/in-content/preferences.xul b/browser/components/preferences/in-content/preferences.xul
index 3b07e4596907..7a01443ab048 100644
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -16,6 +16,7 @@
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/containers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/privacy.css"?>
<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelPreferences.css"?>
+<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?>
<!DOCTYPE page [
<!ENTITY % aboutTorDTD SYSTEM "chrome://torbutton/locale/aboutTor.dtd">
@@ -138,6 +139,9 @@
<image class="category-icon"/>
<label class="category-name" flex="1" data-l10n-id="pane-sync-title2"></label>
</richlistitem>
+
+#include ../../torpreferences/content/torCategory.inc.xul
+
</richlistbox>
<spacer flex="1"/>
@@ -195,6 +199,7 @@
#include privacy.xul
#include containers.xul
#include sync.xul
+#include ../../torpreferences/content/torPane.xul
</vbox>
</vbox>
</vbox>
diff --git a/browser/components/preferences/in-content/privacy.js b/browser/components/preferences/in-content/privacy.js
index e9112a2c467e..297d07fadf1f 100644
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -62,6 +62,7 @@ XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function() {
}
});
+// TODO: module import via ChromeUtils.defineModuleGetter
XPCOMUtils.defineLazyScriptGetter(
this,
["SecurityLevelPreferences"],
diff --git a/browser/components/securitylevel/content/securityLevel.js b/browser/components/securitylevel/content/securityLevel.js
index 9965046a7d15..7f307c5df43a 100644
--- a/browser/components/securitylevel/content/securityLevel.js
+++ b/browser/components/securitylevel/content/securityLevel.js
@@ -8,102 +8,11 @@ XPCOMUtils.defineLazyModuleGetters(this, {
PanelMultiView: "resource:///modules/PanelMultiView.jsm",
});
-XPCOMUtils.defineLazyGlobalGetters(this, ["DOMParser"]);
-XPCOMUtils.defineLazyGetter(this, "domParser", () => {
- const parser = new DOMParser();
- parser.forceEnableDTD();
- return parser;
-});
-
-/*
- Security Level Strings
-
- Strings loaded from torbutton, but en-US defaults provided in case torbutton addon not enabled
-*/
-XPCOMUtils.defineLazyGetter(this, "SecurityLevelStrings", function() {
- // copied from testing/marionette/l10n.js
- let localizeEntity = function(urls, id) {
- // Build a string which contains all possible entity locations
- let locations = [];
- urls.forEach((url, index) => {
- locations.push(`<!ENTITY % dtd_${index} SYSTEM "${url}">%dtd_${index};`);
- });
-
- // Use the DOM parser to resolve the entity and extract its real value
- let header = `<?xml version="1.0"?><!DOCTYPE elem [${locations.join("")}]>`;
- let elem = `<elem id="elementID">&${id};</elem>`;
- let doc = domParser.parseFromString(header + elem, "text/xml");
- let element = doc.querySelector("elem[id='elementID']");
-
- if (element === null) {
- throw new Error(`Entity with id='${id}' hasn't been found`);
- }
-
- return element.textContent;
- };
-
- let getString = function(key, fallback) {
- try {
- return localizeEntity(
- ['chrome://torbutton/locale/torbutton.dtd'],
- `torbutton.prefs.sec_${key}`
- );
- } catch (e) { }
- return fallback;
- };
-
- // read localized strings from torbutton; but use hard-coded en-US strings as fallbacks in case of error
- let retval = {
- securityLevel : getString("caption", "Security Level"),
- customWarning : getString("custom_warning", "Custom"),
- overview : getString("overview", "Disable certain web features that can be used to attack your security and anonymity."),
- standard : {
- level : getString("standard_label", "Standard"),
- tooltip : getString("standard_tooltip", "Security Level : Standard"),
- summary : getString("standard_description", "All Tor Browser and website features are enabled."),
- },
- safer : {
- level : getString("safer_label", "Safer"),
- tooltip : getString("safer_tooltip", "Security Level : Safer"),
- summary : getString("safer_description", "Disables website features that are often dangerous, causing some sites to lose functionality."),
- description1 : getString("js_on_https_sites_only", "JavaScript is disabled on non-HTTPS sites."),
- description2 : getString("limit_typography", "Some fonts and math symbols are disabled."),
- description3 : getString("click_to_play_media", "Audio and video (HTML5 media), and WebGL are click-to-play."),
- },
- safest : {
- level : getString("safest_label", "Safest"),
- tooltip : getString("safest_tooltip", "Security Level : Safest"),
- summary : getString("safest_description", "Only allows website features required for static sites and basic services. These changes affect images, media, and scripts."),
- description1 : getString("js_disabled", "JavaScript is disabled by default on all sites."),
- description2 : getString("limit_graphics_and_typography", "Some fonts, icons, math symbols, and images are disabled."),
- description3 : getString("click_to_play_media", "Audio and video (HTML5 media), and WebGL are click-to-play."),
- },
- custom : {
- summary : getString("custom_summary", "Your custom browser preferences have resulted in unusual security settings. For security and privacy reasons, we recommend you choose one of the default security levels."),
- },
- learnMore : getString("learn_more_label", "Learn more"),
- learnMoreURL : function() {
- let locale = "";
- try {
- let { getLocale } =
- Cu.import("resource://torbutton/modules/utils.js", {});
- locale = getLocale();
- } catch(e) {}
-
- if (locale == "") {
- locale = "en-US";
- }
-
- return "https://tb-manual.torproject.org/" + locale + "/security-settings/";
- }(),
- restoreDefaults : getString("restore_defaults", "Restore Defaults"),
- advancedSecuritySettings : getString("advanced_security_settings", "Advanced Security Settings\u2026"),
- };
-
-
- return retval;
-});
-
+ChromeUtils.defineModuleGetter(
+ this,
+ "TorStrings",
+ "resource:///modules/TorStrings.jsm"
+);
/*
Security Level Prefs
@@ -158,8 +67,8 @@ const SecurityLevelButton = {
_populateXUL : function(securityLevelButton) {
if (securityLevelButton != null) {
- securityLevelButton.setAttribute("tooltiptext", SecurityLevelStrings.securityLevel);
- securityLevelButton.setAttribute("label", SecurityLevelStrings.securityLevel);
+ securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.securityLevel);
+ securityLevelButton.setAttribute("label", TorStrings.securityLevel.securityLevel);
}
},
@@ -171,15 +80,15 @@ const SecurityLevelButton = {
switch(securitySlider) {
case 4:
classList.add("standard");
- securityLevelButton.setAttribute("tooltiptext", SecurityLevelStrings.standard.tooltip);
+ securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.standard.tooltip);
break;
case 2:
classList.add("safer");
- securityLevelButton.setAttribute("tooltiptext", SecurityLevelStrings.safer.tooltip);
+ securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.safer.tooltip);
break;
case 1:
classList.add("safest");
- securityLevelButton.setAttribute("tooltiptext", SecurityLevelStrings.safest.tooltip);
+ securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.safest.tooltip);
break;
}
}
@@ -294,12 +203,12 @@ const SecurityLevelPanel = {
let buttonRestoreDefaults = panelview.querySelector("#securityLevel-restoreDefaults");
let buttonAdvancedSecuritySettings = panelview.querySelector("#securityLevel-advancedSecuritySettings");
- labelHeader.setAttribute("value", SecurityLevelStrings.securityLevel);
- labelCustomWarning.setAttribute("value", SecurityLevelStrings.customWarning);
- labelLearnMore.setAttribute("value", SecurityLevelStrings.learnMore);
- labelLearnMore.setAttribute("href", SecurityLevelStrings.learnMoreURL);
- buttonRestoreDefaults.setAttribute("label", SecurityLevelStrings.restoreDefaults);
- buttonAdvancedSecuritySettings.setAttribute("label", SecurityLevelStrings.advancedSecuritySettings);
+ labelHeader.setAttribute("value", TorStrings.securityLevel.securityLevel);
+ labelCustomWarning.setAttribute("value", TorStrings.securityLevel.customWarning);
+ labelLearnMore.setAttribute("value", TorStrings.securityLevel.learnMore);
+ labelLearnMore.setAttribute("href", TorStrings.securityLevel.learnMoreURL);
+ buttonRestoreDefaults.setAttribute("label", TorStrings.securityLevel.restoreDefaults);
+ buttonAdvancedSecuritySettings.setAttribute("label", TorStrings.securityLevel.advancedSecuritySettings);
// rest of the XUL is set based on security prefs
this._configUIFromPrefs();
@@ -328,24 +237,24 @@ const SecurityLevelPanel = {
switch(securitySlider) {
// standard
case 4:
- labelLevel.setAttribute("value", SecurityLevelStrings.standard.level);
- summary.textContent = SecurityLevelStrings.standard.summary;
+ labelLevel.setAttribute("value", TorStrings.securityLevel.standard.level);
+ summary.textContent = TorStrings.securityLevel.standard.summary;
break;
// safer
case 2:
- labelLevel.setAttribute("value", SecurityLevelStrings.safer.level);
- summary.textContent = SecurityLevelStrings.safer.summary;
+ labelLevel.setAttribute("value", TorStrings.securityLevel.safer.level);
+ summary.textContent = TorStrings.securityLevel.safer.summary;
break;
// safest
case 1:
- labelLevel.setAttribute("value", SecurityLevelStrings.safest.level);
- summary.textContent = SecurityLevelStrings.safest.summary;
+ labelLevel.setAttribute("value", TorStrings.securityLevel.safest.level);
+ summary.textContent = TorStrings.securityLevel.safest.summary;
break;
}
// override the summary text with custom warning
if (securityCustom) {
- summary.textContent = SecurityLevelStrings.custom.summary;
+ summary.textContent = TorStrings.securityLevel.custom.summary;
}
},
@@ -425,14 +334,14 @@ const SecurityLevelPreferences =
let groupbox = document.getElementById("securityLevel-groupbox");
let labelHeader = groupbox.querySelector("#securityLevel-header");
- labelHeader.setAttribute("value", SecurityLevelStrings.securityLevel);
+ labelHeader.setAttribute("value", TorStrings.securityLevel.securityLevel);
let spanOverview = groupbox.querySelector("#securityLevel-overview");
- spanOverview.textContent = SecurityLevelStrings.overview;
+ spanOverview.textContent = TorStrings.securityLevel.overview;
let labelLearnMore = groupbox.querySelector("#securityLevel-learnMore");
- labelLearnMore.setAttribute("value", SecurityLevelStrings.learnMore);
- labelLearnMore.setAttribute("href", SecurityLevelStrings.learnMoreURL);
+ labelLearnMore.setAttribute("value", TorStrings.securityLevel.learnMore);
+ labelLearnMore.setAttribute("href", TorStrings.securityLevel.learnMoreURL);
let populateRadioElements = function(vboxQuery, stringStruct) {
let vbox = groupbox.querySelector(vboxQuery);
@@ -441,13 +350,13 @@ const SecurityLevelPreferences =
radio.setAttribute("label", stringStruct.level);
let customWarning = vbox.querySelector("#securityLevel-customWarning");
- customWarning.setAttribute("value", SecurityLevelStrings.customWarning);
+ customWarning.setAttribute("value", TorStrings.securityLevel.customWarning);
let labelSummary = vbox.querySelector("#securityLevel-summary");
labelSummary.textContent = stringStruct.summary;
let labelRestoreDefaults = vbox.querySelector("#securityLevel-restoreDefaults");
- labelRestoreDefaults.setAttribute("value", SecurityLevelStrings.restoreDefaults);
+ labelRestoreDefaults.setAttribute("value", TorStrings.securityLevel.restoreDefaults);
let description1 = vbox.querySelector("#securityLevel-description1");
if (description1) {
@@ -463,9 +372,9 @@ const SecurityLevelPreferences =
}
};
- populateRadioElements("#securityLevel-vbox-standard", SecurityLevelStrings.standard);
- populateRadioElements("#securityLevel-vbox-safer", SecurityLevelStrings.safer);
- populateRadioElements("#securityLevel-vbox-safest", SecurityLevelStrings.safest);
+ populateRadioElements("#securityLevel-vbox-standard", TorStrings.securityLevel.standard);
+ populateRadioElements("#securityLevel-vbox-safer", TorStrings.securityLevel.safer);
+ populateRadioElements("#securityLevel-vbox-safest", TorStrings.securityLevel.safest);
},
_configUIFromPrefs : function() {
diff --git a/browser/components/torpreferences/content/parseFunctions.jsm b/browser/components/torpreferences/content/parseFunctions.jsm
new file mode 100644
index 000000000000..a6e6c554ca63
--- /dev/null
+++ b/browser/components/torpreferences/content/parseFunctions.jsm
@@ -0,0 +1,76 @@
+"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 tring 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' delimited string of bridge string, which we split and trim
+let parseBridgeStrings = function(aBridgeStrings) {
+ let splitStrings = aBridgeStrings.split("\n");
+ return splitStrings.map(val => val.trim());
+};
+
+// 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..b66272edf880
--- /dev/null
+++ b/browser/components/torpreferences/content/requestBridgeDialog.jsm
@@ -0,0 +1,220 @@
+"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._submitCommand = null;
+ this._submitButton = null;
+ this._dialogDescription = null;
+ this._captchaImage = null;
+ this._captchaEntryTextbox = null;
+ this._captchaRefreshCommand = 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",
+ submitCommand: "command#torPreferences-requestBridge-submitCommand",
+ captchaImage: "image#torPreferences-requestBridge-captchaImage",
+ captchaEntryTextbox:
+ "textbox#torPreferences-requestBridge-captchaTextbox",
+ refreshCaptchaCommand:
+ "command#torPreferences-requestBridge-refreshCaptchaCommand",
+ refreshCaptchaButton:
+ "button#torPreferences-requestBridge-refreshCaptchaButton",
+ incorrectCaptchaHbox:
+ "hbox#torPreferences-requestBridge-incorrectCaptchaHbox",
+ incorrectCaptchaLabel:
+ "label#torPreferences-requestBridge-incorrectCaptchaError",
+ };
+ }
+
+ _populateXUL(dialog) {
+ const selectors = RequestBridgeDialog.selectors;
+
+ this._dialog = dialog;
+ this._dialog.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.acceptDialog();
+ }
+ });
+
+ this._submitCommand = this._dialog.querySelector(selectors.submitCommand);
+
+ this._submitButton = this._dialog.getButton(selectors.submitButton);
+ this._submitButton.setAttribute("label", TorStrings.settings.submitCaptcha);
+ this._submitButton.setAttribute("command", this._submitCommand.id);
+ this._submitButton.disabled = true;
+
+ 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;
+ this._captchaEntryTextbox.onkeypress = evt => {
+ const ENTER_KEY = 13;
+ if (evt.keyCode == ENTER_KEY) {
+ // logically same as pressing the 'submit' button of the parent dialog
+ this.onSubmitCaptcha();
+ return false;
+ }
+ return true;
+ };
+ // disable submit if entry textbox is empty
+ this._captchaEntryTextbox.oninput = () => {
+ this._submitButton.disabled = this._captchaEntryTextbox.value == "";
+ };
+
+ this._captchaRefreshCommand = this._dialog.querySelector(
+ selectors.refreshCaptchaCommand
+ );
+ this._captchaRefreshButton = this._dialog.querySelector(
+ selectors.refreshCaptchaButton
+ );
+ this._captchaRefreshButton.setAttribute(
+ "command",
+ this._captchaRefreshCommand.id
+ );
+ 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._dialog.acceptDialog();
+ })
+ .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.xul",
+ "resizable=yes",
+ this,
+ () => {
+ this.close();
+ aCloseCallback(this._bridges);
+ }
+ );
+ }
+}
diff --git a/browser/components/torpreferences/content/requestBridgeDialog.xul b/browser/components/torpreferences/content/requestBridgeDialog.xul
new file mode 100644
index 000000000000..67cae40b9a48
--- /dev/null
+++ b/browser/components/torpreferences/content/requestBridgeDialog.xul
@@ -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"?>
+
+<dialog id="torPreferences-requestBridge-dialog" type="child" class="prefwindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Request Bridge"
+ buttons="cancel,accept"
+ role="dialog">
+ <command id="torPreferences-requestBridge-submitCommand" oncommand="requestBridgeDialog.onSubmitCaptcha();"/>
+ <!-- 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">
+ <textbox id="torPreferences-requestBridge-captchaTextbox" flex="1" />
+ <command id="torPreferences-requestBridge-refreshCaptchaCommand" oncommand="requestBridgeDialog.onRefreshCaptcha();"/>
+ <button id="torPreferences-requestBridge-refreshCaptchaButton" image="chrome://browser/skin/reload.svg"/>
+ </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>
\ 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.xul b/browser/components/torpreferences/content/torCategory.inc.xul
new file mode 100644
index 000000000000..746059358d5f
--- /dev/null
+++ b/browser/components/torpreferences/content/torCategory.inc.xul
@@ -0,0 +1,8 @@
+<richlistitem id="category-tor"
+ class="category"
+ value="paneTor"
+ helpTopic="prefs-tor"
+ align="center">
+ <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..13a8c42884d2
--- /dev/null
+++ b/browser/components/torpreferences/content/torLogDialog.jsm
@@ -0,0 +1,65 @@
+"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;
+ this._dialog.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.xul",
+ "resizable=yes",
+ this
+ );
+ }
+}
diff --git a/browser/components/torpreferences/content/torLogDialog.xul b/browser/components/torpreferences/content/torLogDialog.xul
new file mode 100644
index 000000000000..ae0f4b294204
--- /dev/null
+++ b/browser/components/torpreferences/content/torLogDialog.xul
@@ -0,0 +1,22 @@
+<?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"?>
+
+<dialog id="torPreferences-torLog-dialog" type="child" class="prefwindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ buttons="accept,extra1"
+ role="dialog">
+ <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>
\ 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..08de0613e1d4
--- /dev/null
+++ b/browser/components/torpreferences/content/torPane.js
@@ -0,0 +1,802 @@
+"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: "textbox#torPreferences-localProxy-textboxAddress",
+ proxyPortLabel: "label#torPreferences-localProxy-port",
+ proxyPortTextbox: "input#torPreferences-localProxy-textboxPort",
+ proxyUsernameLabel: "label#torPreferences-localProxy-username",
+ proxyUsernameTextbox: "textbox#torPreferences-localProxy-textboxUsername",
+ proxyPasswordLabel: "label#torPreferences-localProxy-password",
+ proxyPasswordTextbox: "textbox#torPreferences-localProxy-textboxPassword",
+ useFirewallCheckbox: "checkbox#torPreferences-advanced-toggleFirewall",
+ firewallAllowedPortsLabel: "label#torPreferences-advanced-allowedPorts",
+ firewallAllowedPortsTextbox:
+ "textbox#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._bridgeSelectionRadiogroup = prefpane.querySelector(
+ selectors.bridges.bridgeSelectionRadiogroup
+ );
+ this._bridgeSelectionRadiogroup.value = TorBridgeSource.BUILTIN;
+
+ // 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
+ );
+
+ // 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._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
+ );
+
+ // 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._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
+ );
+ for (let currentProxy of mockProxies) {
+ let menuEntry = document.createElement("menuitem");
+ menuEntry.setAttribute("value", currentProxy.value);
+ menuEntry.setAttribute("label", currentProxy.label);
+ this._proxyTypeMenulist.querySelector("menupopup").append(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._proxyPortLabel = prefpane.querySelector(
+ selectors.advanced.proxyPortLabel
+ );
+ this._proxyPortLabel.setAttribute("value", TorStrings.settings.proxyPort);
+ this._proxyPortTextbox = prefpane.querySelector(
+ selectors.advanced.proxyPortTextbox
+ );
+ 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._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
+ );
+
+ // Local firewall
+ this._useFirewallCheckbox = prefpane.querySelector(
+ selectors.advanced.useFirewallCheckbox
+ );
+ this._useFirewallCheckbox.setAttribute(
+ "label",
+ TorStrings.settings.useFirewall
+ );
+ 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
+ );
+
+ // Tor logs
+ prefpane
+ .querySelector(selectors.advanced.torLogsLabel)
+ .setAttribute("value", TorStrings.settings.showTorDaemonLogs);
+ prefpane
+ .querySelector(selectors.advanced.torLogsButton)
+ .setAttribute("label", TorStrings.settings.showLogs);
+
+ // 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.createElement("menuitem");
+ menuEntry.setAttribute("value", currentBridge);
+ menuEntry.setAttribute("label", currentBridge);
+ this._builtinBridgeMenulist
+ .querySelector("menupopup")
+ .append(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();
+ },
+
+ //
+ // 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 brige 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.xul b/browser/components/torpreferences/content/torPane.xul
new file mode 100644
index 000000000000..298c148bcbf2
--- /dev/null
+++ b/browser/components/torpreferences/content/torPane.xul
@@ -0,0 +1,119 @@
+<!-- Tor panel -->
+
+<script type="application/javascript"
+ src="chrome://browser/content/torpreferences/torPane.js"/>
+
+<hbox id="torPreferencesCategory"
+ class="subcategory"
+ hidden="true"
+ data-category="paneTor">
+ <html:h1 id="torPreferences-header"/>
+</hbox>
+
+<groupbox data-category="paneTor" >
+ <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" >
+ <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" oncommand="gTorPane.onToggleBridge(this.checked).onUpdateBridgeSettings();"/>
+ <radiogroup id="torPreferences-bridges-bridgeSelection"
+ oncommand="gTorPane.onSelectBridgeOption(this.value).onUpdateBridgeSettings();">
+ <hbox class="indent">
+ <radio id="torPreferences-bridges-radioBuiltin"/>
+ <spacer flex="1"/>
+ <menulist id="torPreferences-bridges-builtinList" class="torMarginFix" oncommand="gTorPane.onUpdateBridgeSettings();">
+ <menupopup/>
+ </menulist>
+ </hbox>
+ <vbox class="indent">
+ <hbox>
+ <radio id="torPreferences-bridges-radioRequestBridge"/>
+ <space flex="1"/>
+ <button id="torPreferences-bridges-buttonRequestBridge" class="torMarginFix" oncommand="gTorPane.onRequestBridge();"/>
+ </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"
+ onblur="gTorPane.onUpdateBridgeSettings();"/>
+ </vbox>
+ </hbox>
+ </radiogroup>
+</groupbox>
+
+<!-- Advanced -->
+<groupbox id="torPreferences-advanced-group" data-category="paneTor">
+ <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"/>
+ </description>
+ <grid flex="1">
+ <columns>
+ <column flex="0"/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <!-- Local Proxy -->
+ <checkbox id="torPreferences-advanced-toggleProxy" oncommand="gTorPane.onToggleProxy(this.checked).onUpdateProxySettings();"/>
+ <row class="indent" align="center">
+ <label id="torPreferences-localProxy-type"/>
+ <hbox>
+ <spacer flex="1"/>
+ <menulist id="torPreferences-localProxy-builtinList" class="torMarginFix" oncommand="gTorPane.onSelectProxyType(this.value).onUpdateProxySettings();">
+ <menupopup/>
+ </menulist>
+ </hbox>
+ </row>
+ <row class="indent" align="center">
+ <label id="torPreferences-localProxy-address"/>
+ <hbox align="center">
+ <textbox id="torPreferences-localProxy-textboxAddress" class="torMarginFix" flex="4" onblur="gTorPane.onUpdateProxySettings();"/>
+ <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" onblur="gTorPane.onUpdateProxySettings();"/>
+ </hbox>
+ </row>
+ <row class="indent" align="center">
+ <label id="torPreferences-localProxy-username"/>
+ <hbox align="center">
+ <textbox id="torPreferences-localProxy-textboxUsername" class="torMarginFix" flex="1" onblur="gTorPane.onUpdateProxySettings();"/>
+ <label id="torPreferences-localProxy-password"/>
+ <textbox id="torPreferences-localProxy-textboxPassword" class="torMarginFix" type="password" flex="1" onblur="gTorPane.onUpdateProxySettings();"/>
+ </hbox>
+ </row>
+ <!-- Firewall -->
+ <checkbox id="torPreferences-advanced-toggleFirewall" oncommand="gTorPane.onToggleFirewall(this.checked).onUpdateFirewallSettings();"/>
+ <row class="indent" align="center">
+ <label id="torPreferences-advanced-allowedPorts"/>
+ <textbox id="torPreferences-advanced-textboxAllowedPorts" class="torMarginFix" value="80,443" onblur="gTorPane.onUpdateFirewallSettings();"/>
+ </row>
+ </rows>
+ </grid>
+ <hbox id="torPreferences-torDaemon-hbox" align="center">
+ <label id="torPreferences-torLogs"/>
+ <spacer flex="1"/>
+ <button id="torPreferences-buttonTorLogs" class="torMarginFix" oncommand="gTorPane.onViewTorLogs();"/>
+ </hbox>
+</groupbox>
diff --git a/browser/components/torpreferences/content/torPreferences.css b/browser/components/torpreferences/content/torPreferences.css
new file mode 100644
index 000000000000..19c7421cd647
--- /dev/null
+++ b/browser/components/torpreferences/content/torPreferences.css
@@ -0,0 +1,63 @@
+#category-tor > .category-icon {
+ list-style-image: url("chrome://browser/content/torpreferences/torPreferencesIcon.svg");
+}
+
+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..44920322fe4f
--- /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.xul (content/requestBridgeDialog.xul)
+ content/browser/torpreferences/requestBridgeDialog.jsm (content/requestBridgeDialog.jsm)
+ content/browser/torpreferences/torBridgeSettings.jsm (content/torBridgeSettings.jsm)
+ content/browser/torpreferences/torCategory.inc.xul (content/torCategory.inc.xul)
+ content/browser/torpreferences/torFirewallSettings.jsm (content/torFirewallSettings.jsm)
+ content/browser/torpreferences/torLogDialog.jsm (content/torLogDialog.jsm)
+ content/browser/torpreferences/torLogDialog.xul (content/torLogDialog.xul)
+ content/browser/torpreferences/torPane.js (content/torPane.js)
+ content/browser/torpreferences/torPane.xul (content/torPane.xul)
+ 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..7e103239c8d6
--- /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..16bf02e6c688
--- /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..682e7be9de1a
--- /dev/null
+++ b/browser/modules/TorProtocolService.jsm
@@ -0,0 +1,203 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["TorProtocolService"];
+
+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) {
+ // console.log(`${setting} : ${value}`);
+ 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;
+ },
+};
diff --git a/browser/modules/TorStrings.jsm b/browser/modules/TorStrings.jsm
new file mode 100644
index 000000000000..c7bd6f4236ae
--- /dev/null
+++ b/browser/modules/TorStrings.jsm
@@ -0,0 +1,326 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["TorStrings"];
+
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+const { getLocale } = ChromeUtils.import(
+ "resource://torbutton/modules/utils.js"
+);
+
+XPCOMUtils.defineLazyGlobalGetters(this, ["DOMParser"]);
+XPCOMUtils.defineLazyGetter(this, "domParser", () => {
+ const parser = new DOMParser();
+ parser.forceEnableDTD();
+ return parser;
+});
+
+/*
+ Tor String Bundle
+
+ Strings loaded from torbutton/tor-launcher, but provide a fallback in case they aren't available
+*/
+class TorStringBundle {
+ constructor(aBundleURLs, aPrefix) {
+ let locations = [];
+ for (const [index, url] of aBundleURLs.entries()) {
+ locations.push(`<!ENTITY % dtd_${index} SYSTEM "${url}">%dtd_${index};`);
+ }
+ this._locations = locations;
+ this._prefix = aPrefix;
+ }
+
+ // copied from testing/marionette/l10n.js
+ localizeEntity(urls, id) {
+ // Use the DOM parser to resolve the entity and extract its real value
+ let header = `<?xml version="1.0"?><!DOCTYPE elem [${this._locations.join(
+ ""
+ )}]>`;
+ let elem = `<elem id="elementID">&${id};</elem>`;
+ let doc = domParser.parseFromString(header + elem, "text/xml");
+ let element = doc.querySelector("elem[id='elementID']");
+
+ if (element === null) {
+ throw new Error(`Entity with id='${id}' hasn't been found`);
+ }
+
+ return element.textContent;
+ }
+
+ getString(key, fallback) {
+ if (key) {
+ try {
+ return this.localizeEntity(this._bundleURLs, `${this._prefix}${key}`);
+ } catch (e) {}
+ }
+
+ // on failure, assign the fallback if it exists
+ if (fallback) {
+ return fallback;
+ }
+ // otherwise return string key
+ return `$(${key})`;
+ }
+}
+
+/*
+ Security Level Strings
+*/
+var TorStrings = {
+ /*
+ Tor Browser Security Level Strings
+ */
+ securityLevel: (function() {
+ let tsb = new TorStringBundle(
+ ["chrome://torbutton/locale/torbutton.dtd"],
+ "torbutton.prefs.sec_"
+ );
+ let getString = function(key, fallback) {
+ return tsb.getString(key, fallback);
+ };
+
+ // read localized strings from torbutton; but use hard-coded en-US strings as fallbacks in case of error
+ let retval = {
+ securityLevel: getString("caption", "Security Level"),
+ customWarning: getString("custom_warning", "Custom"),
+ overview: getString(
+ "overview",
+ "Disable certain web features that can be used to attack your security and anonymity."
+ ),
+ standard: {
+ level: getString("standard_label", "Standard"),
+ tooltip: getString("standard_tooltip", "Security Level : Standard"),
+ summary: getString(
+ "standard_description",
+ "All Tor Browser and website features are enabled."
+ ),
+ },
+ safer: {
+ level: getString("safer_label", "Safer"),
+ tooltip: getString("safer_tooltip", "Security Level : Safer"),
+ summary: getString(
+ "safer_description",
+ "Disables website features that are often dangerous, causing some sites to lose functionality."
+ ),
+ description1: getString(
+ "js_on_https_sites_only",
+ "JavaScript is disabled on non-HTTPS sites."
+ ),
+ description2: getString(
+ "limit_typography",
+ "Some fonts and math symbols are disabled."
+ ),
+ description3: getString(
+ "click_to_play_media",
+ "Audio and video (HTML5 media), and WebGL are click-to-play."
+ ),
+ },
+ safest: {
+ level: getString("safest_label", "Safest"),
+ tooltip: getString("safest_tooltip", "Security Level : Safest"),
+ summary: getString(
+ "safest_description",
+ "Only allows website features required for static sites and basic services. These changes affect images, media, and scripts."
+ ),
+ description1: getString(
+ "js_disabled",
+ "JavaScript is disabled by default on all sites."
+ ),
+ description2: getString(
+ "limit_graphics_and_typography",
+ "Some fonts, icons, math symbols, and images are disabled."
+ ),
+ description3: getString(
+ "click_to_play_media",
+ "Audio and video (HTML5 media), and WebGL are click-to-play."
+ ),
+ },
+ custom: {
+ summary: getString(
+ "custom_summary",
+ "Your custom browser preferences have resulted in unusual security settings. For security and privacy reasons, we recommend you choose one of the default security levels."
+ ),
+ },
+ learnMore: getString("learn_more_label", "Learn more"),
+ learnMoreURL: `https://tb-manual.torproject.org/${getLocale()}/security-settings/`,
+ restoreDefaults: getString("restore_defaults", "Restore Defaults"),
+ advancedSecuritySettings: getString(
+ "advanced_security_settings",
+ "Advanced Security Settings\u2026"
+ ),
+ };
+ return retval;
+ })() /* Security Level Strings */,
+
+ /*
+ Tor about:preferences#tor Strings
+ */
+ settings: (function() {
+ let tsb = new TorStringBundle(
+ ["chrome://torlauncher/locale/network-settings.dtd"],
+ ""
+ );
+ let getString = function(key, fallback) {
+ return tsb.getString(key, fallback);
+ };
+
+ let retval = {
+ categoryTitle: getString("torPreferences.categoryTitle", "Tor"),
+ torPreferencesHeading: getString(
+ "torPreferences.torSettings",
+ "Tor Settings"
+ ),
+ torPreferencesDescription: getString(
+ "torPreferences.torSettingsDescription",
+ "Tor Browser routes your traffic over the Tor Network, run by thousands of volunteers around the world."
+ ),
+ learnMore: getString("torPreferences.learnMore", "Learn More"),
+ bridgesHeading: getString("torPreferences.bridges", "Bridges"),
+ bridgesDescription: getString(
+ "torPreferences.bridgesDescription",
+ "Bridges help you access the Tor Network in places where Tor is blocked. Depending on where you are, one bridge may work better than another."
+ ),
+ useBridge: getString("torPreferences.useBridge", "Use a bridge"),
+ selectBridge: getString(
+ "torsettings.useBridges.default",
+ "Select a bridge"
+ ),
+ requestBridgeFromTorProject: getString(
+ "torsettings.useBridges.bridgeDB",
+ "Request a bridge from torproject.org"
+ ),
+ requestNewBridge: getString(
+ "torPreferences.requestNewBridge",
+ "Request a New Bridge\u2026"
+ ),
+ provideBridge: getString(
+ "torPreferences.provideBridge",
+ "Provide a bridge"
+ ),
+ provideBridgeDirections: getString(
+ "torsettings.useBridges.label",
+ "Enter bridge information from a trusted source."
+ ),
+ provideBridgePlaceholder: getString(
+ "torsettings.useBridges.placeholder",
+ "type address:port (one per line)"
+ ),
+ advancedHeading: getString("torPreferences.advanced", "Advanced"),
+ advancedDescription: getString(
+ "torPreferences.advancedDescription",
+ "Configure how Tor Browser connects to the internet."
+ ),
+ useLocalProxy: getString("torPreferences.useProxy", "Use a local proxy"),
+ proxyType: getString("torsettings.useProxy.type", "Proxy Type"),
+ proxyTypeSOCKS4: getString("torsettings.useProxy.type.socks4", "SOCKS4"),
+ proxyTypeSOCKS5: getString("torsettings.useProxy.type.socks5", "SOCKS5"),
+ proxyTypeHTTP: getString("torsettings.useProxy.type.http", "HTTP/HTTPS"),
+ proxyAddress: getString("torsettings.useProxy.address", "Address"),
+ proxyAddressPlaceholder: getString(
+ "torsettings.useProxy.address.placeholder",
+ "IP address or hostname"
+ ),
+ proxyPort: getString("torsettings.useProxy.port", "Port"),
+ proxyUsername: getString("torsettings.useProxy.username", "Username"),
+ proxyPassword: getString("torsettings.useProxy.password", "Password"),
+ proxyUsernamePasswordPlaceholder: getString(
+ "torsettings.optional",
+ "Optional"
+ ),
+ useFirewall: getString(
+ "torsettings.firewall.checkbox",
+ "This computer goes through a firewall that only allows connections to certain ports"
+ ),
+ allowedPorts: getString(
+ "torsettings.firewall.allowedPorts",
+ "Allowed Ports"
+ ),
+ allowedPortsPlaceholder: getString(
+ "torPreferences.firewallPortsPlaceholder",
+ "Comma-seperated values"
+ ),
+ requestBridgeDialogTitle: getString(
+ "torPreferences.requestBridgeDialogTitle",
+ "Request Bridge"
+ ),
+ submitCaptcha: getString(
+ "torsettings.useBridges.captchaSubmit",
+ "Submit"
+ ),
+ contactingBridgeDB: getString(
+ "torPreferences.requestBridgeDialogWaitPrompt",
+ "Contacting BridgeDB. Please Wait."
+ ),
+ solveTheCaptcha: getString(
+ "torPreferences.requestBridgeDialogSolvePrompt",
+ "Solve the CAPTCHA to request a bridge."
+ ),
+ captchaTextboxPlaceholder: getString(
+ "torsettings.useBridges.captchaSolution.placeholder",
+ "Enter the characters from the image"
+ ),
+ incorrectCaptcha: getString(
+ "torPreferences.requestBridgeErrorBadSolution",
+ "The solution is not correct. Please try again."
+ ),
+ showTorDaemonLogs: getString(
+ "torPreferences.viewTorLogs",
+ "View the Tor logs."
+ ),
+ showLogs: getString("torPreferences.viewLogs", "View Logs\u2026"),
+ torLogDialogTitle: getString(
+ "torPreferences.torLogsDialogTitle",
+ "Tor Logs"
+ ),
+ copyLog: getString("torsettings.copyLog", "Copy Tor Log to Clipboard"),
+
+ learnMoreTorBrowserURL: `https://tb-manual.torproject.org/${getLocale()}/about/`,
+ learnMoreBridgesURL: `https://tb-manual.torproject.org/${getLocale()}/bridges/`,
+ learnMoreNetworkSettingsURL: `about:blank`,
+ };
+
+ return retval;
+ })() /* Tor Network Settings Strings */,
+
+ /*
+ Tor Deamon Configuration Key Strings
+ */
+
+ // TODO: proper camel case
+ configKeys: {
+ /* Bridge Conf Settings */
+ useBridges: "UseBridges",
+ bridgeList: "Bridge",
+ /* Proxy Conf Strings */
+ socks4Proxy: "Socks4Proxy",
+ socks5Proxy: "Socks5Proxy",
+ socks5ProxyUsername: "Socks5ProxyUsername",
+ socks5ProxyPassword: "Socks5ProxyPassword",
+ httpsProxy: "HTTPSProxy",
+ httpsProxyAuthenticator: "HTTPSProxyAuthenticator",
+ /* Firewall Conf Strings */
+ reachableAddresses: "ReachableAddresses",
+
+ /* BridgeDB Strings */
+ clientTransportPlugin: "ClientTransportPlugin",
+ },
+
+ /*
+ about:config preference keys
+ */
+
+ preferenceKeys: {
+ defaultBridgeType: "extensions.torlauncher.default_bridge_type",
+ recommendedBridgeType:
+ "extensions.torlauncher.default_bridge_recommended_type",
+ },
+
+ /*
+ about:config preference branches
+ */
+ preferenceBranches: {
+ defaultBridge: "extensions.torlauncher.default_bridge.",
+ bridgeDBBridges: "extensions.torlauncher.bridgedb_bridge.",
+ },
+};
diff --git a/browser/modules/moz.build b/browser/modules/moz.build
index 304bd34d2ff2..43f1524f4553 100644
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -134,6 +134,7 @@ XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
EXTRA_JS_MODULES += [
'AboutNewTab.jsm',
'AsyncTabSwitcher.jsm',
+ 'BridgeDB.jsm',
'BrowserUsageTelemetry.jsm',
'BrowserWindowTracker.jsm',
'ContentClick.jsm',
@@ -165,6 +166,8 @@ EXTRA_JS_MODULES += [
'TabsList.jsm',
'TabUnloader.jsm',
'ThemeVariableMap.jsm',
+ 'TorProtocolService.jsm',
+ 'TorStrings.jsm',
'TransientPrefs.jsm',
'webrtcUI.jsm',
'ZoomUI.jsm',
1
0
commit a1fe61ca5e14e166617c516f2af449b043a3ef1c
Author: Georg Koppen <gk(a)torproject.org>
Date: Sat Oct 12 12:09:12 2019 +0000
Translations update
---
chrome/locale/ar/browserOnboarding.properties | 2 +-
chrome/locale/bn-BD/browserOnboarding.properties | 2 +-
chrome/locale/ca/browserOnboarding.properties | 2 +-
chrome/locale/cs/browserOnboarding.properties | 2 +-
chrome/locale/da/browserOnboarding.properties | 2 +-
chrome/locale/de/browserOnboarding.properties | 2 +-
chrome/locale/el/browserOnboarding.properties | 2 +-
chrome/locale/es-AR/browserOnboarding.properties | 2 +-
chrome/locale/eu/browserOnboarding.properties | 2 +-
chrome/locale/fa/browserOnboarding.properties | 2 +-
chrome/locale/fr/browserOnboarding.properties | 8 ++++----
chrome/locale/ga-IE/browserOnboarding.properties | 2 +-
chrome/locale/he/browserOnboarding.properties | 12 ++++++------
chrome/locale/hu/browserOnboarding.properties | 2 +-
chrome/locale/id/browserOnboarding.properties | 2 +-
chrome/locale/is/browserOnboarding.properties | 2 +-
chrome/locale/it/browserOnboarding.properties | 2 +-
chrome/locale/ja/browserOnboarding.properties | 4 ++--
chrome/locale/ja/torbutton.properties | 6 +++---
chrome/locale/ka/browserOnboarding.properties | 2 +-
chrome/locale/ko/browserOnboarding.properties | 2 +-
chrome/locale/mk/browserOnboarding.properties | 12 ++++++------
chrome/locale/nb-NO/browserOnboarding.properties | 8 ++++----
chrome/locale/nl/browserOnboarding.properties | 12 ++++++------
chrome/locale/pl/browserOnboarding.properties | 2 +-
chrome/locale/pt-BR/browserOnboarding.properties | 12 ++++++------
chrome/locale/ro/browserOnboarding.properties | 2 +-
chrome/locale/ru/browserOnboarding.properties | 2 +-
chrome/locale/sv-SE/browserOnboarding.properties | 2 +-
chrome/locale/tr/browserOnboarding.properties | 12 ++++++------
chrome/locale/tr/torbutton.properties | 2 +-
chrome/locale/vi/browserOnboarding.properties | 2 +-
chrome/locale/zh-CN/browserOnboarding.properties | 12 ++++++------
chrome/locale/zh-TW/browserOnboarding.properties | 2 +-
34 files changed, 73 insertions(+), 73 deletions(-)
diff --git a/chrome/locale/ar/browserOnboarding.properties b/chrome/locale/ar/browserOnboarding.properties
index 33aaf5d2..4366f4d4 100644
--- a/chrome/locale/ar/browserOnboarding.properties
+++ b/chrome/locale/ar/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=تحديث
onboarding.tour-tor-toolbar=شريط الأدوات
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=اذهب الى شبكة تور
diff --git a/chrome/locale/bn-BD/browserOnboarding.properties b/chrome/locale/bn-BD/browserOnboarding.properties
index 1b6aa316..9513be00 100644
--- a/chrome/locale/bn-BD/browserOnboarding.properties
+++ b/chrome/locale/bn-BD/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Updated
onboarding.tour-tor-toolbar=Toolbar
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=টর নেটওয়ার্কে যান
diff --git a/chrome/locale/ca/browserOnboarding.properties b/chrome/locale/ca/browserOnboarding.properties
index 247260b3..b3125ed0 100644
--- a/chrome/locale/ca/browserOnboarding.properties
+++ b/chrome/locale/ca/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Actualitzat
onboarding.tour-tor-toolbar=Barra d'eines
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Aneu a la Xarxa Tor
diff --git a/chrome/locale/cs/browserOnboarding.properties b/chrome/locale/cs/browserOnboarding.properties
index 8f6d6e86..de61ab4e 100644
--- a/chrome/locale/cs/browserOnboarding.properties
+++ b/chrome/locale/cs/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Aktualizováno
onboarding.tour-tor-toolbar=Panel nástrojů
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Přejít na síť Tor
diff --git a/chrome/locale/da/browserOnboarding.properties b/chrome/locale/da/browserOnboarding.properties
index 3993caf5..0b6f4ddc 100644
--- a/chrome/locale/da/browserOnboarding.properties
+++ b/chrome/locale/da/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Opdateret
onboarding.tour-tor-toolbar=Værktøjslinje
onboarding.tour-tor-toolbar-update-9.0.title=Farvel Onion-knap.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=Forespørg en ny identitet
onboarding.tour-tor-toolbar-update-9.0.next-button=Gå til Tor-netværk
diff --git a/chrome/locale/de/browserOnboarding.properties b/chrome/locale/de/browserOnboarding.properties
index b6750198..9eea6582 100644
--- a/chrome/locale/de/browserOnboarding.properties
+++ b/chrome/locale/de/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Aktualisiert
onboarding.tour-tor-toolbar=Symbolleiste
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Öffne Tor-Netzwerk
diff --git a/chrome/locale/el/browserOnboarding.properties b/chrome/locale/el/browserOnboarding.properties
index 1f061edc..d76aebc3 100644
--- a/chrome/locale/el/browserOnboarding.properties
+++ b/chrome/locale/el/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Αναβαθμίστηκε
onboarding.tour-tor-toolbar=Γραμμή εργαλείων
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Πηγαίντε στο Δίκτυο Tor
diff --git a/chrome/locale/es-AR/browserOnboarding.properties b/chrome/locale/es-AR/browserOnboarding.properties
index cc8931f8..b3d531b7 100644
--- a/chrome/locale/es-AR/browserOnboarding.properties
+++ b/chrome/locale/es-AR/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Actualizado
onboarding.tour-tor-toolbar=Barra de herramientas
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Ir a la red Tor
diff --git a/chrome/locale/eu/browserOnboarding.properties b/chrome/locale/eu/browserOnboarding.properties
index 35289c49..e9103eaf 100644
--- a/chrome/locale/eu/browserOnboarding.properties
+++ b/chrome/locale/eu/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Eguneratua
onboarding.tour-tor-toolbar=Tresna-barra
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Joan Tor sarera
diff --git a/chrome/locale/fa/browserOnboarding.properties b/chrome/locale/fa/browserOnboarding.properties
index 8926015e..4d25d226 100644
--- a/chrome/locale/fa/browserOnboarding.properties
+++ b/chrome/locale/fa/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=بهروزرسانی شد
onboarding.tour-tor-toolbar=نوار ابزار
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=برو به شبکه تور
diff --git a/chrome/locale/fr/browserOnboarding.properties b/chrome/locale/fr/browserOnboarding.properties
index 01065625..8e1019c3 100644
--- a/chrome/locale/fr/browserOnboarding.properties
+++ b/chrome/locale/fr/browserOnboarding.properties
@@ -15,7 +15,7 @@ onboarding.tour-tor-privacy.button=Poursuivre vers Réseau Tor
onboarding.tour-tor-network=Réseau Tor
onboarding.tour-tor-network.title=Naviguez sur un réseau décentralisé.
onboarding.tour-tor-network.description=Le Navigateur Tor vous connecte au réseau Tor exploité par des milliers de bénévoles dans le monde entier. Contrairement à un RPV, il n’y a pas de point de défaillance unique ou d’entité centralisée auxquels vous devez faire confiance pour profiter d’Internet en toute confidentialité.
-onboarding.tour-tor-network.description-para2=NOUVEAU : paramètres du réseau Tor, dont la possibilité de demander des ponts où Tor est bloqué, se trouvent maintenant dans Préférences.
+onboarding.tour-tor-network.description-para2=NOUVEAU : Paramètres du réseau Tor, dont la possibilité de demander des ponts où Tor est bloqué, se trouvent maintenant dans Préférences.
onboarding.tour-tor-network.action-button=Réglez vos paramètres du réseau Tor
onboarding.tour-tor-network.button=Poursuivre vers Affichage des circuits
@@ -49,10 +49,10 @@ onboarding.tour-tor-update.prefix-new=Nouveau
onboarding.tour-tor-update.prefix-updated=Mis à jour
onboarding.tour-tor-toolbar=Barre d’outils
-onboarding.tour-tor-toolbar-update-9.0.title=Au revoir le bouton oignon.
+onboarding.tour-tor-toolbar-update-9.0.title=Adieu au bouton oignon.
onboarding.tour-tor-toolbar-update-9.0.description=Nous voulons que votre expérience d’utilisation de Tor soit entièrement intégrée au Navigateur Tor.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
-onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
+onboarding.tour-tor-toolbar-update-9.0.description-para2=C’est pourquoi désormais, au lieu d’utiliser le bouton oignon, vous pouvez voir votre circuit Tor par le [i] de la barre d’URL et demander une nouvelle identité en utilisant le bouton de la barre d’outils ou le menu [≡] .
+onboarding.tour-tor-toolbar-update-9.0.button=Comment demander une nouvelle identité
onboarding.tour-tor-toolbar-update-9.0.next-button=Poursuivre vers Réseau Tor
# Circuit Display onboarding.
diff --git a/chrome/locale/ga-IE/browserOnboarding.properties b/chrome/locale/ga-IE/browserOnboarding.properties
index 6ec36431..754e6467 100644
--- a/chrome/locale/ga-IE/browserOnboarding.properties
+++ b/chrome/locale/ga-IE/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Nuashonraithe
onboarding.tour-tor-toolbar=Barra Uirlisí
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Téigh go Líonra Tor
diff --git a/chrome/locale/he/browserOnboarding.properties b/chrome/locale/he/browserOnboarding.properties
index 3da89d1a..d9a89e69 100644
--- a/chrome/locale/he/browserOnboarding.properties
+++ b/chrome/locale/he/browserOnboarding.properties
@@ -15,8 +15,8 @@ onboarding.tour-tor-privacy.button=לך אל רשת Tor
onboarding.tour-tor-network=רשת Tor
onboarding.tour-tor-network.title=טייל ברשת מבוזרת.
onboarding.tour-tor-network.description=דפדפן Tor מחבר אותך אל רשת Tor המופעלת ע"י אלפי מתנדבים ברחבי העולם. בשונה מאשר VPN, אין נקודת כשל בודדת או כניסה ממורכזת שאתה צריך לסמוך עליה על מנת ליהנות מהאינטרנט באופן פרטי.
-onboarding.tour-tor-network.description-para2=NEW: Tor Network Settings, including the ability to request bridges where Tor is blocked, can now be found in Preferences.
-onboarding.tour-tor-network.action-button=Adjust Your Tor Network Settings
+onboarding.tour-tor-network.description-para2=חדש: הגדרות רשת Tor, כולל היכולת לבקש גשרים במקום שבו Tor חסום, יכולות להימצא בהעדפות.
+onboarding.tour-tor-network.action-button=התאם את הגדרות רשת Tor שלך
onboarding.tour-tor-network.button=לך אל תצוגת מעגל
onboarding.tour-tor-circuit-display=תצוגת מעגל
@@ -49,10 +49,10 @@ onboarding.tour-tor-update.prefix-new=חדש
onboarding.tour-tor-update.prefix-updated=מעודכן
onboarding.tour-tor-toolbar=סרגל כלים
-onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
-onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
-onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
+onboarding.tour-tor-toolbar-update-9.0.title=להתראות כפתור בצל.
+onboarding.tour-tor-toolbar-update-9.0.description=אנחנו רוצים את החוויה של שימוש ב־Tor שלך שתהיה משולבת במלואה בתוך דפדפן Tor.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=זאת הסיבה מדוע כעת, ללא שימוש בכפתור הבצל, אתה יכול לראות את מעגל Tor שלך דרך [i] בשורת הכתובת ולבקש זהות חדשה ע"י שימוש בכפתור סרגל הכלים או בתפריט [≡].
+onboarding.tour-tor-toolbar-update-9.0.button=איך לבקש זהות חדשה
onboarding.tour-tor-toolbar-update-9.0.next-button=לך אל רשת Tor
# Circuit Display onboarding.
diff --git a/chrome/locale/hu/browserOnboarding.properties b/chrome/locale/hu/browserOnboarding.properties
index 3eafe389..cbb31d72 100644
--- a/chrome/locale/hu/browserOnboarding.properties
+++ b/chrome/locale/hu/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Frissített
onboarding.tour-tor-toolbar=Toolbar
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Irány a Tor Hálózat
diff --git a/chrome/locale/id/browserOnboarding.properties b/chrome/locale/id/browserOnboarding.properties
index 217ad01c..958fba00 100644
--- a/chrome/locale/id/browserOnboarding.properties
+++ b/chrome/locale/id/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Telah terupdate
onboarding.tour-tor-toolbar=Toolbar
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Pergi ke Jaringan Tor
diff --git a/chrome/locale/is/browserOnboarding.properties b/chrome/locale/is/browserOnboarding.properties
index 81980cb1..656b8ae5 100644
--- a/chrome/locale/is/browserOnboarding.properties
+++ b/chrome/locale/is/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Uppfært
onboarding.tour-tor-toolbar=Verkfærastika
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Fara á Tor-netið
diff --git a/chrome/locale/it/browserOnboarding.properties b/chrome/locale/it/browserOnboarding.properties
index b24127ce..95633f21 100644
--- a/chrome/locale/it/browserOnboarding.properties
+++ b/chrome/locale/it/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Aggiornato
onboarding.tour-tor-toolbar=Barra degli strumenti
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Vai alla rete Tor
diff --git a/chrome/locale/ja/browserOnboarding.properties b/chrome/locale/ja/browserOnboarding.properties
index 64f1baea..e1241ee5 100644
--- a/chrome/locale/ja/browserOnboarding.properties
+++ b/chrome/locale/ja/browserOnboarding.properties
@@ -44,14 +44,14 @@ onboarding.tour-tor-onion-services.description=Onion サービスは、検閲に
onboarding.tour-tor-onion-services.button=Onion サイトを訪問
onboarding.tour-tor-onion-services.next-button=完了
-onboarding.overlay-icon-tooltip-updated2=最新情報\n%S に
+onboarding.overlay-icon-tooltip-updated2=%S の\n変更点をご紹介します
onboarding.tour-tor-update.prefix-new=新機能
onboarding.tour-tor-update.prefix-updated=更新
onboarding.tour-tor-toolbar=ツールバー
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Tor ネットワークへ
diff --git a/chrome/locale/ja/torbutton.properties b/chrome/locale/ja/torbutton.properties
index b30ecc3c..a03e2d7b 100644
--- a/chrome/locale/ja/torbutton.properties
+++ b/chrome/locale/ja/torbutton.properties
@@ -7,7 +7,7 @@ torbutton.circuit_display.tor_bridge = ブリッジ
torbutton.circuit_display.unknown_country = 不明な国
torbutton.circuit_display.guard = ガード
torbutton.circuit_display.guard_note = [ガード] ノードは変更されない場合があります。
-torbutton.circuit_display.learn_more = さらに知る
+torbutton.circuit_display.learn_more = 詳細情報
torbutton.content_sizer.margin_tooltip = Tor Browser はウィンドウのサイズを平凡にするためにこのマージンを追加します。これにより、あなたのことをオンラインで追跡しにくくします。
torbutton.panel.tooltip.disabled = クリックしてTorを有効にする
torbutton.panel.tooltip.enabled = クリックしてTorを無効にする
@@ -56,5 +56,5 @@ profileMigrationFailed=存在しているプロファイル %S へのマイグ
updateDownloadingPanelUILabel=%S 更新をダウンロードしています
# .Onion Page Info prompt. Strings are kept here for ease of translation.
-pageInfo_OnionEncryptionWithBitsAndProtocol=接続が暗号化されています (Onionサービス、%1$S、鍵長 %2$S bit、%3$S)
-pageInfo_OnionEncryption=接続が暗号化されています (Onionサービス)
+pageInfo_OnionEncryptionWithBitsAndProtocol=接続が暗号化されています (Onion サービス、%1$S、鍵長 %2$S bit、%3$S)
+pageInfo_OnionEncryption=接続が暗号化されています (Onion サービス)
diff --git a/chrome/locale/ka/browserOnboarding.properties b/chrome/locale/ka/browserOnboarding.properties
index 11c88645..11832796 100644
--- a/chrome/locale/ka/browserOnboarding.properties
+++ b/chrome/locale/ka/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=განახლებული
onboarding.tour-tor-toolbar=ხელსაწყოთა ზოლი
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=იხილეთ Tor-ქსელი
diff --git a/chrome/locale/ko/browserOnboarding.properties b/chrome/locale/ko/browserOnboarding.properties
index 732ea8ab..ed818936 100644
--- a/chrome/locale/ko/browserOnboarding.properties
+++ b/chrome/locale/ko/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=업데이트됨
onboarding.tour-tor-toolbar=툴바
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Tor 네트워크 항목으로 가봅니다.
diff --git a/chrome/locale/mk/browserOnboarding.properties b/chrome/locale/mk/browserOnboarding.properties
index c0247407..08935029 100644
--- a/chrome/locale/mk/browserOnboarding.properties
+++ b/chrome/locale/mk/browserOnboarding.properties
@@ -15,8 +15,8 @@ onboarding.tour-tor-privacy.button=Оди на Tor Мрежа
onboarding.tour-tor-network=Tor Мрежа
onboarding.tour-tor-network.title=Патувајте низ децентрализираната мрежа.
onboarding.tour-tor-network.description=Tor Browser ве поврзува на Tor мрежата одржувана од илјадници волонтери низ целиот свет. За разлика од VPN, овде нема место за неуспех или централизиран ентитет на кого треба да му верувате со цел да уживате приватност на Интернет.
-onboarding.tour-tor-network.description-para2=NEW: Tor Network Settings, including the ability to request bridges where Tor is blocked, can now be found in Preferences.
-onboarding.tour-tor-network.action-button=Adjust Your Tor Network Settings
+onboarding.tour-tor-network.description-para2=НОВО: Tor Мрежните поставувања, вклучуваат можност да побараат мостови таму каде Tor е блокиран, сега можат да бидат пронајдени во Својства.
+onboarding.tour-tor-network.action-button=Прилагоди ги твоите Мрежни поставки
onboarding.tour-tor-network.button=Оди на Круг екранот
onboarding.tour-tor-circuit-display=Круг екран
@@ -49,10 +49,10 @@ onboarding.tour-tor-update.prefix-new=Ново
onboarding.tour-tor-update.prefix-updated=Ажурирано
onboarding.tour-tor-toolbar=Лента со алатки
-onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
-onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
-onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
+onboarding.tour-tor-toolbar-update-9.0.title=Збогум Онион копче.
+onboarding.tour-tor-toolbar-update-9.0.description=Сакаме вашето искуство користејќи го Tor да биде целосно вградено во Tor Browser.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=Затоа сега, наместо да го користите онион копчето, можете да го видите вашиот Tor Круг преку [i] во URL лентата и да побарате Нов Идентитет користејќи го копчето во алатникот или [≡] менито.
+onboarding.tour-tor-toolbar-update-9.0.button=Како да побарате Нов Идентитет
onboarding.tour-tor-toolbar-update-9.0.next-button=Оди на Tor Мрежа
# Circuit Display onboarding.
diff --git a/chrome/locale/nb-NO/browserOnboarding.properties b/chrome/locale/nb-NO/browserOnboarding.properties
index 89bfd8e9..a444b823 100644
--- a/chrome/locale/nb-NO/browserOnboarding.properties
+++ b/chrome/locale/nb-NO/browserOnboarding.properties
@@ -15,8 +15,8 @@ onboarding.tour-tor-privacy.button=Gå til Tor Nettverk
onboarding.tour-tor-network=Tor Nettverk
onboarding.tour-tor-network.title=Reis et decentralisert nettverk.
onboarding.tour-tor-network.description=Tor Nettleser forbinder deg med Tor-nettet som drives av tusenvis av frivillige rundt om i verden. I motsetning til en VPN er det ingen feilpunkt eller sentralisert enhet du må stole på for å kunne nyte Internettet privat.
-onboarding.tour-tor-network.description-para2=NEW: Tor Network Settings, including the ability to request bridges where Tor is blocked, can now be found in Preferences.
-onboarding.tour-tor-network.action-button=Adjust Your Tor Network Settings
+onboarding.tour-tor-network.description-para2=NYTT: Tor nettverksinnstillinger, inkluderer muligheten til å be om broer der Tor er blokkert, kan nå finnes i Innstillinger.
+onboarding.tour-tor-network.action-button=Juster din Tor-nettverksinnstillinger
onboarding.tour-tor-network.button=Gå til Kretsvisning
onboarding.tour-tor-circuit-display=Kretsvisning
@@ -51,8 +51,8 @@ onboarding.tour-tor-update.prefix-updated=Oppdatert
onboarding.tour-tor-toolbar=Verktøylinje
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
-onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
+onboarding.tour-tor-toolbar-update-9.0.button=Hvordan be om en Ny identitet
onboarding.tour-tor-toolbar-update-9.0.next-button=Gå til Tor Nettverk
# Circuit Display onboarding.
diff --git a/chrome/locale/nl/browserOnboarding.properties b/chrome/locale/nl/browserOnboarding.properties
index 84ab0176..f109d951 100644
--- a/chrome/locale/nl/browserOnboarding.properties
+++ b/chrome/locale/nl/browserOnboarding.properties
@@ -15,8 +15,8 @@ onboarding.tour-tor-privacy.button=Naar Tor-netwerk
onboarding.tour-tor-network=Tor-netwerk
onboarding.tour-tor-network.title=Reis over een gedecentraliseerd netwerk.
onboarding.tour-tor-network.description=Tor Browser verbindt u met het Tor-netwerk, mogelijk gemaakt door duizenden vrijwilligers over de hele wereld. Anders dan bij een VPN is er geen sprake van één zwakke plek of centrale autoriteit die u moet vertrouwen om privé van het internet gebruik te maken.
-onboarding.tour-tor-network.description-para2=NEW: Tor Network Settings, including the ability to request bridges where Tor is blocked, can now be found in Preferences.
-onboarding.tour-tor-network.action-button=Adjust Your Tor Network Settings
+onboarding.tour-tor-network.description-para2=NIEUW: de Tor-netwerkinstellingen, waaronder de mogelijkheid om bridges aan te vragen waar Tor is geblokkeerd, zijn nu te vinden in Voorkeuren.
+onboarding.tour-tor-network.action-button=Uw Tor-netwerkinstellingen aanpassen
onboarding.tour-tor-network.button=Naar Circuit-venster
onboarding.tour-tor-circuit-display=Circuit-venster
@@ -49,10 +49,10 @@ onboarding.tour-tor-update.prefix-new=Nieuw
onboarding.tour-tor-update.prefix-updated=Bijgewerkt
onboarding.tour-tor-toolbar=Werkbalk
-onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
-onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
-onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
+onboarding.tour-tor-toolbar-update-9.0.title=Vaarwel Onion-knop.
+onboarding.tour-tor-toolbar-update-9.0.description=We willen dat uw ervaring met Tor volledig is geïntegreerd binnen Tor Browser.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=Dat is waarom u nu, in plaats van de onion-knop te gebruiken, uw Tor-circuit via de [i] in de URL-balk kunt zien en een nieuwe identiteit kunt aanvragen via de werkbalkknop of het menu [≡].
+onboarding.tour-tor-toolbar-update-9.0.button=Een nieuwe identiteit aanvragen
onboarding.tour-tor-toolbar-update-9.0.next-button=Naar Tor-netwerk
# Circuit Display onboarding.
diff --git a/chrome/locale/pl/browserOnboarding.properties b/chrome/locale/pl/browserOnboarding.properties
index e0591437..e9ae1d69 100644
--- a/chrome/locale/pl/browserOnboarding.properties
+++ b/chrome/locale/pl/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Zaktualizowano
onboarding.tour-tor-toolbar=Pasek narzędzi
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Przejdź do Sieci Tor
diff --git a/chrome/locale/pt-BR/browserOnboarding.properties b/chrome/locale/pt-BR/browserOnboarding.properties
index 11f2240e..dfe66c08 100644
--- a/chrome/locale/pt-BR/browserOnboarding.properties
+++ b/chrome/locale/pt-BR/browserOnboarding.properties
@@ -15,8 +15,8 @@ onboarding.tour-tor-privacy.button=Vá para a Rede Tor.
onboarding.tour-tor-network=Rede Tor
onboarding.tour-tor-network.title=Viaje por uma rede descentralizada.
onboarding.tour-tor-network.description=O Tor Browser conecta você à rede Tor, composta por milhares de voluntários ao redor do mundo. Diferente de uma VPN, não há uma entidade ou ponto de falha central em que você precisa confiar para desfrutar da internet com privacidade.
-onboarding.tour-tor-network.description-para2=NEW: Tor Network Settings, including the ability to request bridges where Tor is blocked, can now be found in Preferences.
-onboarding.tour-tor-network.action-button=Adjust Your Tor Network Settings
+onboarding.tour-tor-network.description-para2=NOVO: As configurações de rede do Tor, incluindo a capacidade de solicitar pontes onde o Tor está bloqueado, agora podem ser encontradas em Preferências.
+onboarding.tour-tor-network.action-button=Ajuste suas configurações de rede do Tor
onboarding.tour-tor-network.button=Vá para a Exibição de Circuitos
onboarding.tour-tor-circuit-display=Exibição de Circuitos
@@ -49,10 +49,10 @@ onboarding.tour-tor-update.prefix-new=Novo
onboarding.tour-tor-update.prefix-updated=Atualizado
onboarding.tour-tor-toolbar=Barra de ferramentas
-onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
-onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
-onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
+onboarding.tour-tor-toolbar-update-9.0.title=Adeus Onion Botão.
+onboarding.tour-tor-toolbar-update-9.0.description=Queremos que sua experiência com o Tor seja totalmente integrada ao Tor Browser.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=É por isso que agora, em vez de usar o onion botão, você pode ver o seu Tor Circuit através do [i] na barra de URL e solicitar uma nova identidade usando o botão da barra de ferramentas ou o menu [≡].
+onboarding.tour-tor-toolbar-update-9.0.button=Como solicitar uma nova identidade
onboarding.tour-tor-toolbar-update-9.0.next-button=Vá para a Rede Tor.
# Circuit Display onboarding.
diff --git a/chrome/locale/ro/browserOnboarding.properties b/chrome/locale/ro/browserOnboarding.properties
index c76a29e0..27989597 100644
--- a/chrome/locale/ro/browserOnboarding.properties
+++ b/chrome/locale/ro/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Actualizat
onboarding.tour-tor-toolbar=Bara cu instrumente
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Sari la Rețeaua Tor
diff --git a/chrome/locale/ru/browserOnboarding.properties b/chrome/locale/ru/browserOnboarding.properties
index 45cb0a8d..baba509f 100644
--- a/chrome/locale/ru/browserOnboarding.properties
+++ b/chrome/locale/ru/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Обновленный
onboarding.tour-tor-toolbar=Панель инструментов
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Перейти в Сеть Tor
diff --git a/chrome/locale/sv-SE/browserOnboarding.properties b/chrome/locale/sv-SE/browserOnboarding.properties
index 06b41901..21001db9 100644
--- a/chrome/locale/sv-SE/browserOnboarding.properties
+++ b/chrome/locale/sv-SE/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Uppdaterad
onboarding.tour-tor-toolbar=Verktygsfält
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Gå till Tor-nätverket
diff --git a/chrome/locale/tr/browserOnboarding.properties b/chrome/locale/tr/browserOnboarding.properties
index 0b50cf36..b5e9b49f 100644
--- a/chrome/locale/tr/browserOnboarding.properties
+++ b/chrome/locale/tr/browserOnboarding.properties
@@ -15,8 +15,8 @@ onboarding.tour-tor-privacy.button=Tor Ağına Geç
onboarding.tour-tor-network=Tor Ağı
onboarding.tour-tor-network.title=Tek merkezden yönetilmeyen bir ağda gezinin.
onboarding.tour-tor-network.description=Tor Browser sizi tüm dünyada binlerce gönüllü tarafından işletilen Tor Ağına bağlar. VPN bağlantısının aksine güvenlik açığı oluşturacak bir nokta ya da İnternet üzerinde kişisel gizliliğinizi koruyarak gezinmeniz için güvenmeniz gereken merkezi bir kuruluş yoktur.
-onboarding.tour-tor-network.description-para2=NEW: Tor Network Settings, including the ability to request bridges where Tor is blocked, can now be found in Preferences.
-onboarding.tour-tor-network.action-button=Adjust Your Tor Network Settings
+onboarding.tour-tor-network.description-para2=YENİ: Tor Ağı Ayarları, Tor ağına erişimin engellendiği yerlerde köprü isteğinde bulunma özelliği artı Ayarlar bölümü altında bulunabilir.
+onboarding.tour-tor-network.action-button=Tor Ağı Ayarlarınızı Yapın
onboarding.tour-tor-network.button=Devre Görünümüne Geç
onboarding.tour-tor-circuit-display=Devre Görünümü
@@ -49,10 +49,10 @@ onboarding.tour-tor-update.prefix-new=Yeni
onboarding.tour-tor-update.prefix-updated=Güncellendi
onboarding.tour-tor-toolbar=Araç Çubuğu
-onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
-onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
-onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
+onboarding.tour-tor-toolbar-update-9.0.title=Soğan Düğmesine Veda Edin.
+onboarding.tour-tor-toolbar-update-9.0.description=Tor işlemlerini tamamen Tor Browser ile yapabilmenizi istiyoruz.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=Bu nedenle, artık soğan düğmesini kullanmak yerine adres çubuğundaki [i] simgesine tıklayarak Tor Devresini görebilir, araç çubuğu düğmesini ya da [≡] menüsünü kullanarak Yeni Kimlik isteğinde bulunabilirsiniz.
+onboarding.tour-tor-toolbar-update-9.0.button=Nasıl Yeni Kimlik İsteğinde Bulunacaksınız
onboarding.tour-tor-toolbar-update-9.0.next-button=Tor Ağına Geç
# Circuit Display onboarding.
diff --git a/chrome/locale/tr/torbutton.properties b/chrome/locale/tr/torbutton.properties
index 6105c7b1..4523d729 100644
--- a/chrome/locale/tr/torbutton.properties
+++ b/chrome/locale/tr/torbutton.properties
@@ -1,6 +1,6 @@
torbutton.circuit_display.internet = İnternet
torbutton.circuit_display.ip_unknown = Bilinmeyen IP
-torbutton.circuit_display.onion_site = Soğan sitesi
+torbutton.circuit_display.onion_site = Onion sitesi
torbutton.circuit_display.this_browser = Bu tarayıcı
torbutton.circuit_display.relay = Aktarıcı
torbutton.circuit_display.tor_bridge = Köprü
diff --git a/chrome/locale/vi/browserOnboarding.properties b/chrome/locale/vi/browserOnboarding.properties
index e9508e20..0b14f935 100644
--- a/chrome/locale/vi/browserOnboarding.properties
+++ b/chrome/locale/vi/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=Updated
onboarding.tour-tor-toolbar=Toolbar
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=Đi đến mạng Tor
diff --git a/chrome/locale/zh-CN/browserOnboarding.properties b/chrome/locale/zh-CN/browserOnboarding.properties
index 83aa1571..f188ac01 100644
--- a/chrome/locale/zh-CN/browserOnboarding.properties
+++ b/chrome/locale/zh-CN/browserOnboarding.properties
@@ -15,8 +15,8 @@ onboarding.tour-tor-privacy.button=前往“Tor 网络”
onboarding.tour-tor-network=Tor 网络
onboarding.tour-tor-network.title=通过非中心化的网络进行浏览。
onboarding.tour-tor-network.description=Tor 浏览器将你和世界数千名志愿者支持的 Tor 网络相连。和 VPN 不同的是,这里不会有单点故障,也不需要你为了私密浏览信任某一个中心化的实体。
-onboarding.tour-tor-network.description-para2=NEW: Tor Network Settings, including the ability to request bridges where Tor is blocked, can now be found in Preferences.
-onboarding.tour-tor-network.action-button=Adjust Your Tor Network Settings
+onboarding.tour-tor-network.description-para2=新内容:设置中的 Tor 网络设置,包括在 Tor 被屏蔽时获取网桥。
+onboarding.tour-tor-network.action-button=调整 Tor 网络设置
onboarding.tour-tor-network.button=前往 ’显示链路'
onboarding.tour-tor-circuit-display=显示链路
@@ -49,10 +49,10 @@ onboarding.tour-tor-update.prefix-new=新建
onboarding.tour-tor-update.prefix-updated=已更新
onboarding.tour-tor-toolbar=工具栏
-onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
-onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
-onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
+onboarding.tour-tor-toolbar-update-9.0.title=拜拜,洋葱按钮。
+onboarding.tour-tor-toolbar-update-9.0.description=我们希望将你使用 Tor 全部操作搬到 Tor 浏览器内。
+onboarding.tour-tor-toolbar-update-9.0.description-para2=所以现在,你可以在地址栏中的[i]按钮中看到 Tor 链路,在工具栏按钮[≡]菜单中请求新的身份,而不是使用洋葱按钮。
+onboarding.tour-tor-toolbar-update-9.0.button=如何请求新身份
onboarding.tour-tor-toolbar-update-9.0.next-button=前往“Tor 网络”
# Circuit Display onboarding.
diff --git a/chrome/locale/zh-TW/browserOnboarding.properties b/chrome/locale/zh-TW/browserOnboarding.properties
index 5cd92164..0c96e284 100644
--- a/chrome/locale/zh-TW/browserOnboarding.properties
+++ b/chrome/locale/zh-TW/browserOnboarding.properties
@@ -51,7 +51,7 @@ onboarding.tour-tor-update.prefix-updated=已更新
onboarding.tour-tor-toolbar=工具列
onboarding.tour-tor-toolbar-update-9.0.title=Goodbye Onion Button.
onboarding.tour-tor-toolbar-update-9.0.description=We want your experience using Tor to be fully integrated within Tor Browser.
-onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [=] menu.
+onboarding.tour-tor-toolbar-update-9.0.description-para2=That's why now, rather than using the onion button, you can see your Tor Circuit via the [i] in the URL bar and request a New Identity using the toolbar button or the [≡] menu.
onboarding.tour-tor-toolbar-update-9.0.button=How to Request a New Identity
onboarding.tour-tor-toolbar-update-9.0.next-button=前往 Tor 網路
1
0