lists.torproject.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

tbb-commits

Thread Start a new thread
Threads by month
  • ----- 2026 -----
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2021 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2020 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2019 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2018 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2017 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2016 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2015 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2014 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
tbb-commits@lists.torproject.org

  • 1 participants
  • 20633 discussions
[tor-browser/tor-browser-86.0b5-10.5-1] Bug 30605: Honor privacy.spoof_english in Android
by gk@torproject.org 04 Feb '21

04 Feb '21
commit 9ac35399967dd33cdbd7253917cc8175305920a1 Author: Alex Catarineu <acat(a)torproject.org> Date: Fri Oct 16 10:45:17 2020 +0200 Bug 30605: Honor privacy.spoof_english in Android This checks `privacy.spoof_english` whenever `setLocales` is called from Fenix side and sets `intl.accept_languages` accordingly. --- mobile/android/components/geckoview/GeckoViewStartup.jsm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mobile/android/components/geckoview/GeckoViewStartup.jsm b/mobile/android/components/geckoview/GeckoViewStartup.jsm index 83558ff558a9..432d27f86f8a 100644 --- a/mobile/android/components/geckoview/GeckoViewStartup.jsm +++ b/mobile/android/components/geckoview/GeckoViewStartup.jsm @@ -18,6 +18,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { Preferences: "resource://gre/modules/Preferences.jsm", SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm", Services: "resource://gre/modules/Services.jsm", + RFPHelper: "resource://gre/modules/RFPHelper.jsm", }); const { debug, warn } = GeckoViewUtils.initLogging("Startup"); @@ -251,6 +252,10 @@ class GeckoViewStartup { if (aData.requestedLocales) { Services.locale.requestedLocales = aData.requestedLocales; } + RFPHelper._handleSpoofEnglishChanged(); + if (Services.prefs.getIntPref("privacy.spoof_english", 0) === 2) { + break; + } const pls = Cc["@mozilla.org/pref-localizedstring;1"].createInstance( Ci.nsIPrefLocalizedString );
1 0
0 0
[tor-browser/tor-browser-86.0b5-10.5-1] Bug 40199: Avoid using system locale for intl.accept_languages in GeckoView
by gk@torproject.org 04 Feb '21

04 Feb '21
commit 3422732468dfacc5064bca744e79b0cdb4b92008 Author: Alex Catarineu <acat(a)torproject.org> Date: Tue Oct 20 17:44:36 2020 +0200 Bug 40199: Avoid using system locale for intl.accept_languages in GeckoView --- .../mozilla/geckoview/GeckoRuntimeSettings.java | 26 +++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java index 7b36b93ebb35..625883afe8d3 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java @@ -762,19 +762,25 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { private String computeAcceptLanguages() { ArrayList<String> locales = new ArrayList<String>(); - // Explicitly-set app prefs come first: - if (mRequestedLocales != null) { - for (String locale : mRequestedLocales) { - locales.add(locale.toLowerCase(Locale.ROOT)); - } - } - // OS prefs come second: - for (String locale : getDefaultLocales()) { - locale = locale.toLowerCase(Locale.ROOT); - if (!locales.contains(locale)) { + // In Desktop, these are defined in the `intl.accept_languages` localized property. + // At some point we should probably use the same values here, but for now we use a simple + // strategy which will hopefully result in reasonable acceptLanguage values. + if (mRequestedLocales != null && mRequestedLocales.length > 0) { + String locale = mRequestedLocales[0].toLowerCase(Locale.ROOT); + // No need to include `en-us` twice. + if (!locale.equals("en-us")) { locales.add(locale); + if (locale.contains("-")) { + String lang = locale.split("-")[0]; + // No need to include `en` twice. + if (!lang.equals("en")) { + locales.add(lang); + } + } } } + locales.add("en-us"); + locales.add("en"); return TextUtils.join(",", locales); }
1 0
0 0
[tor-browser/tor-browser-86.0b5-10.5-1] Bug 40091: Load HTTPS Everywhere as a builtin addon in desktop
by gk@torproject.org 04 Feb '21

04 Feb '21
commit 99e0bc59c81e5e7795f8a8e6e45d63613071fade Author: Alex Catarineu <acat(a)torproject.org> Date: Fri Sep 4 12:34:35 2020 +0200 Bug 40091: Load HTTPS Everywhere as a builtin addon in desktop This loads HTTPS Everywhere as a builtin addon from a hardcoded resource:// URI in desktop. It also ensures that the non-builtin HTTPS Everywhere addon is always uninstalled on browser startup. The reason of making this desktop-only is that there are some issues when installing a builtin extension from geckoview side, making the extension not available on first startup. So, at least for now we handle the Fenix case separately. See #40118 for a followup for investigating these. --- browser/components/BrowserGlue.jsm | 37 ++++++++++++++++++++++ toolkit/components/extensions/Extension.jsm | 10 ++++-- .../mozapps/extensions/internal/XPIProvider.jsm | 13 ++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index a9a3b6ac014c..28d53f995c9a 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -41,6 +41,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { DownloadsViewableInternally: "resource:///modules/DownloadsViewableInternally.jsm", E10SUtils: "resource://gre/modules/E10SUtils.jsm", + ExtensionData: "resource://gre/modules/Extension.jsm", ExtensionsUI: "resource:///modules/ExtensionsUI.jsm", FeatureGate: "resource://featuregates/FeatureGate.jsm", FirefoxMonitor: "resource:///modules/FirefoxMonitor.jsm", @@ -115,6 +116,13 @@ XPCOMUtils.defineLazyServiceGetter( "nsIPushService" ); +XPCOMUtils.defineLazyServiceGetters(this, { + resProto: [ + "@mozilla.org/network/protocol;1?name=resource", + "nsISubstitutingProtocolHandler", + ], +}); + const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state"; /** @@ -1357,6 +1365,35 @@ BrowserGlue.prototype = { "resource://builtin-themes/alpenglow/" ); + // Install https-everywhere builtin addon if needed. + (async () => { + const HTTPS_EVERYWHERE_ID = "https-everywhere-eff(a)eff.org"; + const HTTPS_EVERYWHERE_BUILTIN_URL = + "resource://torbutton/content/extensions/https-everywhere/"; + // This does something similar as GeckoViewWebExtension.jsm: it tries + // to load the manifest to retrieve the version of the builtin and + // compares it to the currently installed one to see whether we need + // to install or not. Here we delegate that to + // AddonManager.maybeInstallBuiltinAddon. + try { + const resolvedURI = Services.io.newURI( + resProto.resolveURI(Services.io.newURI(HTTPS_EVERYWHERE_BUILTIN_URL)) + ); + const extensionData = new ExtensionData(resolvedURI); + const manifest = await extensionData.loadManifest(); + + await AddonManager.maybeInstallBuiltinAddon( + HTTPS_EVERYWHERE_ID, + manifest.version, + HTTPS_EVERYWHERE_BUILTIN_URL + ); + } catch (e) { + const log = Log.repository.getLogger("HttpsEverywhereBuiltinLoader"); + log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter())); + log.error("Could not install https-everywhere extension", e); + } + })(); + if (AppConstants.MOZ_NORMANDY) { Normandy.init(); } diff --git a/toolkit/components/extensions/Extension.jsm b/toolkit/components/extensions/Extension.jsm index 1b940b6d9954..d178e0a74432 100644 --- a/toolkit/components/extensions/Extension.jsm +++ b/toolkit/components/extensions/Extension.jsm @@ -212,6 +212,7 @@ const LOGGER_ID_BASE = "addons.webextension."; const UUID_MAP_PREF = "extensions.webextensions.uuids"; const LEAVE_STORAGE_PREF = "extensions.webextensions.keepStorageOnUninstall"; const LEAVE_UUID_PREF = "extensions.webextensions.keepUuidOnUninstall"; +const PERSISTENT_EXTENSIONS = new Set(["https-everywhere-eff(a)eff.org"]); const COMMENT_REGEXP = new RegExp( String.raw` @@ -358,7 +359,8 @@ var ExtensionAddonObserver = { ); } - if (!Services.prefs.getBoolPref(LEAVE_STORAGE_PREF, false)) { + if (!Services.prefs.getBoolPref(LEAVE_STORAGE_PREF, false) && + !PERSISTENT_EXTENSIONS.has(addon.id)) { // Clear browser.storage.local backends. AsyncShutdown.profileChangeTeardown.addBlocker( `Clear Extension Storage ${addon.id} (File Backend)`, @@ -406,7 +408,8 @@ var ExtensionAddonObserver = { ExtensionPermissions.removeAll(addon.id); - if (!Services.prefs.getBoolPref(LEAVE_UUID_PREF, false)) { + if (!Services.prefs.getBoolPref(LEAVE_UUID_PREF, false) && + !PERSISTENT_EXTENSIONS.has(addon.id)) { // Clear the entry in the UUID map UUIDMap.remove(addon.id); } @@ -2596,7 +2599,8 @@ class Extension extends ExtensionData { ); } else if ( this.startupReason === "ADDON_INSTALL" && - !Services.prefs.getBoolPref(LEAVE_STORAGE_PREF, false) + !Services.prefs.getBoolPref(LEAVE_STORAGE_PREF, false) && + !PERSISTENT_EXTENSIONS.has(this.id) ) { // If the extension has been just installed, set it as migrated, // because there will not be any data to migrate. diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 14c6afbf57a0..b9420a092733 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -1508,6 +1508,19 @@ var XPIStates = { continue; } + // Uninstall HTTPS Everywhere if it is installed in the user profile. + if ( + id === "https-everywhere-eff(a)eff.org" && + loc.name === KEY_APP_PROFILE + ) { + logger.debug( + "Uninstalling the HTTPS Everywhere extension from user profile." + ); + loc.installer.uninstallAddon(id); + changed = true; + continue; + } + let xpiState = loc.get(id); if (!xpiState) { // If the location is not supported for sideloading, skip new
1 0
0 0
[tor-browser/tor-browser-86.0b5-10.5-1] Bug 40198: Expose privacy.spoof_english pref in GeckoView
by gk@torproject.org 04 Feb '21

04 Feb '21
commit 906f60ce5eeda9bdb393e1121fc05ee4aac5bb57 Author: Alex Catarineu <acat(a)torproject.org> Date: Sun Oct 18 17:06:04 2020 +0200 Bug 40198: Expose privacy.spoof_english pref in GeckoView --- mobile/android/geckoview/api.txt | 3 ++ .../mozilla/geckoview/GeckoRuntimeSettings.java | 33 ++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/mobile/android/geckoview/api.txt b/mobile/android/geckoview/api.txt index 61fd351cda2b..e267ee00f996 100644 --- a/mobile/android/geckoview/api.txt +++ b/mobile/android/geckoview/api.txt @@ -639,6 +639,7 @@ package org.mozilla.geckoview { method public boolean getRemoteDebuggingEnabled(); method @Nullable public GeckoRuntime getRuntime(); method @Nullable public Rect getScreenSizeOverride(); + method public boolean getSpoofEnglish(); method @Nullable public RuntimeTelemetry.Delegate getTelemetryDelegate(); method public int getTorSecurityLevel(); method public boolean getUseMaxScreenDepth(); @@ -658,6 +659,7 @@ package org.mozilla.geckoview { method @NonNull public GeckoRuntimeSettings setLoginAutofillEnabled(boolean); method @NonNull public GeckoRuntimeSettings setPreferredColorScheme(int); method @NonNull public GeckoRuntimeSettings setRemoteDebuggingEnabled(boolean); + method @NonNull public GeckoRuntimeSettings setSpoofEnglish(boolean); method @NonNull public GeckoRuntimeSettings setTorSecurityLevel(int); method @NonNull public GeckoRuntimeSettings setWebFontsEnabled(boolean); method @NonNull public GeckoRuntimeSettings setWebManifestEnabled(boolean); @@ -693,6 +695,7 @@ package org.mozilla.geckoview { method @NonNull public GeckoRuntimeSettings.Builder preferredColorScheme(int); method @NonNull public GeckoRuntimeSettings.Builder remoteDebuggingEnabled(boolean); method @NonNull public GeckoRuntimeSettings.Builder screenSizeOverride(int, int); + method @NonNull public GeckoRuntimeSettings.Builder spoofEnglish(boolean); method @NonNull public GeckoRuntimeSettings.Builder telemetryDelegate(@NonNull RuntimeTelemetry.Delegate); method @NonNull public GeckoRuntimeSettings.Builder torSecurityLevel(int); method @NonNull public GeckoRuntimeSettings.Builder useMaxScreenDepth(boolean); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java index 625883afe8d3..0d0ad52730ad 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java @@ -457,6 +457,17 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { getSettings().mTorSecurityLevel.set(level); return this; } + + /** + * Sets whether we should spoof locale to English for webpages. + * + * @param flag True if we should spoof locale to English for webpages, false otherwise. + * @return This Builder instance. + */ + public @NonNull Builder spoofEnglish(final boolean flag) { + getSettings().mSpoofEnglish.set(flag ? 2 : 1); + return this; + } } private GeckoRuntime mRuntime; @@ -507,6 +518,8 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { "signon.autofillForms", true); /* package */ final Pref<Integer> mTorSecurityLevel = new Pref<>( "extensions.torbutton.security_slider", 4); + /* package */ final Pref<Integer> mSpoofEnglish = new Pref<>( + "privacy.spoof_english", 0); /* package */ int mPreferredColorScheme = COLOR_SCHEME_SYSTEM; @@ -1175,6 +1188,26 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { return this; } + /** + * Get whether we should spoof locale to English for webpages. + * + * @return Whether we should spoof locale to English for webpages. + */ + public boolean getSpoofEnglish() { + return mSpoofEnglish.get() == 2; + } + + /** + * Set whether we should spoof locale to English for webpages. + * + * @param flag A flag determining whether we should locale to English for webpages. + * @return This GeckoRuntimeSettings instance. + */ + public @NonNull GeckoRuntimeSettings setSpoofEnglish(final boolean flag) { + mSpoofEnglish.commit(flag ? 2 : 1); + return this; + } + @Override // Parcelable public void writeToParcel(final Parcel out, final int flags) { super.writeToParcel(out, flags);
1 0
0 0
[tor-browser/tor-browser-86.0b5-10.5-1] Bug 40125: Expose Security Level pref in GeckoView
by gk@torproject.org 04 Feb '21

04 Feb '21
commit 79aa1229f78d186fc281ed97d7f2d81d10a22df7 Author: Matthew Finkel <sysrqb(a)torproject.org> Date: Mon Sep 14 02:52:28 2020 +0000 Bug 40125: Expose Security Level pref in GeckoView --- mobile/android/geckoview/api.txt | 3 ++ .../mozilla/geckoview/GeckoRuntimeSettings.java | 33 ++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/mobile/android/geckoview/api.txt b/mobile/android/geckoview/api.txt index f8eb4894bcf2..61fd351cda2b 100644 --- a/mobile/android/geckoview/api.txt +++ b/mobile/android/geckoview/api.txt @@ -640,6 +640,7 @@ package org.mozilla.geckoview { method @Nullable public GeckoRuntime getRuntime(); method @Nullable public Rect getScreenSizeOverride(); method @Nullable public RuntimeTelemetry.Delegate getTelemetryDelegate(); + method public int getTorSecurityLevel(); method public boolean getUseMaxScreenDepth(); method public boolean getWebFontsEnabled(); method public boolean getWebManifestEnabled(); @@ -657,6 +658,7 @@ package org.mozilla.geckoview { method @NonNull public GeckoRuntimeSettings setLoginAutofillEnabled(boolean); method @NonNull public GeckoRuntimeSettings setPreferredColorScheme(int); method @NonNull public GeckoRuntimeSettings setRemoteDebuggingEnabled(boolean); + method @NonNull public GeckoRuntimeSettings setTorSecurityLevel(int); method @NonNull public GeckoRuntimeSettings setWebFontsEnabled(boolean); method @NonNull public GeckoRuntimeSettings setWebManifestEnabled(boolean); field public static final int COLOR_SCHEME_DARK = 1; @@ -692,6 +694,7 @@ package org.mozilla.geckoview { method @NonNull public GeckoRuntimeSettings.Builder remoteDebuggingEnabled(boolean); method @NonNull public GeckoRuntimeSettings.Builder screenSizeOverride(int, int); method @NonNull public GeckoRuntimeSettings.Builder telemetryDelegate(@NonNull RuntimeTelemetry.Delegate); + method @NonNull public GeckoRuntimeSettings.Builder torSecurityLevel(int); method @NonNull public GeckoRuntimeSettings.Builder useMaxScreenDepth(boolean); method @NonNull public GeckoRuntimeSettings.Builder webFontsEnabled(boolean); method @NonNull public GeckoRuntimeSettings.Builder webManifest(boolean); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java index 12d2adfb0538..7b36b93ebb35 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java @@ -446,6 +446,17 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { getSettings().mForceUserScalable.set(flag); return this; } + + /** + * Set security level. + * + * @param level A value determining the security level. Default is 0. + * @return This Builder instance. + */ + public @NonNull Builder torSecurityLevel(final int level) { + getSettings().mTorSecurityLevel.set(level); + return this; + } } private GeckoRuntime mRuntime; @@ -494,6 +505,8 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { "browser.ui.zoom.force-user-scalable", false); /* package */ final Pref<Boolean> mAutofillLogins = new Pref<Boolean>( "signon.autofillForms", true); + /* package */ final Pref<Integer> mTorSecurityLevel = new Pref<>( + "extensions.torbutton.security_slider", 4); /* package */ int mPreferredColorScheme = COLOR_SCHEME_SYSTEM; @@ -1136,6 +1149,26 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { return this; } + /** + * Gets the current security level. + * + * @return current security protection level + */ + public int getTorSecurityLevel() { + return mTorSecurityLevel.get(); + } + + /** + * Sets the Tor Security Level. + * + * @param level security protection level + * @return This GeckoRuntimeSettings instance. + */ + public @NonNull GeckoRuntimeSettings setTorSecurityLevel(final int level) { + mTorSecurityLevel.commit(level); + return this; + } + @Override // Parcelable public void writeToParcel(final Parcel out, final int flags) { super.writeToParcel(out, flags);
1 0
0 0
[tor-browser/tor-browser-86.0b5-10.5-1] Bug 40309: Avoid using regional OS locales
by gk@torproject.org 04 Feb '21

04 Feb '21
commit e16f7eddc571a6ad9c2907062f6f407b739baf71 Author: Alex Catarineu <acat(a)torproject.org> Date: Wed Jan 27 11:28:05 2021 +0100 Bug 40309: Avoid using regional OS locales Only use regional OS locales if the pref `intl.regional_prefs.use_os_locales` is set to true. --- intl/locale/LocaleService.cpp | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/intl/locale/LocaleService.cpp b/intl/locale/LocaleService.cpp index 6e59119f623b..add1ac1730eb 100644 --- a/intl/locale/LocaleService.cpp +++ b/intl/locale/LocaleService.cpp @@ -457,31 +457,6 @@ LocaleService::GetRegionalPrefsLocales(nsTArray<nsCString>& aRetVal) { OSPreferences::GetInstance()->GetRegionalPrefsLocales(aRetVal))) { return NS_OK; } - - // If we fail to retrieve them, return the app locales. - GetAppLocalesAsBCP47(aRetVal); - return NS_OK; - } - - // Otherwise, fetch OS Regional Preferences locales and compare the first one - // to the app locale. If the language subtag matches, we can safely use - // the OS Regional Preferences locale. - // - // This facilitates scenarios such as Firefox in "en-US" and User sets - // regional prefs to "en-GB". - nsAutoCString appLocale; - AutoTArray<nsCString, 10> regionalPrefsLocales; - LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale); - - if (NS_FAILED(OSPreferences::GetInstance()->GetRegionalPrefsLocales( - regionalPrefsLocales))) { - GetAppLocalesAsBCP47(aRetVal); - return NS_OK; - } - - if (LocaleService::LanguagesMatch(appLocale, regionalPrefsLocales[0])) { - aRetVal = regionalPrefsLocales.Clone(); - return NS_OK; } // Otherwise use the app locales.
1 0
0 0
[tor-browser/tor-browser-86.0b5-10.5-1] Bug 40166: Disable security.certerrors.mitm.auto_enable_enterprise_roots
by gk@torproject.org 04 Feb '21

04 Feb '21
commit 10e00ec4faddf2f58ddf6989b6f547704b2efe31 Author: Alex Catarineu <acat(a)torproject.org> Date: Fri Oct 9 12:55:35 2020 +0200 Bug 40166: Disable security.certerrors.mitm.auto_enable_enterprise_roots --- browser/app/profile/000-tor-browser.js | 3 +++ browser/components/BrowserGlue.jsm | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/browser/app/profile/000-tor-browser.js b/browser/app/profile/000-tor-browser.js index c849d98f259c..5ffdc386042b 100644 --- a/browser/app/profile/000-tor-browser.js +++ b/browser/app/profile/000-tor-browser.js @@ -314,6 +314,9 @@ pref("security.enterprise_roots.enabled", false); // Don't ping Mozilla for MitM detection, see bug 32321 pref("security.certerrors.mitm.priming.enabled", false); +// Don't automatically enable enterprise roots, see bug 40166 +pref("security.certerrors.mitm.auto_enable_enterprise_roots", false); + // Disable the language pack signing check for now on macOS, see #31942 #ifdef XP_MACOSX pref("extensions.langpacks.signatures.required", false); diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index 28d53f995c9a..4bf0af3b75b4 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -1341,6 +1341,20 @@ BrowserGlue.prototype = { // handle any UI migration this._migrateUI(); + // Clear possibly auto enabled enterprise_roots prefs (see bug 40166) + if ( + !Services.prefs.getBoolPref( + "security.certerrors.mitm.auto_enable_enterprise_roots" + ) && + Services.prefs.getBoolPref( + "security.enterprise_roots.auto-enabled", + false + ) + ) { + Services.prefs.clearUserPref("security.enterprise_roots.enabled"); + Services.prefs.clearUserPref("security.enterprise_roots.auto-enabled"); + } + if (!Services.prefs.prefHasUserValue(PREF_PDFJS_ISDEFAULT_CACHE_STATE)) { PdfJs.checkIsDefault(this._isNewProfile); }
1 0
0 0
[tor-browser/tor-browser-86.0b5-10.5-1] Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#tor
by gk@torproject.org 04 Feb '21

04 Feb '21
commit cc68badf12c17df5f6ee7d81f7da5e58f8d5aad2 Author: Richard Pospesel <richard(a)torproject.org> Date: Mon Sep 16 15:25:39 2019 -0700 Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#tor This patch adds a new about:preferences#tor page which allows modifying bridge, proxy, and firewall settings from within Tor Browser. All of the functionality present in tor-launcher's Network Configuration panel is present: - Setting built-in bridges - Requesting bridges from BridgeDB via moat - Using user-provided bridges - Configuring SOCKS4, SOCKS5, and HTTP/HTTPS proxies - Setting firewall ports - Viewing and Copying Tor's logs - The Networking Settings in General preferences has been removed --- browser/components/moz.build | 1 + browser/components/preferences/main.inc.xhtml | 55 -- browser/components/preferences/main.js | 14 - browser/components/preferences/preferences.js | 9 + browser/components/preferences/preferences.xhtml | 5 + browser/components/preferences/privacy.js | 1 + .../torpreferences/content/parseFunctions.jsm | 89 +++ .../torpreferences/content/requestBridgeDialog.jsm | 204 +++++ .../content/requestBridgeDialog.xhtml | 35 + .../torpreferences/content/torBridgeSettings.jsm | 325 ++++++++ .../torpreferences/content/torCategory.inc.xhtml | 9 + .../torpreferences/content/torFirewallSettings.jsm | 72 ++ .../torpreferences/content/torLogDialog.jsm | 66 ++ .../torpreferences/content/torLogDialog.xhtml | 23 + .../components/torpreferences/content/torPane.js | 857 +++++++++++++++++++++ .../torpreferences/content/torPane.xhtml | 123 +++ .../torpreferences/content/torPreferences.css | 77 ++ .../torpreferences/content/torPreferencesIcon.svg | 5 + .../torpreferences/content/torProxySettings.jsm | 245 ++++++ browser/components/torpreferences/jar.mn | 14 + browser/components/torpreferences/moz.build | 1 + browser/modules/BridgeDB.jsm | 110 +++ browser/modules/TorProtocolService.jsm | 212 +++++ browser/modules/moz.build | 2 + 24 files changed, 2485 insertions(+), 69 deletions(-) diff --git a/browser/components/moz.build b/browser/components/moz.build index c90f0a4dad33..7bc1a92e199f 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/main.inc.xhtml b/browser/components/preferences/main.inc.xhtml index ec30d31cde49..16c1880e1320 100644 --- a/browser/components/preferences/main.inc.xhtml +++ b/browser/components/preferences/main.inc.xhtml @@ -665,59 +665,4 @@ <label id="cfrFeaturesLearnMore" class="learnMore" data-l10n-id="browsing-cfr-recommendations-learn-more" is="text-link"/> </hbox> </groupbox> - -<hbox id="networkProxyCategory" - class="subcategory" - hidden="true" - data-category="paneGeneral"> - <html:h1 data-l10n-id="network-settings-title"/> -</hbox> - -<!-- Network Settings--> -<groupbox id="connectionGroup" data-category="paneGeneral" hidden="true"> - <label class="search-header" hidden="true"><html:h2 data-l10n-id="network-settings-title"/></label> - - <hbox align="center"> - <hbox align="center" flex="1"> - <description id="connectionSettingsDescription" control="connectionSettings"/> - <spacer width="5"/> - <label id="connectionSettingsLearnMore" class="learnMore" is="text-link" - data-l10n-id="network-proxy-connection-learn-more"> - </label> - <separator orient="vertical"/> - </hbox> - - <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. --> - <hbox> - <button id="connectionSettings" - is="highlightable-button" - class="accessory-button" - data-l10n-id="network-proxy-connection-settings" - searchkeywords="doh trr" - search-l10n-ids=" - connection-window.title, - connection-proxy-option-no.label, - connection-proxy-option-auto.label, - connection-proxy-option-system.label, - connection-proxy-option-manual.label, - connection-proxy-http, - connection-proxy-https, - connection-proxy-ftp, - connection-proxy-http-port, - connection-proxy-socks, - connection-proxy-socks4, - connection-proxy-socks5, - connection-proxy-noproxy, - connection-proxy-noproxy-desc, - connection-proxy-http-sharing.label, - connection-proxy-autotype.label, - connection-proxy-reload.label, - connection-proxy-autologin.label, - connection-proxy-socks-remote-dns.label, - connection-dns-over-https.label, - connection-dns-over-https-url-custom.label, - " /> - </hbox> - </hbox> -</groupbox> </html:template> diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js index acb15e61198a..812f06d084bb 100644 --- a/browser/components/preferences/main.js +++ b/browser/components/preferences/main.js @@ -360,15 +360,6 @@ var gMainPane = { }); this.updatePerformanceSettingsBox({ duringChangeEvent: false }); this.displayUseSystemLocale(); - let connectionSettingsLink = document.getElementById( - "connectionSettingsLearnMore" - ); - let connectionSettingsUrl = - Services.urlFormatter.formatURLPref("app.support.baseURL") + - "prefs-connection-settings"; - connectionSettingsLink.setAttribute("href", connectionSettingsUrl); - this.updateProxySettingsUI(); - initializeProxyUI(gMainPane); if (Services.prefs.getBoolPref("intl.multilingual.enabled")) { gMainPane.initBrowserLocale(); @@ -502,11 +493,6 @@ var gMainPane = { "change", gMainPane.updateHardwareAcceleration.bind(gMainPane) ); - setEventListener( - "connectionSettings", - "command", - gMainPane.showConnections - ); setEventListener( "browserContainersCheckbox", "command", diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js index 91e9e469cea2..a89fddd0306d 100644 --- a/browser/components/preferences/preferences.js +++ b/browser/components/preferences/preferences.js @@ -13,6 +13,7 @@ /* import-globals-from findInPage.js */ /* import-globals-from ../../base/content/utilityOverlay.js */ /* import-globals-from ../../../toolkit/content/preferencesBindings.js */ +/* import-globals-from ../torpreferences/content/torPane.js */ "use strict"; @@ -136,6 +137,14 @@ function init_all() { register_module("paneSync", gSyncPane); } register_module("paneSearchResults", gSearchResultsPane); + if (gTorPane.enabled) { + document.getElementById("category-tor").hidden = false; + register_module("paneTor", gTorPane); + } else { + // Remove the pane from the DOM so it doesn't get incorrectly included in search results. + document.getElementById("template-paneTor").remove(); + } + gSearchResultsPane.init(); gMainPane.preInit(); diff --git a/browser/components/preferences/preferences.xhtml b/browser/components/preferences/preferences.xhtml index 5f218b7f3e4b..6c1c12044d26 100644 --- a/browser/components/preferences/preferences.xhtml +++ b/browser/components/preferences/preferences.xhtml @@ -13,6 +13,7 @@ <?xml-stylesheet href="chrome://browser/skin/preferences/containers.css"?> <?xml-stylesheet href="chrome://browser/skin/preferences/privacy.css"?> <?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelPreferences.css"?> +<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?> <!DOCTYPE html [ <!ENTITY % aboutTorDTD SYSTEM "chrome://torbutton/locale/aboutTor.dtd"> @@ -155,6 +156,9 @@ <image class="category-icon"/> <label class="category-name" flex="1" data-l10n-id="pane-experimental-title"></label> </richlistitem> + +#include ../torpreferences/content/torCategory.inc.xhtml + </richlistbox> <spacer flex="1"/> @@ -215,6 +219,7 @@ #include containers.inc.xhtml #include sync.inc.xhtml #include experimental.inc.xhtml +#include ../torpreferences/content/torPane.xhtml </vbox> </vbox> </vbox> diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js index 2827dc8341ee..a279072199bf 100644 --- a/browser/components/preferences/privacy.js +++ b/browser/components/preferences/privacy.js @@ -80,6 +80,7 @@ XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function() { } }); +// TODO: module import via ChromeUtils.defineModuleGetter XPCOMUtils.defineLazyScriptGetter( this, ["SecurityLevelPreferences"], diff --git a/browser/components/torpreferences/content/parseFunctions.jsm b/browser/components/torpreferences/content/parseFunctions.jsm new file mode 100644 index 000000000000..954759de63a5 --- /dev/null +++ b/browser/components/torpreferences/content/parseFunctions.jsm @@ -0,0 +1,89 @@ +"use strict"; + +var EXPORTED_SYMBOLS = [ + "parsePort", + "parseAddrPort", + "parseUsernamePassword", + "parseAddrPortList", + "parseBridgeStrings", + "parsePortList", +]; + +// expects a string representation of an integer from 1 to 65535 +let parsePort = function(aPort) { + // ensure port string is a valid positive integer + const validIntRegex = /^[0-9]+$/; + if (!validIntRegex.test(aPort)) { + throw new Error(`Invalid PORT string : '${aPort}'`); + } + + // ensure port value is on valid range + let port = Number.parseInt(aPort); + if (port < 1 || port > 65535) { + throw new Error( + `Invalid PORT value, needs to be on range [1,65535] : '${port}'` + ); + } + + return port; +}; +// expects a string in the format: "ADDRESS:PORT" +let parseAddrPort = function(aAddrColonPort) { + let tokens = aAddrColonPort.split(":"); + if (tokens.length != 2) { + throw new Error(`Invalid ADDRESS:PORT string : '${aAddrColonPort}'`); + } + let address = tokens[0]; + let port = parsePort(tokens[1]); + return [address, port]; +}; + +// expects a string in the format: "USERNAME:PASSWORD" +// split on the first colon and any subsequent go into password +let parseUsernamePassword = function(aUsernameColonPassword) { + let colonIndex = aUsernameColonPassword.indexOf(":"); + if (colonIndex < 0) { + // we don't log the contents of the potentially password containing string + throw new Error("Invalid USERNAME:PASSWORD string"); + } + + let username = aUsernameColonPassword.substring(0, colonIndex); + let password = aUsernameColonPassword.substring(colonIndex + 1); + + return [username, password]; +}; + +// expects a string in the format: ADDRESS:PORT,ADDRESS:PORT,... +// returns array of ports (as ints) +let parseAddrPortList = function(aAddrPortList) { + let addrPorts = aAddrPortList.split(","); + // parse ADDRESS:PORT string and only keep the port (second element in returned array) + let retval = addrPorts.map(addrPort => parseAddrPort(addrPort)[1]); + return retval; +}; + +// expects a '/n' or '/r/n' delimited bridge string, which we split and trim +// each bridge string can also optionally have 'bridge' at the beginning ie: +// bridge $(type) $(address):$(port) $(certificate) +// we strip out the 'bridge' prefix here +let parseBridgeStrings = function(aBridgeStrings) { + + // replace carriage returns ('\r') with new lines ('\n') + aBridgeStrings = aBridgeStrings.replace(/\r/g, "\n"); + // then replace contiguous new lines ('\n') with a single one + aBridgeStrings = aBridgeStrings.replace(/[\n]+/g, "\n"); + + // split on the newline and for each bridge string: trim, remove starting 'bridge' string + // finally discard entries that are empty strings; empty strings could occur if we receive + // a new line containing only whitespace + let splitStrings = aBridgeStrings.split("\n"); + return splitStrings.map(val => val.trim().replace(/^bridge\s+/i, "")) + .filter(bridgeString => bridgeString != ""); +}; + +// expecting a ',' delimited list of ints with possible white space between +// returns an array of ints +let parsePortList = function(aPortListString) { + let splitStrings = aPortListString.split(","); + return splitStrings.map(val => parsePort(val.trim())); +}; diff --git a/browser/components/torpreferences/content/requestBridgeDialog.jsm b/browser/components/torpreferences/content/requestBridgeDialog.jsm new file mode 100644 index 000000000000..807d46cdfb18 --- /dev/null +++ b/browser/components/torpreferences/content/requestBridgeDialog.jsm @@ -0,0 +1,204 @@ +"use strict"; + +var EXPORTED_SYMBOLS = ["RequestBridgeDialog"]; + +const { BridgeDB } = ChromeUtils.import("resource:///modules/BridgeDB.jsm"); +const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); + +class RequestBridgeDialog { + constructor() { + this._dialog = null; + this._submitButton = null; + this._dialogDescription = null; + this._captchaImage = null; + this._captchaEntryTextbox = null; + this._captchaRefreshButton = null; + this._incorrectCaptchaHbox = null; + this._incorrectCaptchaLabel = null; + this._bridges = []; + this._proxyURI = null; + } + + static get selectors() { + return { + submitButton: + "accept" /* not really a selector but a key for dialog's getButton */, + dialogDescription: "description#torPreferences-requestBridge-description", + captchaImage: "image#torPreferences-requestBridge-captchaImage", + captchaEntryTextbox: "input#torPreferences-requestBridge-captchaTextbox", + refreshCaptchaButton: + "button#torPreferences-requestBridge-refreshCaptchaButton", + incorrectCaptchaHbox: + "hbox#torPreferences-requestBridge-incorrectCaptchaHbox", + incorrectCaptchaLabel: + "label#torPreferences-requestBridge-incorrectCaptchaError", + }; + } + + _populateXUL(dialog) { + const selectors = RequestBridgeDialog.selectors; + + this._dialog = dialog; + const dialogWin = dialog.parentElement; + dialogWin.setAttribute( + "title", + TorStrings.settings.requestBridgeDialogTitle + ); + // user may have opened a Request Bridge dialog in another tab, so update the + // CAPTCHA image or close out the dialog if we have a bridge list + this._dialog.addEventListener("focusin", () => { + const uri = BridgeDB.currentCaptchaImage; + const bridges = BridgeDB.currentBridges; + + // new captcha image + if (uri) { + this._setcaptchaImage(uri); + } else if (bridges) { + this._bridges = bridges; + this._submitButton.disabled = false; + this._dialog.cancelDialog(); + } + }); + + this._submitButton = this._dialog.getButton(selectors.submitButton); + this._submitButton.setAttribute("label", TorStrings.settings.submitCaptcha); + this._submitButton.disabled = true; + this._dialog.addEventListener("dialogaccept", e => { + e.preventDefault(); + this.onSubmitCaptcha(); + }); + + this._dialogDescription = this._dialog.querySelector( + selectors.dialogDescription + ); + this._dialogDescription.textContent = + TorStrings.settings.contactingBridgeDB; + + this._captchaImage = this._dialog.querySelector(selectors.captchaImage); + + // request captcha from bridge db + BridgeDB.requestNewCaptchaImage(this._proxyURI).then(uri => { + this._setcaptchaImage(uri); + }); + + this._captchaEntryTextbox = this._dialog.querySelector( + selectors.captchaEntryTextbox + ); + this._captchaEntryTextbox.setAttribute( + "placeholder", + TorStrings.settings.captchaTextboxPlaceholder + ); + this._captchaEntryTextbox.disabled = true; + // disable submit if entry textbox is empty + this._captchaEntryTextbox.oninput = () => { + this._submitButton.disabled = this._captchaEntryTextbox.value == ""; + }; + + this._captchaRefreshButton = this._dialog.querySelector( + selectors.refreshCaptchaButton + ); + this._captchaRefreshButton.disabled = true; + + this._incorrectCaptchaHbox = this._dialog.querySelector( + selectors.incorrectCaptchaHbox + ); + this._incorrectCaptchaLabel = this._dialog.querySelector( + selectors.incorrectCaptchaLabel + ); + this._incorrectCaptchaLabel.setAttribute( + "value", + TorStrings.settings.incorrectCaptcha + ); + + return true; + } + + _setcaptchaImage(uri) { + if (uri != this._captchaImage.src) { + this._captchaImage.src = uri; + this._dialogDescription.textContent = TorStrings.settings.solveTheCaptcha; + this._setUIDisabled(false); + this._captchaEntryTextbox.focus(); + this._captchaEntryTextbox.select(); + } + } + + _setUIDisabled(disabled) { + this._submitButton.disabled = this._captchaGuessIsEmpty() || disabled; + this._captchaEntryTextbox.disabled = disabled; + this._captchaRefreshButton.disabled = disabled; + } + + _captchaGuessIsEmpty() { + return this._captchaEntryTextbox.value == ""; + } + + init(window, dialog) { + // defer to later until firefox has populated the dialog with all our elements + window.setTimeout(() => { + this._populateXUL(dialog); + }, 0); + } + + close() { + BridgeDB.close(); + } + + /* + Event Handlers + */ + onSubmitCaptcha() { + let captchaText = this._captchaEntryTextbox.value.trim(); + // noop if the field is empty + if (captchaText == "") { + return; + } + + // freeze ui while we make request + this._setUIDisabled(true); + this._incorrectCaptchaHbox.style.visibility = "hidden"; + + BridgeDB.submitCaptchaGuess(captchaText) + .then(aBridges => { + this._bridges = aBridges; + + this._submitButton.disabled = false; + // This was successful, but use cancelDialog() to close, since + // we intercept the `dialogaccept` event. + this._dialog.cancelDialog(); + }) + .catch(aError => { + this._bridges = []; + this._setUIDisabled(false); + this._incorrectCaptchaHbox.style.visibility = "visible"; + }); + } + + onRefreshCaptcha() { + this._setUIDisabled(true); + this._captchaImage.src = ""; + this._dialogDescription.textContent = + TorStrings.settings.contactingBridgeDB; + this._captchaEntryTextbox.value = ""; + this._incorrectCaptchaHbox.style.visibility = "hidden"; + + BridgeDB.requestNewCaptchaImage(this._proxyURI).then(uri => { + this._setcaptchaImage(uri); + }); + } + + openDialog(gSubDialog, aProxyURI, aCloseCallback) { + this._proxyURI = aProxyURI; + gSubDialog.open( + "chrome://browser/content/torpreferences/requestBridgeDialog.xhtml", + { + features: "resizable=yes", + closingCallback: () => { + this.close(); + aCloseCallback(this._bridges); + } + }, + this, + ); + } +} diff --git a/browser/components/torpreferences/content/requestBridgeDialog.xhtml b/browser/components/torpreferences/content/requestBridgeDialog.xhtml new file mode 100644 index 000000000000..64c4507807fb --- /dev/null +++ b/browser/components/torpreferences/content/requestBridgeDialog.xhtml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> +<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?> + +<window type="child" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml"> +<dialog id="torPreferences-requestBridge-dialog" + buttons="accept,cancel"> + <!-- ok, so &#8203; 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">&#8203;</description> + <!-- init to transparent 400x125 png --> + <image id="torPreferences-requestBridge-captchaImage" flex="1"/> + <hbox id="torPreferences-requestBridge-inputHbox"> + <html:input id="torPreferences-requestBridge-captchaTextbox" type="text" style="-moz-box-flex: 1;"/> + <button id="torPreferences-requestBridge-refreshCaptchaButton" + image="chrome://browser/skin/reload.svg" + oncommand="requestBridgeDialog.onRefreshCaptcha();"/> + </hbox> + <hbox id="torPreferences-requestBridge-incorrectCaptchaHbox" align="center"> + <image id="torPreferences-requestBridge-errorIcon" /> + <label id="torPreferences-requestBridge-incorrectCaptchaError" flex="1"/> + </hbox> + <script type="application/javascript"><![CDATA[ + "use strict"; + + let requestBridgeDialog = window.arguments[0]; + let dialog = document.getElementById("torPreferences-requestBridge-dialog"); + requestBridgeDialog.init(window, dialog); + ]]></script> +</dialog> +</window> \ No newline at end of file diff --git a/browser/components/torpreferences/content/torBridgeSettings.jsm b/browser/components/torpreferences/content/torBridgeSettings.jsm new file mode 100644 index 000000000000..ceb61d3ec972 --- /dev/null +++ b/browser/components/torpreferences/content/torBridgeSettings.jsm @@ -0,0 +1,325 @@ +"use strict"; + +var EXPORTED_SYMBOLS = [ + "TorBridgeSource", + "TorBridgeSettings", + "makeTorBridgeSettingsNone", + "makeTorBridgeSettingsBuiltin", + "makeTorBridgeSettingsBridgeDB", + "makeTorBridgeSettingsUserProvided", +]; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const { TorProtocolService } = ChromeUtils.import( + "resource:///modules/TorProtocolService.jsm" +); +const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); + +const TorBridgeSource = { + NONE: "NONE", + BUILTIN: "BUILTIN", + BRIDGEDB: "BRIDGEDB", + USERPROVIDED: "USERPROVIDED", +}; + +class TorBridgeSettings { + constructor() { + this._bridgeSource = TorBridgeSource.NONE; + this._selectedDefaultBridgeType = null; + this._bridgeStrings = []; + } + + get selectedDefaultBridgeType() { + if (this._bridgeSource == TorBridgeSource.BUILTIN) { + return this._selectedDefaultBridgeType; + } + return undefined; + } + + get bridgeSource() { + return this._bridgeSource; + } + + // for display + get bridgeStrings() { + return this._bridgeStrings.join("\n"); + } + + // raw + get bridgeStringsArray() { + return this._bridgeStrings; + } + + static get defaultBridgeTypes() { + if (TorBridgeSettings._defaultBridgeTypes) { + return TorBridgeSettings._defaultBridgeTypes; + } + + let bridgeListBranch = Services.prefs.getBranch( + TorStrings.preferenceBranches.defaultBridge + ); + let bridgePrefs = bridgeListBranch.getChildList("", {}); + + // an unordered set for shoving bridge types into + let bridgeTypes = new Set(); + // look for keys ending in ".N" and treat string before that as the bridge type + const pattern = /\.[0-9]+$/; + for (const key of bridgePrefs) { + const offset = key.search(pattern); + if (offset != -1) { + const bt = key.substring(0, offset); + bridgeTypes.add(bt); + } + } + + // recommended bridge type goes first in the list + let recommendedBridgeType = Services.prefs.getCharPref( + TorStrings.preferenceKeys.recommendedBridgeType, + null + ); + + let retval = []; + if (recommendedBridgeType && bridgeTypes.has(recommendedBridgeType)) { + retval.push(recommendedBridgeType); + } + + for (const bridgeType of bridgeTypes.values()) { + if (bridgeType != recommendedBridgeType) { + retval.push(bridgeType); + } + } + + // cache off + TorBridgeSettings._defaultBridgeTypes = retval; + return retval; + } + + _readDefaultBridges(aBridgeType) { + let bridgeBranch = Services.prefs.getBranch( + TorStrings.preferenceBranches.defaultBridge + ); + let bridgeBranchPrefs = bridgeBranch.getChildList("", {}); + + let retval = []; + + // regex matches against strings ending in ".N" where N is a positive integer + let pattern = /\.[0-9]+$/; + for (const key of bridgeBranchPrefs) { + // verify the location of the match is the correct offset required for aBridgeType + // to fit, and that the string begins with aBridgeType + if ( + key.search(pattern) == aBridgeType.length && + key.startsWith(aBridgeType) + ) { + let bridgeStr = bridgeBranch.getCharPref(key); + retval.push(bridgeStr); + } + } + + // fisher-yates shuffle + // shuffle so that Tor Browser users don't all try the built-in bridges in the same order + for (let i = retval.length - 1; i > 0; --i) { + // number n such that 0.0 <= n < 1.0 + const n = Math.random(); + // integer j such that 0 <= j <= i + const j = Math.floor(n * (i + 1)); + + // swap values at indices i and j + const tmp = retval[i]; + retval[i] = retval[j]; + retval[j] = tmp; + } + + return retval; + } + + _readBridgeDBBridges() { + let bridgeBranch = Services.prefs.getBranch( + `${TorStrings.preferenceBranches.bridgeDBBridges}` + ); + let bridgeBranchPrefs = bridgeBranch.getChildList("", {}); + // the child prefs do not come in any particular order so sort the keys + // so the values can be compared to what we get out off torrc + bridgeBranchPrefs.sort(); + + // just assume all of the prefs under the parent point to valid bridge string + let retval = bridgeBranchPrefs.map(key => + bridgeBranch.getCharPref(key).trim() + ); + + return retval; + } + + _readTorrcBridges() { + let bridgeList = TorProtocolService.readStringArraySetting( + TorStrings.configKeys.bridgeList + ); + + let retval = []; + for (const line of bridgeList) { + let trimmedLine = line.trim(); + if (trimmedLine) { + retval.push(trimmedLine); + } + } + + return retval; + } + + // analagous to initBridgeSettings() + readSettings() { + // restore to defaults + this._bridgeSource = TorBridgeSource.NONE; + this._selectedDefaultBridgeType = null; + this._bridgeStrings = []; + + // So the way tor-launcher determines the origin of the configured bridges is a bit + // weird and depends on inferring our scenario based on some firefox prefs and the + // relationship between the saved list of bridges in about:config vs the list saved in torrc + + // first off, if "extensions.torlauncher.default_bridge_type" is set to one of our + // builtin default types (obfs4, meek-azure, snowflake, etc) then we provide the + // bridges in "extensions.torlauncher.default_bridge.*" (filtered by our default_bridge_type) + + // next, we compare the list of bridges saved in torrc to the bridges stored in the + // "extensions.torlauncher.bridgedb_bridge."" branch. If they match *exactly* then we assume + // the bridges were retrieved from BridgeDB and use those. If the torrc list is empty then we know + // we have no bridge settings + + // finally, if none of the previous conditions are not met, it is assumed the bridges stored in + // torrc are user-provided + + // what we should(?) do once we excise tor-launcher entirely is explicitly store an int/enum in + // about:config that tells us which scenario we are in so we don't have to guess + + let defaultBridgeType = Services.prefs.getCharPref( + TorStrings.preferenceKeys.defaultBridgeType, + null + ); + + // check if source is BUILTIN + if (defaultBridgeType) { + this._bridgeStrings = this._readDefaultBridges(defaultBridgeType); + this._bridgeSource = TorBridgeSource.BUILTIN; + this._selectedDefaultBridgeType = defaultBridgeType; + return; + } + + let torrcBridges = this._readTorrcBridges(); + + // no stored bridges means no bridge is in use + if (torrcBridges.length == 0) { + this._bridgeStrings = []; + this._bridgeSource = TorBridgeSource.NONE; + return; + } + + let bridgedbBridges = this._readBridgeDBBridges(); + + // if these two lists are equal then we got our bridges from bridgedb + // ie: same element in identical order + let arraysEqual = (left, right) => { + if (left.length != right.length) { + return false; + } + const length = left.length; + for (let i = 0; i < length; ++i) { + if (left[i] != right[i]) { + return false; + } + } + return true; + }; + + // agreement between prefs and torrc means bridgedb bridges + if (arraysEqual(torrcBridges, bridgedbBridges)) { + this._bridgeStrings = torrcBridges; + this._bridgeSource = TorBridgeSource.BRIDGEDB; + return; + } + + // otherwise they must be user provided + this._bridgeStrings = torrcBridges; + this._bridgeSource = TorBridgeSource.USERPROVIDED; + } + + writeSettings() { + let settingsObject = new Map(); + + // init tor bridge settings to null + settingsObject.set(TorStrings.configKeys.useBridges, null); + settingsObject.set(TorStrings.configKeys.bridgeList, null); + + // clear bridge related firefox prefs + Services.prefs.setCharPref(TorStrings.preferenceKeys.defaultBridgeType, ""); + let bridgeBranch = Services.prefs.getBranch( + `${TorStrings.preferenceBranches.bridgeDBBridges}` + ); + let bridgeBranchPrefs = bridgeBranch.getChildList("", {}); + for (const pref of bridgeBranchPrefs) { + Services.prefs.clearUserPref( + `${TorStrings.preferenceBranches.bridgeDBBridges}${pref}` + ); + } + + switch (this._bridgeSource) { + case TorBridgeSource.BUILTIN: + // set builtin bridge type to use in prefs + Services.prefs.setCharPref( + TorStrings.preferenceKeys.defaultBridgeType, + this._selectedDefaultBridgeType + ); + break; + case TorBridgeSource.BRIDGEDB: + // save bridges off to prefs + for (let i = 0; i < this.bridgeStringsArray.length; ++i) { + Services.prefs.setCharPref( + `${TorStrings.preferenceBranches.bridgeDBBridges}${i}`, + this.bridgeStringsArray[i] + ); + } + break; + } + + // write over our bridge list if bridges are enabled + if (this._bridgeSource != TorBridgeSource.NONE) { + settingsObject.set(TorStrings.configKeys.useBridges, true); + settingsObject.set( + TorStrings.configKeys.bridgeList, + this.bridgeStringsArray + ); + } + TorProtocolService.writeSettings(settingsObject); + } +} + +function makeTorBridgeSettingsNone() { + return new TorBridgeSettings(); +} + +function makeTorBridgeSettingsBuiltin(aBridgeType) { + let retval = new TorBridgeSettings(); + retval._bridgeSource = TorBridgeSource.BUILTIN; + retval._selectedDefaultBridgeType = aBridgeType; + retval._bridgeStrings = retval._readDefaultBridges(aBridgeType); + + return retval; +} + +function makeTorBridgeSettingsBridgeDB(aBridges) { + let retval = new TorBridgeSettings(); + retval._bridgeSource = TorBridgeSource.BRIDGEDB; + retval._selectedDefaultBridgeType = null; + retval._bridgeStrings = aBridges; + + return retval; +} + +function makeTorBridgeSettingsUserProvided(aBridges) { + let retval = new TorBridgeSettings(); + retval._bridgeSource = TorBridgeSource.USERPROVIDED; + retval._selectedDefaultBridgeType = null; + retval._bridgeStrings = aBridges; + + return retval; +} diff --git a/browser/components/torpreferences/content/torCategory.inc.xhtml b/browser/components/torpreferences/content/torCategory.inc.xhtml new file mode 100644 index 000000000000..abe56200f571 --- /dev/null +++ b/browser/components/torpreferences/content/torCategory.inc.xhtml @@ -0,0 +1,9 @@ +<richlistitem id="category-tor" + class="category" + value="paneTor" + helpTopic="prefs-tor" + align="center" + hidden="true"> + <image class="category-icon"/> + <label id="torPreferences-labelCategory" class="category-name" flex="1" value="Tor"/> +</richlistitem> diff --git a/browser/components/torpreferences/content/torFirewallSettings.jsm b/browser/components/torpreferences/content/torFirewallSettings.jsm new file mode 100644 index 000000000000..e77f18ef2fae --- /dev/null +++ b/browser/components/torpreferences/content/torFirewallSettings.jsm @@ -0,0 +1,72 @@ +"use strict"; + +var EXPORTED_SYMBOLS = [ + "TorFirewallSettings", + "makeTorFirewallSettingsNone", + "makeTorFirewallSettingsCustom", +]; + +const { TorProtocolService } = ChromeUtils.import( + "resource:///modules/TorProtocolService.jsm" +); +const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); +const { parseAddrPortList } = ChromeUtils.import( + "chrome://browser/content/torpreferences/parseFunctions.jsm" +); + +class TorFirewallSettings { + constructor() { + this._allowedPorts = []; + } + + get portsConfigurationString() { + let portStrings = this._allowedPorts.map(port => `*:${port}`); + return portStrings.join(","); + } + + get commaSeparatedListString() { + return this._allowedPorts.join(","); + } + + get hasPorts() { + return this._allowedPorts.length > 0; + } + + readSettings() { + let addressPortList = TorProtocolService.readStringSetting( + TorStrings.configKeys.reachableAddresses + ); + + let allowedPorts = []; + if (addressPortList) { + allowedPorts = parseAddrPortList(addressPortList); + } + this._allowedPorts = allowedPorts; + } + + writeSettings() { + let settingsObject = new Map(); + + // init to null so Tor daemon resets if no ports + settingsObject.set(TorStrings.configKeys.reachableAddresses, null); + + if (this._allowedPorts.length > 0) { + settingsObject.set( + TorStrings.configKeys.reachableAddresses, + this.portsConfigurationString + ); + } + + TorProtocolService.writeSettings(settingsObject); + } +} + +function makeTorFirewallSettingsNone() { + return new TorFirewallSettings(); +} + +function makeTorFirewallSettingsCustom(aPortsList) { + let retval = new TorFirewallSettings(); + retval._allowedPorts = aPortsList; + return retval; +} diff --git a/browser/components/torpreferences/content/torLogDialog.jsm b/browser/components/torpreferences/content/torLogDialog.jsm new file mode 100644 index 000000000000..ecc684d878c2 --- /dev/null +++ b/browser/components/torpreferences/content/torLogDialog.jsm @@ -0,0 +1,66 @@ +"use strict"; + +var EXPORTED_SYMBOLS = ["TorLogDialog"]; + +const { TorProtocolService } = ChromeUtils.import( + "resource:///modules/TorProtocolService.jsm" +); +const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); + +class TorLogDialog { + constructor() { + this._dialog = null; + this._logTextarea = null; + this._copyLogButton = null; + } + + static get selectors() { + return { + copyLogButton: "extra1", + logTextarea: "textarea#torPreferences-torDialog-textarea", + }; + } + + _populateXUL(aDialog) { + this._dialog = aDialog; + const dialogWin = this._dialog.parentElement; + dialogWin.setAttribute("title", TorStrings.settings.torLogDialogTitle); + + this._logTextarea = this._dialog.querySelector( + TorLogDialog.selectors.logTextarea + ); + + this._copyLogButton = this._dialog.getButton( + TorLogDialog.selectors.copyLogButton + ); + this._copyLogButton.setAttribute("label", TorStrings.settings.copyLog); + this._copyLogButton.addEventListener("command", () => { + this.copyTorLog(); + }); + + this._logTextarea.value = TorProtocolService.getLog(); + } + + init(window, aDialog) { + // defer to later until firefox has populated the dialog with all our elements + window.setTimeout(() => { + this._populateXUL(aDialog); + }, 0); + } + + copyTorLog() { + // Copy tor log messages to the system clipboard. + let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( + Ci.nsIClipboardHelper + ); + clipboard.copyString(this._logTextarea.value); + } + + openDialog(gSubDialog) { + gSubDialog.open( + "chrome://browser/content/torpreferences/torLogDialog.xhtml", + { features: "resizable=yes" }, + this + ); + } +} diff --git a/browser/components/torpreferences/content/torLogDialog.xhtml b/browser/components/torpreferences/content/torLogDialog.xhtml new file mode 100644 index 000000000000..9c17f8132978 --- /dev/null +++ b/browser/components/torpreferences/content/torLogDialog.xhtml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> +<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?> + +<window type="child" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml"> +<dialog id="torPreferences-torLog-dialog" + buttons="accept,extra1"> + <html:textarea + id="torPreferences-torDialog-textarea" + multiline="true" + readonly="true"/> + <script type="application/javascript"><![CDATA[ + "use strict"; + + let torLogDialog = window.arguments[0]; + let dialog = document.getElementById("torPreferences-torLog-dialog"); + torLogDialog.init(window, dialog); + ]]></script> +</dialog> +</window> \ No newline at end of file diff --git a/browser/components/torpreferences/content/torPane.js b/browser/components/torpreferences/content/torPane.js new file mode 100644 index 000000000000..49054b5dac6a --- /dev/null +++ b/browser/components/torpreferences/content/torPane.js @@ -0,0 +1,857 @@ +"use strict"; + +const { TorProtocolService } = ChromeUtils.import( + "resource:///modules/TorProtocolService.jsm" +); + +const { + TorBridgeSource, + TorBridgeSettings, + makeTorBridgeSettingsNone, + makeTorBridgeSettingsBuiltin, + makeTorBridgeSettingsBridgeDB, + makeTorBridgeSettingsUserProvided, +} = ChromeUtils.import( + "chrome://browser/content/torpreferences/torBridgeSettings.jsm" +); + +const { + TorProxyType, + TorProxySettings, + makeTorProxySettingsNone, + makeTorProxySettingsSocks4, + makeTorProxySettingsSocks5, + makeTorProxySettingsHTTPS, +} = ChromeUtils.import( + "chrome://browser/content/torpreferences/torProxySettings.jsm" +); +const { + TorFirewallSettings, + makeTorFirewallSettingsNone, + makeTorFirewallSettingsCustom, +} = ChromeUtils.import( + "chrome://browser/content/torpreferences/torFirewallSettings.jsm" +); + +const { TorLogDialog } = ChromeUtils.import( + "chrome://browser/content/torpreferences/torLogDialog.jsm" +); + +const { RequestBridgeDialog } = ChromeUtils.import( + "chrome://browser/content/torpreferences/requestBridgeDialog.jsm" +); + +ChromeUtils.defineModuleGetter( + this, + "TorStrings", + "resource:///modules/TorStrings.jsm" +); + +const { parsePort, parseBridgeStrings, parsePortList } = ChromeUtils.import( + "chrome://browser/content/torpreferences/parseFunctions.jsm" +); + +/* + Tor Pane + + Code for populating the XUL in about:preferences#tor, handling input events, interfacing with tor-launcher +*/ +const gTorPane = (function() { + /* CSS selectors for all of the Tor Network DOM elements we need to access */ + const selectors = { + category: { + title: "label#torPreferences-labelCategory", + }, + torPreferences: { + header: "h1#torPreferences-header", + description: "span#torPreferences-description", + learnMore: "label#torPreferences-learnMore", + }, + bridges: { + header: "h2#torPreferences-bridges-header", + description: "span#torPreferences-bridges-description", + learnMore: "label#torPreferences-bridges-learnMore", + useBridgeCheckbox: "checkbox#torPreferences-bridges-toggle", + bridgeSelectionRadiogroup: + "radiogroup#torPreferences-bridges-bridgeSelection", + builtinBridgeOption: "radio#torPreferences-bridges-radioBuiltin", + builtinBridgeList: "menulist#torPreferences-bridges-builtinList", + requestBridgeOption: "radio#torPreferences-bridges-radioRequestBridge", + requestBridgeButton: "button#torPreferences-bridges-buttonRequestBridge", + requestBridgeTextarea: + "textarea#torPreferences-bridges-textareaRequestBridge", + provideBridgeOption: "radio#torPreferences-bridges-radioProvideBridge", + provideBridgeDescription: + "description#torPreferences-bridges-descriptionProvideBridge", + provideBridgeTextarea: + "textarea#torPreferences-bridges-textareaProvideBridge", + }, + advanced: { + header: "h2#torPreferences-advanced-header", + description: "span#torPreferences-advanced-description", + learnMore: "label#torPreferences-advanced-learnMore", + useProxyCheckbox: "checkbox#torPreferences-advanced-toggleProxy", + proxyTypeLabel: "label#torPreferences-localProxy-type", + proxyTypeList: "menulist#torPreferences-localProxy-builtinList", + proxyAddressLabel: "label#torPreferences-localProxy-address", + proxyAddressTextbox: "input#torPreferences-localProxy-textboxAddress", + proxyPortLabel: "label#torPreferences-localProxy-port", + proxyPortTextbox: "input#torPreferences-localProxy-textboxPort", + proxyUsernameLabel: "label#torPreferences-localProxy-username", + proxyUsernameTextbox: "input#torPreferences-localProxy-textboxUsername", + proxyPasswordLabel: "label#torPreferences-localProxy-password", + proxyPasswordTextbox: "input#torPreferences-localProxy-textboxPassword", + useFirewallCheckbox: "checkbox#torPreferences-advanced-toggleFirewall", + firewallAllowedPortsLabel: "label#torPreferences-advanced-allowedPorts", + firewallAllowedPortsTextbox: + "input#torPreferences-advanced-textboxAllowedPorts", + torLogsLabel: "label#torPreferences-torLogs", + torLogsButton: "button#torPreferences-buttonTorLogs", + }, + }; /* selectors */ + + let retval = { + // cached frequently accessed DOM elements + _useBridgeCheckbox: null, + _bridgeSelectionRadiogroup: null, + _builtinBridgeOption: null, + _builtinBridgeMenulist: null, + _requestBridgeOption: null, + _requestBridgeButton: null, + _requestBridgeTextarea: null, + _provideBridgeOption: null, + _provideBridgeTextarea: null, + _useProxyCheckbox: null, + _proxyTypeLabel: null, + _proxyTypeMenulist: null, + _proxyAddressLabel: null, + _proxyAddressTextbox: null, + _proxyPortLabel: null, + _proxyPortTextbox: null, + _proxyUsernameLabel: null, + _proxyUsernameTextbox: null, + _proxyPasswordLabel: null, + _proxyPasswordTextbox: null, + _useFirewallCheckbox: null, + _allowedPortsLabel: null, + _allowedPortsTextbox: null, + + // tor network settings + _bridgeSettings: null, + _proxySettings: null, + _firewallSettings: null, + + // disables the provided list of elements + _setElementsDisabled(elements, disabled) { + for (let currentElement of elements) { + currentElement.disabled = disabled; + } + }, + + // populate xul with strings and cache the relevant elements + _populateXUL() { + // saves tor settings to disk when navigate away from about:preferences + window.addEventListener("blur", val => { + TorProtocolService.flushSettings(); + }); + + document + .querySelector(selectors.category.title) + .setAttribute("value", TorStrings.settings.categoryTitle); + + let prefpane = document.getElementById("mainPrefPane"); + + // Heading + prefpane.querySelector(selectors.torPreferences.header).innerText = + TorStrings.settings.torPreferencesHeading; + prefpane.querySelector(selectors.torPreferences.description).textContent = + TorStrings.settings.torPreferencesDescription; + { + let learnMore = prefpane.querySelector( + selectors.torPreferences.learnMore + ); + learnMore.setAttribute("value", TorStrings.settings.learnMore); + learnMore.setAttribute( + "href", + TorStrings.settings.learnMoreTorBrowserURL + ); + } + + // Bridge setup + prefpane.querySelector(selectors.bridges.header).innerText = + TorStrings.settings.bridgesHeading; + prefpane.querySelector(selectors.bridges.description).textContent = + TorStrings.settings.bridgesDescription; + { + let learnMore = prefpane.querySelector(selectors.bridges.learnMore); + learnMore.setAttribute("value", TorStrings.settings.learnMore); + learnMore.setAttribute("href", TorStrings.settings.learnMoreBridgesURL); + } + + this._useBridgeCheckbox = prefpane.querySelector( + selectors.bridges.useBridgeCheckbox + ); + this._useBridgeCheckbox.setAttribute( + "label", + TorStrings.settings.useBridge + ); + this._useBridgeCheckbox.addEventListener("command", e => { + const checked = this._useBridgeCheckbox.checked; + gTorPane.onToggleBridge(checked).onUpdateBridgeSettings(); + }); + this._bridgeSelectionRadiogroup = prefpane.querySelector( + selectors.bridges.bridgeSelectionRadiogroup + ); + this._bridgeSelectionRadiogroup.value = TorBridgeSource.BUILTIN; + this._bridgeSelectionRadiogroup.addEventListener("command", e => { + const value = this._bridgeSelectionRadiogroup.value; + gTorPane.onSelectBridgeOption(value).onUpdateBridgeSettings(); + }); + + // Builtin bridges + this._builtinBridgeOption = prefpane.querySelector( + selectors.bridges.builtinBridgeOption + ); + this._builtinBridgeOption.setAttribute( + "label", + TorStrings.settings.selectBridge + ); + this._builtinBridgeOption.setAttribute("value", TorBridgeSource.BUILTIN); + this._builtinBridgeMenulist = prefpane.querySelector( + selectors.bridges.builtinBridgeList + ); + this._builtinBridgeMenulist.addEventListener("command", e => { + gTorPane.onUpdateBridgeSettings(); + }); + + // Request bridge + this._requestBridgeOption = prefpane.querySelector( + selectors.bridges.requestBridgeOption + ); + this._requestBridgeOption.setAttribute( + "label", + TorStrings.settings.requestBridgeFromTorProject + ); + this._requestBridgeOption.setAttribute("value", TorBridgeSource.BRIDGEDB); + this._requestBridgeButton = prefpane.querySelector( + selectors.bridges.requestBridgeButton + ); + this._requestBridgeButton.setAttribute( + "label", + TorStrings.settings.requestNewBridge + ); + this._requestBridgeButton.addEventListener("command", () => + gTorPane.onRequestBridge() + ); + this._requestBridgeTextarea = prefpane.querySelector( + selectors.bridges.requestBridgeTextarea + ); + + // Provide a bridge + this._provideBridgeOption = prefpane.querySelector( + selectors.bridges.provideBridgeOption + ); + this._provideBridgeOption.setAttribute( + "label", + TorStrings.settings.provideBridge + ); + this._provideBridgeOption.setAttribute( + "value", + TorBridgeSource.USERPROVIDED + ); + prefpane.querySelector( + selectors.bridges.provideBridgeDescription + ).textContent = TorStrings.settings.provideBridgeDirections; + this._provideBridgeTextarea = prefpane.querySelector( + selectors.bridges.provideBridgeTextarea + ); + this._provideBridgeTextarea.setAttribute( + "placeholder", + TorStrings.settings.provideBridgePlaceholder + ); + this._provideBridgeTextarea.addEventListener("blur", () => { + gTorPane.onUpdateBridgeSettings(); + }); + + // Advanced setup + prefpane.querySelector(selectors.advanced.header).innerText = + TorStrings.settings.advancedHeading; + prefpane.querySelector(selectors.advanced.description).textContent = + TorStrings.settings.advancedDescription; + { + let learnMore = prefpane.querySelector(selectors.advanced.learnMore); + learnMore.setAttribute("value", TorStrings.settings.learnMore); + learnMore.setAttribute( + "href", + TorStrings.settings.learnMoreNetworkSettingsURL + ); + } + + // Local Proxy + this._useProxyCheckbox = prefpane.querySelector( + selectors.advanced.useProxyCheckbox + ); + this._useProxyCheckbox.setAttribute( + "label", + TorStrings.settings.useLocalProxy + ); + this._useProxyCheckbox.addEventListener("command", e => { + const checked = this._useProxyCheckbox.checked; + gTorPane.onToggleProxy(checked).onUpdateProxySettings(); + }); + this._proxyTypeLabel = prefpane.querySelector( + selectors.advanced.proxyTypeLabel + ); + this._proxyTypeLabel.setAttribute("value", TorStrings.settings.proxyType); + + let mockProxies = [ + { + value: TorProxyType.SOCKS4, + label: TorStrings.settings.proxyTypeSOCKS4, + }, + { + value: TorProxyType.SOCKS5, + label: TorStrings.settings.proxyTypeSOCKS5, + }, + { value: TorProxyType.HTTPS, label: TorStrings.settings.proxyTypeHTTP }, + ]; + this._proxyTypeMenulist = prefpane.querySelector( + selectors.advanced.proxyTypeList + ); + this._proxyTypeMenulist.addEventListener("command", e => { + const value = this._proxyTypeMenulist.value; + gTorPane.onSelectProxyType(value).onUpdateProxySettings(); + }); + for (let currentProxy of mockProxies) { + let menuEntry = document.createXULElement("menuitem"); + menuEntry.setAttribute("value", currentProxy.value); + menuEntry.setAttribute("label", currentProxy.label); + this._proxyTypeMenulist + .querySelector("menupopup") + .appendChild(menuEntry); + } + + this._proxyAddressLabel = prefpane.querySelector( + selectors.advanced.proxyAddressLabel + ); + this._proxyAddressLabel.setAttribute( + "value", + TorStrings.settings.proxyAddress + ); + this._proxyAddressTextbox = prefpane.querySelector( + selectors.advanced.proxyAddressTextbox + ); + this._proxyAddressTextbox.setAttribute( + "placeholder", + TorStrings.settings.proxyAddressPlaceholder + ); + this._proxyAddressTextbox.addEventListener("blur", () => { + gTorPane.onUpdateProxySettings(); + }); + this._proxyPortLabel = prefpane.querySelector( + selectors.advanced.proxyPortLabel + ); + this._proxyPortLabel.setAttribute("value", TorStrings.settings.proxyPort); + this._proxyPortTextbox = prefpane.querySelector( + selectors.advanced.proxyPortTextbox + ); + this._proxyPortTextbox.addEventListener("blur", () => { + gTorPane.onUpdateProxySettings(); + }); + this._proxyUsernameLabel = prefpane.querySelector( + selectors.advanced.proxyUsernameLabel + ); + this._proxyUsernameLabel.setAttribute( + "value", + TorStrings.settings.proxyUsername + ); + this._proxyUsernameTextbox = prefpane.querySelector( + selectors.advanced.proxyUsernameTextbox + ); + this._proxyUsernameTextbox.setAttribute( + "placeholder", + TorStrings.settings.proxyUsernamePasswordPlaceholder + ); + this._proxyUsernameTextbox.addEventListener("blur", () => { + gTorPane.onUpdateProxySettings(); + }); + this._proxyPasswordLabel = prefpane.querySelector( + selectors.advanced.proxyPasswordLabel + ); + this._proxyPasswordLabel.setAttribute( + "value", + TorStrings.settings.proxyPassword + ); + this._proxyPasswordTextbox = prefpane.querySelector( + selectors.advanced.proxyPasswordTextbox + ); + this._proxyPasswordTextbox.setAttribute( + "placeholder", + TorStrings.settings.proxyUsernamePasswordPlaceholder + ); + this._proxyPasswordTextbox.addEventListener("blur", () => { + gTorPane.onUpdateProxySettings(); + }); + + // Local firewall + this._useFirewallCheckbox = prefpane.querySelector( + selectors.advanced.useFirewallCheckbox + ); + this._useFirewallCheckbox.setAttribute( + "label", + TorStrings.settings.useFirewall + ); + this._useFirewallCheckbox.addEventListener("command", e => { + const checked = this._useFirewallCheckbox.checked; + gTorPane.onToggleFirewall(checked).onUpdateFirewallSettings(); + }); + this._allowedPortsLabel = prefpane.querySelector( + selectors.advanced.firewallAllowedPortsLabel + ); + this._allowedPortsLabel.setAttribute( + "value", + TorStrings.settings.allowedPorts + ); + this._allowedPortsTextbox = prefpane.querySelector( + selectors.advanced.firewallAllowedPortsTextbox + ); + this._allowedPortsTextbox.setAttribute( + "placeholder", + TorStrings.settings.allowedPortsPlaceholder + ); + this._allowedPortsTextbox.addEventListener("blur", () => { + gTorPane.onUpdateFirewallSettings(); + }); + + // Tor logs + prefpane + .querySelector(selectors.advanced.torLogsLabel) + .setAttribute("value", TorStrings.settings.showTorDaemonLogs); + let torLogsButton = prefpane.querySelector( + selectors.advanced.torLogsButton + ); + torLogsButton.setAttribute("label", TorStrings.settings.showLogs); + torLogsButton.addEventListener("command", () => { + gTorPane.onViewTorLogs(); + }); + + // Disable all relevant elements by default + this._setElementsDisabled( + [ + this._builtinBridgeOption, + this._builtinBridgeMenulist, + this._requestBridgeOption, + this._requestBridgeButton, + this._requestBridgeTextarea, + this._provideBridgeOption, + this._provideBridgeTextarea, + this._proxyTypeLabel, + this._proxyTypeMenulist, + this._proxyAddressLabel, + this._proxyAddressTextbox, + this._proxyPortLabel, + this._proxyPortTextbox, + this._proxyUsernameLabel, + this._proxyUsernameTextbox, + this._proxyPasswordLabel, + this._proxyPasswordTextbox, + this._allowedPortsLabel, + this._allowedPortsTextbox, + ], + true + ); + + // load bridge settings + let torBridgeSettings = new TorBridgeSettings(); + torBridgeSettings.readSettings(); + + // populate the bridge list + for (let currentBridge of TorBridgeSettings.defaultBridgeTypes) { + let menuEntry = document.createXULElement("menuitem"); + menuEntry.setAttribute("value", currentBridge); + menuEntry.setAttribute("label", currentBridge); + this._builtinBridgeMenulist + .querySelector("menupopup") + .appendChild(menuEntry); + } + + this.onSelectBridgeOption(torBridgeSettings.bridgeSource); + this.onToggleBridge( + torBridgeSettings.bridgeSource != TorBridgeSource.NONE + ); + switch (torBridgeSettings.bridgeSource) { + case TorBridgeSource.NONE: + break; + case TorBridgeSource.BUILTIN: + this._builtinBridgeMenulist.value = + torBridgeSettings.selectedDefaultBridgeType; + break; + case TorBridgeSource.BRIDGEDB: + this._requestBridgeTextarea.value = torBridgeSettings.bridgeStrings; + break; + case TorBridgeSource.USERPROVIDED: + this._provideBridgeTextarea.value = torBridgeSettings.bridgeStrings; + break; + } + + this._bridgeSettings = torBridgeSettings; + + // load proxy settings + let torProxySettings = new TorProxySettings(); + torProxySettings.readSettings(); + + if (torProxySettings.type != TorProxyType.NONE) { + this.onToggleProxy(true); + this.onSelectProxyType(torProxySettings.type); + this._proxyAddressTextbox.value = torProxySettings.address; + this._proxyPortTextbox.value = torProxySettings.port; + this._proxyUsernameTextbox.value = torProxySettings.username; + this._proxyPasswordTextbox.value = torProxySettings.password; + } + + this._proxySettings = torProxySettings; + + // load firewall settings + let torFirewallSettings = new TorFirewallSettings(); + torFirewallSettings.readSettings(); + + if (torFirewallSettings.hasPorts) { + this.onToggleFirewall(true); + this._allowedPortsTextbox.value = + torFirewallSettings.commaSeparatedListString; + } + + this._firewallSettings = torFirewallSettings; + }, + + init() { + this._populateXUL(); + }, + + // whether the page should be present in about:preferences + get enabled() { + return TorProtocolService.ownsTorDaemon; + }, + + // + // Callbacks + // + + // callback when using bridges toggled + onToggleBridge(enabled) { + this._useBridgeCheckbox.checked = enabled; + let disabled = !enabled; + + // first disable all the bridge related elements + this._setElementsDisabled( + [ + this._builtinBridgeOption, + this._builtinBridgeMenulist, + this._requestBridgeOption, + this._requestBridgeButton, + this._requestBridgeTextarea, + this._provideBridgeOption, + this._provideBridgeTextarea, + ], + disabled + ); + + // and selectively re-enable based on the radiogroup's current value + if (enabled) { + this.onSelectBridgeOption(this._bridgeSelectionRadiogroup.value); + } else { + this.onSelectBridgeOption(TorBridgeSource.NONE); + } + return this; + }, + + // callback when a bridge option is selected + onSelectBridgeOption(source) { + // disable all of the bridge elements under radio buttons + this._setElementsDisabled( + [ + this._builtinBridgeMenulist, + this._requestBridgeButton, + this._requestBridgeTextarea, + this._provideBridgeTextarea, + ], + true + ); + + if (source != TorBridgeSource.NONE) { + this._bridgeSelectionRadiogroup.value = source; + } + + switch (source) { + case TorBridgeSource.BUILTIN: { + this._setElementsDisabled([this._builtinBridgeMenulist], false); + break; + } + case TorBridgeSource.BRIDGEDB: { + this._setElementsDisabled( + [this._requestBridgeButton, this._requestBridgeTextarea], + false + ); + break; + } + case TorBridgeSource.USERPROVIDED: { + this._setElementsDisabled([this._provideBridgeTextarea], false); + break; + } + } + return this; + }, + + // called when the request bridge button is activated + onRequestBridge() { + let requestBridgeDialog = new RequestBridgeDialog(); + requestBridgeDialog.openDialog( + gSubDialog, + this._proxySettings.proxyURI, + aBridges => { + if (aBridges.length > 0) { + let bridgeSettings = makeTorBridgeSettingsBridgeDB(aBridges); + bridgeSettings.writeSettings(); + this._bridgeSettings = bridgeSettings; + + this._requestBridgeTextarea.value = bridgeSettings.bridgeStrings; + } + } + ); + return this; + }, + + // pushes bridge settings from UI to tor + onUpdateBridgeSettings() { + let bridgeSettings = null; + + let source = this._useBridgeCheckbox.checked + ? this._bridgeSelectionRadiogroup.value + : TorBridgeSource.NONE; + switch (source) { + case TorBridgeSource.NONE: { + bridgeSettings = makeTorBridgeSettingsNone(); + break; + } + case TorBridgeSource.BUILTIN: { + // if there is a built-in bridge already selected, use that + let bridgeType = this._builtinBridgeMenulist.value; + if (bridgeType) { + bridgeSettings = makeTorBridgeSettingsBuiltin(bridgeType); + } else { + bridgeSettings = makeTorBridgeSettingsNone(); + } + break; + } + case TorBridgeSource.BRIDGEDB: { + // if there are bridgedb bridges saved in the text area, use them + let bridgeStrings = this._requestBridgeTextarea.value; + if (bridgeStrings) { + let bridgeStringList = parseBridgeStrings(bridgeStrings); + bridgeSettings = makeTorBridgeSettingsBridgeDB(bridgeStringList); + } else { + bridgeSettings = makeTorBridgeSettingsNone(); + } + break; + } + case TorBridgeSource.USERPROVIDED: { + // if bridges already exist in the text area, use them + let bridgeStrings = this._provideBridgeTextarea.value; + if (bridgeStrings) { + let bridgeStringList = parseBridgeStrings(bridgeStrings); + bridgeSettings = makeTorBridgeSettingsUserProvided( + bridgeStringList + ); + } else { + bridgeSettings = makeTorBridgeSettingsNone(); + } + break; + } + } + bridgeSettings.writeSettings(); + this._bridgeSettings = bridgeSettings; + return this; + }, + + // callback when proxy is toggled + onToggleProxy(enabled) { + this._useProxyCheckbox.checked = enabled; + let disabled = !enabled; + + this._setElementsDisabled( + [ + this._proxyTypeLabel, + this._proxyTypeMenulist, + this._proxyAddressLabel, + this._proxyAddressTextbox, + this._proxyPortLabel, + this._proxyPortTextbox, + this._proxyUsernameLabel, + this._proxyUsernameTextbox, + this._proxyPasswordLabel, + this._proxyPasswordTextbox, + ], + disabled + ); + this.onSelectProxyType(this._proxyTypeMenulist.value); + return this; + }, + + // callback when proxy type is changed + onSelectProxyType(value) { + if (value == "") { + value = TorProxyType.NONE; + } + this._proxyTypeMenulist.value = value; + switch (value) { + case TorProxyType.NONE: { + this._setElementsDisabled( + [ + this._proxyAddressLabel, + this._proxyAddressTextbox, + this._proxyPortLabel, + this._proxyPortTextbox, + this._proxyUsernameLabel, + this._proxyUsernameTextbox, + this._proxyPasswordLabel, + this._proxyPasswordTextbox, + ], + true + ); // DISABLE + + this._proxyAddressTextbox.value = ""; + this._proxyPortTextbox.value = ""; + this._proxyUsernameTextbox.value = ""; + this._proxyPasswordTextbox.value = ""; + break; + } + case TorProxyType.SOCKS4: { + this._setElementsDisabled( + [ + this._proxyAddressLabel, + this._proxyAddressTextbox, + this._proxyPortLabel, + this._proxyPortTextbox, + ], + false + ); // ENABLE + this._setElementsDisabled( + [ + this._proxyUsernameLabel, + this._proxyUsernameTextbox, + this._proxyPasswordLabel, + this._proxyPasswordTextbox, + ], + true + ); // DISABLE + + this._proxyUsernameTextbox.value = ""; + this._proxyPasswordTextbox.value = ""; + break; + } + case TorProxyType.SOCKS5: + case TorProxyType.HTTPS: { + this._setElementsDisabled( + [ + this._proxyAddressLabel, + this._proxyAddressTextbox, + this._proxyPortLabel, + this._proxyPortTextbox, + this._proxyUsernameLabel, + this._proxyUsernameTextbox, + this._proxyPasswordLabel, + this._proxyPasswordTextbox, + ], + false + ); // ENABLE + break; + } + } + return this; + }, + + // pushes proxy settings from UI to tor + onUpdateProxySettings() { + const proxyType = this._useProxyCheckbox.checked + ? this._proxyTypeMenulist.value + : TorProxyType.NONE; + const addressString = this._proxyAddressTextbox.value; + const portString = this._proxyPortTextbox.value; + const usernameString = this._proxyUsernameTextbox.value; + const passwordString = this._proxyPasswordTextbox.value; + + let proxySettings = null; + + switch (proxyType) { + case TorProxyType.NONE: + proxySettings = makeTorProxySettingsNone(); + break; + case TorProxyType.SOCKS4: + proxySettings = makeTorProxySettingsSocks4( + addressString, + parsePort(portString) + ); + break; + case TorProxyType.SOCKS5: + proxySettings = makeTorProxySettingsSocks5( + addressString, + parsePort(portString), + usernameString, + passwordString + ); + break; + case TorProxyType.HTTPS: + proxySettings = makeTorProxySettingsHTTPS( + addressString, + parsePort(portString), + usernameString, + passwordString + ); + break; + } + + proxySettings.writeSettings(); + this._proxySettings = proxySettings; + return this; + }, + + // callback when firewall proxy is toggled + onToggleFirewall(enabled) { + this._useFirewallCheckbox.checked = enabled; + let disabled = !enabled; + + this._setElementsDisabled( + [this._allowedPortsLabel, this._allowedPortsTextbox], + disabled + ); + + return this; + }, + + // pushes firewall settings from UI to tor + onUpdateFirewallSettings() { + let portListString = this._useFirewallCheckbox.checked + ? this._allowedPortsTextbox.value + : ""; + let firewallSettings = null; + + if (portListString) { + firewallSettings = makeTorFirewallSettingsCustom( + parsePortList(portListString) + ); + } else { + firewallSettings = makeTorFirewallSettingsNone(); + } + + firewallSettings.writeSettings(); + this._firewallSettings = firewallSettings; + return this; + }, + + onViewTorLogs() { + let torLogDialog = new TorLogDialog(); + torLogDialog.openDialog(gSubDialog); + }, + }; + return retval; +})(); /* gTorPane */ diff --git a/browser/components/torpreferences/content/torPane.xhtml b/browser/components/torpreferences/content/torPane.xhtml new file mode 100644 index 000000000000..3c966b2b3726 --- /dev/null +++ b/browser/components/torpreferences/content/torPane.xhtml @@ -0,0 +1,123 @@ +<!-- Tor panel --> + +<script type="application/javascript" + src="chrome://browser/content/torpreferences/torPane.js"/> +<html:template id="template-paneTor"> +<hbox id="torPreferencesCategory" + class="subcategory" + data-category="paneTor" + hidden="true"> + <html:h1 id="torPreferences-header"/> +</hbox> + +<groupbox data-category="paneTor" + hidden="true"> + <description flex="1"> + <html:span id="torPreferences-description" class="tail-with-learn-more"/> + <label id="torPreferences-learnMore" class="learnMore text-link" is="text-link"/> + </description> +</groupbox> + +<!-- Bridges --> +<groupbox id="torPreferences-bridges-group" + data-category="paneTor" + hidden="true"> + <html:h2 id="torPreferences-bridges-header"/> + <description flex="1"> + <html:span id="torPreferences-bridges-description" class="tail-with-learn-more"/> + <label id="torPreferences-bridges-learnMore" class="learnMore text-link" is="text-link"/> + </description> + <checkbox id="torPreferences-bridges-toggle"/> + <radiogroup id="torPreferences-bridges-bridgeSelection"> + <hbox class="indent"> + <radio id="torPreferences-bridges-radioBuiltin"/> + <spacer flex="1"/> + <menulist id="torPreferences-bridges-builtinList" class="torMarginFix"> + <menupopup/> + </menulist> + </hbox> + <vbox class="indent"> + <hbox> + <radio id="torPreferences-bridges-radioRequestBridge"/> + <space flex="1"/> + <button id="torPreferences-bridges-buttonRequestBridge" class="torMarginFix"/> + </hbox> + <html:textarea + id="torPreferences-bridges-textareaRequestBridge" + class="indent torMarginFix" + multiline="true" + rows="3" + readonly="true"/> + </vbox> + <hbox class="indent" flex="1"> + <vbox flex="1"> + <radio id="torPreferences-bridges-radioProvideBridge"/> + <description id="torPreferences-bridges-descriptionProvideBridge" class="indent"/> + <html:textarea + id="torPreferences-bridges-textareaProvideBridge" + class="indent torMarginFix" + multiline="true" + rows="3"/> + </vbox> + </hbox> + </radiogroup> +</groupbox> + +<!-- Advanced --> +<groupbox id="torPreferences-advanced-group" + data-category="paneTor" + hidden="true"> + <html:h2 id="torPreferences-advanced-header"/> + <description flex="1"> + <html:span id="torPreferences-advanced-description" class="tail-with-learn-more"/> + <label id="torPreferences-advanced-learnMore" class="learnMore text-link" is="text-link" style="display:none"/> + </description> + <box id="torPreferences-advanced-grid"> + <!-- Local Proxy --> + <hbox class="torPreferences-advanced-checkbox-container"> + <checkbox id="torPreferences-advanced-toggleProxy"/> + </hbox> + <hbox class="indent" align="center"> + <label id="torPreferences-localProxy-type"/> + </hbox> + <hbox align="center"> + <spacer flex="1"/> + <menulist id="torPreferences-localProxy-builtinList" class="torMarginFix"> + <menupopup/> + </menulist> + </hbox> + <hbox class="indent" align="center"> + <label id="torPreferences-localProxy-address"/> + </hbox> + <hbox align="center"> + <html:input id="torPreferences-localProxy-textboxAddress" type="text" class="torMarginFix"/> + <label id="torPreferences-localProxy-port"/> + <!-- proxy-port-input class style pulled from preferences.css and used in the vanilla proxy setup menu --> + <html:input id="torPreferences-localProxy-textboxPort" class="proxy-port-input torMarginFix" hidespinbuttons="true" type="number" min="0" max="65535" maxlength="5"/> + </hbox> + <hbox class="indent" align="center"> + <label id="torPreferences-localProxy-username"/> + </hbox> + <hbox align="center"> + <html:input id="torPreferences-localProxy-textboxUsername" type="text" class="torMarginFix"/> + <label id="torPreferences-localProxy-password"/> + <html:input id="torPreferences-localProxy-textboxPassword" class="torMarginFix" type="password"/> + </hbox> + <!-- Firewall --> + <hbox class="torPreferences-advanced-checkbox-container"> + <checkbox id="torPreferences-advanced-toggleFirewall"/> + </hbox> + <hbox class="indent" align="center"> + <label id="torPreferences-advanced-allowedPorts"/> + </hbox> + <hbox align="center"> + <html:input id="torPreferences-advanced-textboxAllowedPorts" type="text" class="torMarginFix" value="80,443"/> + </hbox> + </box> + <hbox id="torPreferences-torDaemon-hbox" align="center"> + <label id="torPreferences-torLogs"/> + <spacer flex="1"/> + <button id="torPreferences-buttonTorLogs" class="torMarginFix"/> + </hbox> +</groupbox> +</html:template> \ No newline at end of file diff --git a/browser/components/torpreferences/content/torPreferences.css b/browser/components/torpreferences/content/torPreferences.css new file mode 100644 index 000000000000..4dac2c457823 --- /dev/null +++ b/browser/components/torpreferences/content/torPreferences.css @@ -0,0 +1,77 @@ +#category-tor > .category-icon { + list-style-image: url("chrome://browser/content/torpreferences/torPreferencesIcon.svg"); +} + +#torPreferences-advanced-grid { + display: grid; + grid-template-columns: auto 1fr; +} + +.torPreferences-advanced-checkbox-container { + grid-column: 1 / 3; +} + +#torPreferences-localProxy-textboxAddress, +#torPreferences-localProxy-textboxUsername, +#torPreferences-localProxy-textboxPassword, +#torPreferences-advanced-textboxAllowedPorts { + -moz-box-flex: 1; +} + +hbox#torPreferences-torDaemon-hbox { + margin-top: 20px; +} + +description#torPreferences-requestBridge-description { + /*margin-bottom: 1em;*/ + min-height: 2em; +} + +image#torPreferences-requestBridge-captchaImage { + margin: 1em; + min-height: 125px; +} + +button#torPreferences-requestBridge-refreshCaptchaButton { + min-width: initial; +} + +dialog#torPreferences-requestBridge-dialog > hbox { + margin-bottom: 1em; +} + +/* + Various elements that really should be lining up don't because they have inconsistent margins +*/ +.torMarginFix { + margin-left : 4px; + margin-right : 4px; +} + +/* + This hbox is hidden by css here by default so that the + xul dialog allocates enough screen space for the error message + element, otherwise it gets cut off since dialog's overflow is hidden +*/ +hbox#torPreferences-requestBridge-incorrectCaptchaHbox { + visibility: hidden; +} + +image#torPreferences-requestBridge-errorIcon { + list-style-image: url("chrome://browser/skin/warning.svg"); +} + +groupbox#torPreferences-bridges-group textarea { + white-space: pre; + overflow: auto; +} + +textarea#torPreferences-torDialog-textarea { + -moz-box-flex: 1; + font-family: monospace; + font-size: 0.8em; + white-space: pre; + overflow: auto; + /* 10 lines */ + min-height: 20em; +} \ No newline at end of file diff --git a/browser/components/torpreferences/content/torPreferencesIcon.svg b/browser/components/torpreferences/content/torPreferencesIcon.svg new file mode 100644 index 000000000000..d7895f1107c5 --- /dev/null +++ b/browser/components/torpreferences/content/torPreferencesIcon.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <g fill="context-fill" fill-opacity="context-fill-opacity" fill-rule="nonzero"> + <path d="M12.0246161,21.8174863 L12.0246161,20.3628098 C16.6324777,20.3495038 20.3634751,16.6108555 20.3634751,11.9996673 C20.3634751,7.38881189 16.6324777,3.65016355 12.0246161,3.63685757 L12.0246161,2.18218107 C17.4358264,2.1958197 21.8178189,6.58546322 21.8178189,11.9996673 C21.8178189,17.4142042 17.4358264,21.8041803 12.0246161,21.8174863 L12.0246161,21.8174863 Z M12.0246161,16.7259522 C14.623607,16.7123136 16.7272828,14.6023175 16.7272828,11.9996673 C16.7272828,9.39734991 14.623607,7.28735377 12.0246161,7.27371516 L12.0246161,5.81937131 C15.4272884,5.8326773 18.1819593,8.59400123 18.1819593,11.9996673 C18.1819593,15.4056661 15.4272884,18.1669901 12.0246161,18.1802961 L12.0246161,16.7259522 Z M12.0246161,9.45556355 C13.4187503,9.46886953 14.5454344,10.6022066 14.5454344,11.9996673 C14.5454344,13.3974608 13.4187503,14.5307978 12.0246161,14.5441038 L12.0246161,9.45556355 Z M0,11.9996673 C0,18.6273771 5.37229031,24 12,24 C18.6273771,24 24,18.6273771 24,11.9996673 C24,5.37229031 18.6273771,0 12,0 C5.37229031,0 0,5.37229031 0,11.9996673 Z"/> + </g> +</svg> \ No newline at end of file diff --git a/browser/components/torpreferences/content/torProxySettings.jsm b/browser/components/torpreferences/content/torProxySettings.jsm new file mode 100644 index 000000000000..98bb5e8d5cbf --- /dev/null +++ b/browser/components/torpreferences/content/torProxySettings.jsm @@ -0,0 +1,245 @@ +"use strict"; + +var EXPORTED_SYMBOLS = [ + "TorProxyType", + "TorProxySettings", + "makeTorProxySettingsNone", + "makeTorProxySettingsSocks4", + "makeTorProxySettingsSocks5", + "makeTorProxySettingsHTTPS", +]; + +const { TorProtocolService } = ChromeUtils.import( + "resource:///modules/TorProtocolService.jsm" +); +const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); +const { parseAddrPort, parseUsernamePassword } = ChromeUtils.import( + "chrome://browser/content/torpreferences/parseFunctions.jsm" +); + +const TorProxyType = { + NONE: "NONE", + SOCKS4: "SOCKS4", + SOCKS5: "SOCKS5", + HTTPS: "HTTPS", +}; + +class TorProxySettings { + constructor() { + this._proxyType = TorProxyType.NONE; + this._proxyAddress = undefined; + this._proxyPort = undefined; + this._proxyUsername = undefined; + this._proxyPassword = undefined; + } + + get type() { + return this._proxyType; + } + get address() { + return this._proxyAddress; + } + get port() { + return this._proxyPort; + } + get username() { + return this._proxyUsername; + } + get password() { + return this._proxyPassword; + } + get proxyURI() { + switch (this._proxyType) { + case TorProxyType.SOCKS4: + return `socks4a://${this._proxyAddress}:${this._proxyPort}`; + case TorProxyType.SOCKS5: + if (this._proxyUsername) { + return `socks5://${this._proxyUsername}:${this._proxyPassword}@${ + this._proxyAddress + }:${this._proxyPort}`; + } + return `socks5://${this._proxyAddress}:${this._proxyPort}`; + case TorProxyType.HTTPS: + if (this._proxyUsername) { + return `http://${this._proxyUsername}:${this._proxyPassword}@${ + this._proxyAddress + }:${this._proxyPort}`; + } + return `http://${this._proxyAddress}:${this._proxyPort}`; + } + return undefined; + } + + // attempts to read proxy settings from Tor daemon + readSettings() { + // SOCKS4 + { + let addressPort = TorProtocolService.readStringSetting( + TorStrings.configKeys.socks4Proxy + ); + if (addressPort) { + // address+port + let [proxyAddress, proxyPort] = parseAddrPort(addressPort); + + this._proxyType = TorProxyType.SOCKS4; + this._proxyAddress = proxyAddress; + this._proxyPort = proxyPort; + this._proxyUsername = ""; + this._proxyPassword = ""; + + return; + } + } + + // SOCKS5 + { + let addressPort = TorProtocolService.readStringSetting( + TorStrings.configKeys.socks5Proxy + ); + + if (addressPort) { + // address+port + let [proxyAddress, proxyPort] = parseAddrPort(addressPort); + // username + let proxyUsername = TorProtocolService.readStringSetting( + TorStrings.configKeys.socks5ProxyUsername + ); + // password + let proxyPassword = TorProtocolService.readStringSetting( + TorStrings.configKeys.socks5ProxyPassword + ); + + this._proxyType = TorProxyType.SOCKS5; + this._proxyAddress = proxyAddress; + this._proxyPort = proxyPort; + this._proxyUsername = proxyUsername; + this._proxyPassword = proxyPassword; + + return; + } + } + + // HTTP + { + let addressPort = TorProtocolService.readStringSetting( + TorStrings.configKeys.httpsProxy + ); + + if (addressPort) { + // address+port + let [proxyAddress, proxyPort] = parseAddrPort(addressPort); + + // username:password + let proxyAuthenticator = TorProtocolService.readStringSetting( + TorStrings.configKeys.httpsProxyAuthenticator + ); + + let [proxyUsername, proxyPassword] = ["", ""]; + if (proxyAuthenticator) { + [proxyUsername, proxyPassword] = parseUsernamePassword( + proxyAuthenticator + ); + } + + this._proxyType = TorProxyType.HTTPS; + this._proxyAddress = proxyAddress; + this._proxyPort = proxyPort; + this._proxyUsername = proxyUsername; + this._proxyPassword = proxyPassword; + } + } + // no proxy settings + } /* TorProxySettings::ReadFromTor() */ + + // attempts to write proxy settings to Tor daemon + // throws on error + writeSettings() { + let settingsObject = new Map(); + + // init proxy related settings to null so Tor daemon resets them + settingsObject.set(TorStrings.configKeys.socks4Proxy, null); + settingsObject.set(TorStrings.configKeys.socks5Proxy, null); + settingsObject.set(TorStrings.configKeys.socks5ProxyUsername, null); + settingsObject.set(TorStrings.configKeys.socks5ProxyPassword, null); + settingsObject.set(TorStrings.configKeys.httpsProxy, null); + settingsObject.set(TorStrings.configKeys.httpsProxyAuthenticator, null); + + switch (this._proxyType) { + case TorProxyType.SOCKS4: + settingsObject.set( + TorStrings.configKeys.socks4Proxy, + `${this._proxyAddress}:${this._proxyPort}` + ); + break; + case TorProxyType.SOCKS5: + settingsObject.set( + TorStrings.configKeys.socks5Proxy, + `${this._proxyAddress}:${this._proxyPort}` + ); + settingsObject.set( + TorStrings.configKeys.socks5ProxyUsername, + this._proxyUsername + ); + settingsObject.set( + TorStrings.configKeys.socks5ProxyPassword, + this._proxyPassword + ); + break; + case TorProxyType.HTTPS: + settingsObject.set( + TorStrings.configKeys.httpsProxy, + `${this._proxyAddress}:${this._proxyPort}` + ); + settingsObject.set( + TorStrings.configKeys.httpsProxyAuthenticator, + `${this._proxyUsername}:${this._proxyPassword}` + ); + break; + } + + TorProtocolService.writeSettings(settingsObject); + } /* TorProxySettings::WriteToTor() */ +} + +// factory methods for our various supported proxies +function makeTorProxySettingsNone() { + return new TorProxySettings(); +} + +function makeTorProxySettingsSocks4(aProxyAddress, aProxyPort) { + let retval = new TorProxySettings(); + retval._proxyType = TorProxyType.SOCKS4; + retval._proxyAddress = aProxyAddress; + retval._proxyPort = aProxyPort; + return retval; +} + +function makeTorProxySettingsSocks5( + aProxyAddress, + aProxyPort, + aProxyUsername, + aProxyPassword +) { + let retval = new TorProxySettings(); + retval._proxyType = TorProxyType.SOCKS5; + retval._proxyAddress = aProxyAddress; + retval._proxyPort = aProxyPort; + retval._proxyUsername = aProxyUsername; + retval._proxyPassword = aProxyPassword; + return retval; +} + +function makeTorProxySettingsHTTPS( + aProxyAddress, + aProxyPort, + aProxyUsername, + aProxyPassword +) { + let retval = new TorProxySettings(); + retval._proxyType = TorProxyType.HTTPS; + retval._proxyAddress = aProxyAddress; + retval._proxyPort = aProxyPort; + retval._proxyUsername = aProxyUsername; + retval._proxyPassword = aProxyPassword; + return retval; +} diff --git a/browser/components/torpreferences/jar.mn b/browser/components/torpreferences/jar.mn new file mode 100644 index 000000000000..857bc9ee3eac --- /dev/null +++ b/browser/components/torpreferences/jar.mn @@ -0,0 +1,14 @@ +browser.jar: + content/browser/torpreferences/parseFunctions.jsm (content/parseFunctions.jsm) + content/browser/torpreferences/requestBridgeDialog.xhtml (content/requestBridgeDialog.xhtml) + content/browser/torpreferences/requestBridgeDialog.jsm (content/requestBridgeDialog.jsm) + content/browser/torpreferences/torBridgeSettings.jsm (content/torBridgeSettings.jsm) + content/browser/torpreferences/torCategory.inc.xhtml (content/torCategory.inc.xhtml) + content/browser/torpreferences/torFirewallSettings.jsm (content/torFirewallSettings.jsm) + content/browser/torpreferences/torLogDialog.jsm (content/torLogDialog.jsm) + content/browser/torpreferences/torLogDialog.xhtml (content/torLogDialog.xhtml) + content/browser/torpreferences/torPane.js (content/torPane.js) + content/browser/torpreferences/torPane.xhtml (content/torPane.xhtml) + content/browser/torpreferences/torPreferences.css (content/torPreferences.css) + content/browser/torpreferences/torPreferencesIcon.svg (content/torPreferencesIcon.svg) + content/browser/torpreferences/torProxySettings.jsm (content/torProxySettings.jsm) diff --git a/browser/components/torpreferences/moz.build b/browser/components/torpreferences/moz.build new file mode 100644 index 000000000000..2661ad7cb9f3 --- /dev/null +++ b/browser/components/torpreferences/moz.build @@ -0,0 +1 @@ +JAR_MANIFESTS += ["jar.mn"] diff --git a/browser/modules/BridgeDB.jsm b/browser/modules/BridgeDB.jsm new file mode 100644 index 000000000000..2caa26b4e2e0 --- /dev/null +++ b/browser/modules/BridgeDB.jsm @@ -0,0 +1,110 @@ +"use strict"; + +var EXPORTED_SYMBOLS = ["BridgeDB"]; + +const { TorLauncherBridgeDB } = ChromeUtils.import( + "resource://torlauncher/modules/tl-bridgedb.jsm" +); +const { TorProtocolService } = ChromeUtils.import( + "resource:///modules/TorProtocolService.jsm" +); +const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); + +var BridgeDB = { + _moatRequestor: null, + _currentCaptchaInfo: null, + _bridges: null, + + get currentCaptchaImage() { + if (this._currentCaptchaInfo) { + return this._currentCaptchaInfo.captchaImage; + } + return null; + }, + + get currentBridges() { + return this._bridges; + }, + + submitCaptchaGuess(aCaptchaSolution) { + if (this._moatRequestor && this._currentCaptchaInfo) { + return this._moatRequestor + .finishFetch( + this._currentCaptchaInfo.transport, + this._currentCaptchaInfo.challenge, + aCaptchaSolution + ) + .then(aBridgeInfo => { + this._moatRequestor.close(); + this._moatRequestor = null; + this._currentCaptchaInfo = null; + this._bridges = aBridgeInfo.bridges; + // array of bridge strings + return this._bridges; + }); + } + + return new Promise((aResponse, aReject) => { + aReject(new Error("Invalid _moatRequestor or _currentCaptchaInfo")); + }); + }, + + requestNewCaptchaImage(aProxyURI) { + // close and clear out existing state on captcha request + this.close(); + + let transportPlugins = TorProtocolService.readStringArraySetting( + TorStrings.configKeys.clientTransportPlugin + ); + + let meekClientPath; + let meekTransport; // We support both "meek" and "meek_lite". + let meekClientArgs; + // TODO: shouldn't this early out once meek settings are found? + for (const line of transportPlugins) { + // Parse each ClientTransportPlugin line and look for the meek or + // meek_lite transport. This code works a lot like the Tor daemon's + // parse_transport_line() function. + let tokens = line.split(" "); + if (tokens.length > 2 && tokens[1] == "exec") { + let transportArray = tokens[0].split(",").map(aStr => aStr.trim()); + let transport = transportArray.find( + aTransport => aTransport === "meek" + ); + if (!transport) { + transport = transportArray.find( + aTransport => aTransport === "meek_lite" + ); + } + if (transport) { + meekTransport = transport; + meekClientPath = tokens[2]; + meekClientArgs = tokens.slice(3); + } + } + } + + this._moatRequestor = TorLauncherBridgeDB.createMoatRequestor(); + + return this._moatRequestor + .init(aProxyURI, meekTransport, meekClientPath, meekClientArgs) + .then(() => { + // TODO: get this from TorLauncherUtil + let bridgeType = "obfs4"; + return this._moatRequestor.fetchBridges([bridgeType]); + }) + .then(aCaptchaInfo => { + // cache off the current captcha info as the challenge is needed for response + this._currentCaptchaInfo = aCaptchaInfo; + return aCaptchaInfo.captchaImage; + }); + }, + + close() { + if (this._moatRequestor) { + this._moatRequestor.close(); + this._moatRequestor = null; + } + this._currentCaptchaInfo = null; + }, +}; diff --git a/browser/modules/TorProtocolService.jsm b/browser/modules/TorProtocolService.jsm new file mode 100644 index 000000000000..b4e6ed9a3253 --- /dev/null +++ b/browser/modules/TorProtocolService.jsm @@ -0,0 +1,212 @@ +"use strict"; + +var EXPORTED_SYMBOLS = ["TorProtocolService"]; + +const { TorLauncherUtil } = ChromeUtils.import( + "resource://torlauncher/modules/tl-util.jsm" +); + +var TorProtocolService = { + _tlps: Cc["@torproject.org/torlauncher-protocol-service;1"].getService( + Ci.nsISupports + ).wrappedJSObject, + + // maintain a map of tor settings set by Tor Browser so that we don't + // repeatedly set the same key/values over and over + // this map contains string keys to primitive or array values + _settingsCache: new Map(), + + _typeof(aValue) { + switch (typeof aValue) { + case "boolean": + return "boolean"; + case "string": + return "string"; + case "object": + if (aValue == null) { + return "null"; + } else if (Array.isArray(aValue)) { + return "array"; + } + return "object"; + } + return "unknown"; + }, + + _assertValidSettingKey(aSetting) { + // ensure the 'key' is a string + if (typeof aSetting != "string") { + throw new Error( + `Expected setting of type string but received ${typeof aSetting}` + ); + } + }, + + _assertValidSetting(aSetting, aValue) { + this._assertValidSettingKey(aSetting); + + const valueType = this._typeof(aValue); + switch (valueType) { + case "boolean": + case "string": + case "null": + return; + case "array": + for (const element of aValue) { + if (typeof element != "string") { + throw new Error( + `Setting '${aSetting}' array contains value of invalid type '${typeof element}'` + ); + } + } + return; + default: + throw new Error( + `Invalid object type received for setting '${aSetting}'` + ); + } + }, + + // takes a Map containing tor settings + // throws on error + writeSettings(aSettingsObj) { + // only write settings that have changed + let newSettings = new Map(); + for (const [setting, value] of aSettingsObj) { + let saveSetting = false; + + // make sure we have valid data here + this._assertValidSetting(setting, value); + + if (!this._settingsCache.has(setting)) { + // no cached setting, so write + saveSetting = true; + } else { + const cachedValue = this._settingsCache.get(setting); + if (value != cachedValue) { + // compare arrays member-wise + if (Array.isArray(value) && Array.isArray(cachedValue)) { + if (value.length != cachedValue.length) { + saveSetting = true; + } else { + const arrayLength = value.length; + for (let i = 0; i < arrayLength; ++i) { + if (value[i] != cachedValue[i]) { + saveSetting = true; + break; + } + } + } + } else { + // some other different values + saveSetting = true; + } + } + } + + if (saveSetting) { + newSettings.set(setting, value); + } + } + + // only write if new setting to save + if (newSettings.size > 0) { + // convert settingsObject map to js object for torlauncher-protocol-service + let settingsObject = {}; + for (const [setting, value] of newSettings) { + settingsObject[setting] = value; + } + + let errorObject = {}; + if (!this._tlps.TorSetConfWithReply(settingsObject, errorObject)) { + throw new Error(errorObject.details); + } + + // save settings to cache after successfully writing to Tor + for (const [setting, value] of newSettings) { + this._settingsCache.set(setting, value); + } + } + }, + + _readSetting(aSetting) { + this._assertValidSettingKey(aSetting); + let reply = this._tlps.TorGetConf(aSetting); + if (this._tlps.TorCommandSucceeded(reply)) { + return reply.lineArray; + } + throw new Error(reply.lineArray.join("\n")); + }, + + _readBoolSetting(aSetting) { + let lineArray = this._readSetting(aSetting); + if (lineArray.length != 1) { + throw new Error( + `Expected an array with length 1 but received array of length ${ + lineArray.length + }` + ); + } + + let retval = lineArray[0]; + switch (retval) { + case "0": + return false; + case "1": + return true; + default: + throw new Error(`Expected boolean (1 or 0) but received '${retval}'`); + } + }, + + _readStringSetting(aSetting) { + let lineArray = this._readSetting(aSetting); + if (lineArray.length != 1) { + throw new Error( + `Expected an array with length 1 but received array of length ${ + lineArray.length + }` + ); + } + return lineArray[0]; + }, + + _readStringArraySetting(aSetting) { + let lineArray = this._readSetting(aSetting); + return lineArray; + }, + + readBoolSetting(aSetting) { + let value = this._readBoolSetting(aSetting); + this._settingsCache.set(aSetting, value); + return value; + }, + + readStringSetting(aSetting) { + let value = this._readStringSetting(aSetting); + this._settingsCache.set(aSetting, value); + return value; + }, + + readStringArraySetting(aSetting) { + let value = this._readStringArraySetting(aSetting); + this._settingsCache.set(aSetting, value); + return value; + }, + + // writes current tor settings to disk + flushSettings() { + this._tlps.TorSendCommand("SAVECONF"); + }, + + getLog() { + let countObj = { value: 0 }; + let torLog = this._tlps.TorGetLog(countObj); + return torLog; + }, + + // true if we launched and control tor, false if using system tor + get ownsTorDaemon() { + return TorLauncherUtil.shouldStartAndOwnTor; + }, +}; diff --git a/browser/modules/moz.build b/browser/modules/moz.build index 25d2d197ee90..21a05b5ab738 100644 --- a/browser/modules/moz.build +++ b/browser/modules/moz.build @@ -128,6 +128,7 @@ EXTRA_JS_MODULES += [ "AboutNewTab.jsm", "AppUpdater.jsm", "AsyncTabSwitcher.jsm", + "BridgeDB.jsm", "BrowserUsageTelemetry.jsm", "BrowserWindowTracker.jsm", "ContentCrashHandlers.jsm", @@ -151,6 +152,7 @@ EXTRA_JS_MODULES += [ "TabsList.jsm", "TabUnloader.jsm", "ThemeVariableMap.jsm", + "TorProtocolService.jsm", "TorStrings.jsm", "TransientPrefs.jsm", "webrtcUI.jsm",
1 0
0 0
[tor-browser/tor-browser-86.0b5-10.5-1] Bug 28044: Integrate Tor Launcher into tor-browser
by gk@torproject.org 04 Feb '21

04 Feb '21
commit 6b21bab034f585ee747506545989af3ddf7ec8a3 Author: Kathy Brade <brade(a)pearlcrescent.com> Date: Tue Feb 26 10:07:17 2019 -0500 Bug 28044: Integrate Tor Launcher into tor-browser Build and package Tor Launcher as part of the browser (similar to how pdfjs is handled). If a Tor Launcher extension is present in the user's profile, it is removed. --- browser/extensions/moz.build | 3 +++ browser/installer/package-manifest.in | 5 +++++ toolkit/mozapps/extensions/internal/XPIProvider.jsm | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/browser/extensions/moz.build b/browser/extensions/moz.build index 0eb3c53e76c2..df98fa94f629 100644 --- a/browser/extensions/moz.build +++ b/browser/extensions/moz.build @@ -5,3 +5,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DIRS += ["doh-rollout", "formautofill", "screenshots", "webcompat", "report-site-issue"] + +if not CONFIG["TOR_BROWSER_DISABLE_TOR_LAUNCHER"]: + DIRS += ["tor-launcher"] diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index e0c28c0a075a..ef9d14316a3f 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -252,6 +252,11 @@ @RESPATH@/browser/chrome/browser.manifest @RESPATH@/chrome/pdfjs.manifest @RESPATH@/chrome/pdfjs/* +#ifndef TOR_BROWSER_DISABLE_TOR_LAUNCHER +@RESPATH@/browser/chrome/torlauncher.manifest +@RESPATH@/browser/chrome/torlauncher/* +@RESPATH@/browser/@PREF_DIR@/torlauncher-prefs.js +#endif @RESPATH@/chrome/toolkit@JAREXT@ @RESPATH@/chrome/toolkit.manifest @RESPATH@/chrome/recording.manifest diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 841137abf805..e4d7f96819e8 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -1479,6 +1479,16 @@ var XPIStates = { for (let [id, file] of loc.readAddons()) { knownIds.delete(id); + // Since it is now part of the browser, uninstall the Tor Launcher + // extension. This will remove the Tor Launcher .xpi from user + // profiles on macOS. + if (id === "tor-launcher(a)torproject.org") { + logger.debug("Uninstalling the Tor Launcher extension."); + loc.installer.uninstallAddon(id); + changed = true; + continue; + } + let xpiState = loc.get(id); if (!xpiState) { // If the location is not supported for sideloading, skip new
1 0
0 0
[tor-browser/tor-browser-86.0b5-10.5-1] Bug 40069: Add helpers for message passing with extensions
by gk@torproject.org 04 Feb '21

04 Feb '21
commit ae4cc7484fdbee4a1d8cd7b9249e19b55294ef87 Author: Alex Catarineu <acat(a)torproject.org> Date: Sun Aug 2 19:12:25 2020 +0200 Bug 40069: Add helpers for message passing with extensions --- toolkit/components/extensions/ExtensionParent.jsm | 47 +++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/toolkit/components/extensions/ExtensionParent.jsm b/toolkit/components/extensions/ExtensionParent.jsm index e12ee5478403..a662b62c277f 100644 --- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -264,6 +264,8 @@ const ProxyMessenger = { /** @type Map<number, ParentPort> */ ports: new Map(), + _torRuntimeMessageListeners: [], + init() { this.conduit = new BroadcastConduit(ProxyMessenger, { id: "ProxyMessenger", @@ -326,6 +328,10 @@ const ProxyMessenger = { }, async recvRuntimeMessage(arg, { sender }) { + // We need to listen to some extension messages in Tor Browser + for (const listener of this._torRuntimeMessageListeners) { + listener(arg); + } arg.firstResponse = true; let kind = await this.normalizeArgs(arg, sender); let result = await this.conduit.castRuntimeMessage(kind, arg); @@ -1880,6 +1886,45 @@ for (let name of StartupCache.STORE_NAMES) { StartupCache[name] = new CacheStore(name); } +async function torSendExtensionMessage(extensionId, message) { + // This should broadcast the message to all children "conduits" + // listening for a "RuntimeMessage". Those children conduits + // will either be extension background pages or other extension + // pages listening to browser.runtime.onMessage. + const result = await ProxyMessenger.conduit.castRuntimeMessage("messenger", { + extensionId, + holder: new StructuredCloneHolder(message), + firstResponse: true, + sender: { + id: extensionId, + envType: "addon_child", + }, + }); + return result + ? result.value + : Promise.reject({ message: ERROR_NO_RECEIVERS }); +} + +async function torWaitForExtensionMessage(extensionId, checker) { + return new Promise(resolve => { + const msgListener = msg => { + try { + if (msg && msg.extensionId === extensionId) { + const deserialized = msg.holder.deserialize({}); + if (checker(deserialized)) { + const idx = ProxyMessenger._torRuntimeMessageListeners.indexOf( + msgListener + ); + ProxyMessenger._torRuntimeMessageListeners.splice(idx, 1); + resolve(deserialized); + } + } + } catch (e) {} + }; + ProxyMessenger._torRuntimeMessageListeners.push(msgListener); + }); +} + var ExtensionParent = { GlobalManager, HiddenExtensionPage, @@ -1891,6 +1936,8 @@ var ExtensionParent = { promiseExtensionViewLoaded, watchExtensionProxyContextLoad, DebugUtils, + torSendExtensionMessage, + torWaitForExtensionMessage, }; // browserPaintedPromise and browserStartupPromise are promises that
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 1179
  • 1180
  • 1181
  • 1182
  • 1183
  • 1184
  • 1185
  • ...
  • 2064
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.