tor-commits
Threads by month
- ----- 2026 -----
- 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
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 215210 discussions
[Git][tpo/applications/tor-browser][tor-browser-150.0a1-16.0-2] TB 44615: Remove review prompt
by Dan Ballard (@dan) 30 Apr '26
by Dan Ballard (@dan) 30 Apr '26
30 Apr '26
Dan Ballard pushed to branch tor-browser-150.0a1-16.0-2 at The Tor Project / Applications / Tor Browser
Commits:
cadcb5ec by clairehurst at 2026-04-30T09:56:22-07:00
TB 44615: Remove review prompt
- - - - -
9 changed files:
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/PlayStoreReviewPromptController.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/reviewprompt/ReviewPromptMiddleware.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/reviewprompt/ReviewPromptReducer.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt
- mobile/android/fenix/app/src/main/res/navigation/nav_graph.xml
- mobile/android/fenix/app/src/main/res/xml/preferences.xml
- mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/PlayStoreReviewPromptControllerTest.kt
- mobile/android/fenix/app/src/test/java/org/mozilla/fenix/reviewprompt/ShowPlayStoreReviewPromptTest.kt
Changes:
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt
=====================================
@@ -90,6 +90,12 @@ import org.mozilla.fenix.utils.isLargeScreenSize
import org.mozilla.fenix.wifi.WifiConnectionMonitor
import java.util.concurrent.TimeUnit
+import android.app.Activity
+import com.google.android.gms.tasks.Task
+import com.google.android.gms.tasks.Tasks
+import com.google.android.play.core.review.ReviewInfo
+import com.google.android.play.core.review.ReviewManager
+
private const val AMO_COLLECTION_MAX_CACHE_AGE = 2 * 24 * 60L // Two days in minutes
/**
@@ -251,7 +257,7 @@ class Components(private val context: Context) {
val playStoreReviewPromptController by lazyMonitored {
PlayStoreReviewPromptController(
- manager = ReviewManagerFactory.create(context),
+ manager = NoopReviewManager(context),
numberOfAppLaunches = { settings.numberOfAppLaunches },
)
}
@@ -422,6 +428,19 @@ class Components(private val context: Context) {
val torController by lazyMonitored { TorControllerGV(context) }
}
+class NoopReviewManager(val value: Context) : ReviewManager {
+ override fun launchReviewFlow(
+ p0: Activity,
+ p1: ReviewInfo
+ ): Task<Void?> {
+ return Tasks.forResult(null)
+ }
+
+ override fun requestReviewFlow(): Task<ReviewInfo?> {
+ return Tasks.forResult(null)
+ }
+}
+
/**
* Returns the [Components] object from within a [Composable].
*/
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/PlayStoreReviewPromptController.kt
=====================================
@@ -47,80 +47,16 @@ class PlayStoreReviewPromptController(
onNotDisplayed: () -> Unit = {},
onError: () -> Unit = {},
) {
- logger.info("tryPromptReview in progress...")
- val reviewInfoTask = withContext(Dispatchers.IO) { manager.requestReviewFlow() }
-
- reviewInfoTask.addOnCompleteListener(activity) { task ->
- val result = if (task.isSuccessful) {
- logger.info("Review flow launched.")
- // Launch the in-app flow.
- manager.launchReviewFlow(activity, task.result)
-
- ReviewPromptAttemptResult.from(task.result.toString())
- } else {
- Error
- }
-
- when (result) {
- NotDisplayed -> {
- logger.warn("In-app review flow reported as not displayed, even though there was no error.")
-
- onNotDisplayed()
- }
-
- Error -> {
- val reviewErrorCode =
- (task.exception as? ReviewException)?.errorCode ?: ERROR_CODE_UNEXPECTED
- logger.warn("Failed to launch in-app review flow due to: $reviewErrorCode.")
-
- onError()
- }
-
- Displayed, Unknown -> {}
- }
-
- recordReviewPromptEvent(
- promptAttemptResult = result,
- numberOfAppLaunches = numberOfAppLaunches(),
- now = Date(),
- )
- }
-
- logger.info("tryPromptReview completed.")
+ logger.info("tryPromptReview has been successfully noop'ed.")
+ return
}
/**
* Try to launch the play store review flow.
*/
fun tryLaunchPlayStoreReview(activity: Activity) {
- logger.info("tryLaunchPlayStoreReview in progress...")
-
- try {
- logger.info("Navigating to Play store listing.")
- activity.startActivity(
- Intent(Intent.ACTION_VIEW, SupportUtils.RATE_APP_URL.toUri()),
- )
- } catch (e: ActivityNotFoundException) {
- // Device without the play store installed.
- // Opening the play store website.
-
- activity.applicationContext.components.useCases.fenixBrowserUseCases.loadUrlOrSearch(
- searchTermOrURL = SupportUtils.FENIX_PLAY_STORE_URL,
- newTab = true,
- )
-
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1997148
- (activity as? FragmentActivity)
- ?.supportFragmentManager
- ?.fragments
- ?.firstOrNull { it is NavHostFragment }
- ?.let { (it as NavHostFragment).navController }
- ?.openToBrowser()
-
- logger.warn("Failed to launch play store review flow due to: $e.")
- }
-
- logger.info("tryLaunchPlayStoreReview completed.")
+ logger.info("tryLaunchPlayStoreReview has been successfully noop'ed.")
+ return
}
companion object {
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/reviewprompt/ReviewPromptMiddleware.kt
=====================================
@@ -105,23 +105,7 @@ class ReviewPromptMiddleware(
return
}
- val shouldShowPrompt: Boolean = createJexlHelper().use { jexlHelper ->
- // Keep the legacy criteria around, but use the nimbus data and jexl to trigger.
- // Leaving the original if-else logic and early return for readability.
- if (!shouldUseNewTriggerCriteria()) {
- val legacyCriteriaSatisfied = buildTriggerLegacyCriteria(jexlHelper).all { it }
- return@use legacyCriteriaSatisfied
- }
-
- // Otherwise, we use the new criteria.
- val allMainCriteriaSatisfied = buildTriggerMainCriteria(jexlHelper).all { it }
- if (!allMainCriteriaSatisfied) {
- return@use false
- }
-
- val atLeastOneOfSubCriteriaSatisfied = buildTriggerSubCriteria(jexlHelper).any { it }
- return@use atLeastOneOfSubCriteriaSatisfied
- }
+ val shouldShowPrompt: Boolean = false
if (shouldShowPrompt) {
if (shouldShowCustomPrompt()) {
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/reviewprompt/ReviewPromptReducer.kt
=====================================
@@ -21,11 +21,6 @@ import org.mozilla.fenix.reviewprompt.ReviewPromptState.NotEligible
*/
internal object ReviewPromptReducer {
fun reduce(state: AppState, action: ReviewPromptAction): AppState {
- return when (action) {
- ShowPlayStorePrompt -> state.copy(reviewPrompt = Eligible(Type.PlayStore))
- ShowCustomReviewPrompt -> state.copy(reviewPrompt = Eligible(Type.Custom))
- DoNotShowReviewPrompt, ReviewPromptShown -> state.copy(reviewPrompt = NotEligible)
- CheckIfEligibleForReviewPrompt -> state
- }
+ return state.copy(reviewPrompt = NotEligible)
}
}
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt
=====================================
@@ -544,12 +544,6 @@ class SettingsFragment : PreferenceFragmentCompat(), SystemInsetsPaddedFragment,
SettingsFragmentDirections.actionSettingsFragmentToSyncDebugFragment()
}
- // About preferences
- resources.getString(R.string.pref_key_rate) -> {
- components.playStoreReviewPromptController.tryLaunchPlayStoreReview(requireActivity())
- null
- }
-
resources.getString(R.string.pref_key_about) -> {
SettingsFragmentDirections.actionSettingsFragmentToAboutFragment()
}
=====================================
mobile/android/fenix/app/src/main/res/navigation/nav_graph.xml
=====================================
@@ -214,8 +214,7 @@
app:popUpToInclusive="true"
app:destination="@+id/unlockPrivateTabsFragment"/>
<action
- android:id="@+id/action_global_customReviewPromptDialogFragment"
- app:destination="@id/customReviewPromptDialogFragment" />
+ android:id="@+id/action_global_customReviewPromptDialogFragment" />
<action
android:id="@+id/action_global_to_installedAddonDetailsFragment"
app:destination="@id/installedAddonDetailsFragment" />
=====================================
mobile/android/fenix/app/src/main/res/xml/preferences.xml
=====================================
@@ -289,10 +289,6 @@
android:title="@string/preferences_category_about"
app:iconSpaceReserved="false"
android:layout="@layout/preference_category_no_icon_style">
- <androidx.preference.Preference
- android:key="@string/pref_key_rate"
- app:iconSpaceReserved="false"
- android:title="@string/preferences_rate" />
<androidx.preference.Preference
android:key="@string/pref_key_about"
=====================================
mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/PlayStoreReviewPromptControllerTest.kt
=====================================
@@ -59,7 +59,7 @@ class PlayStoreReviewPromptControllerTest {
launch {
controller.tryPromptReview(activity)
- assertTrue(reviewManager.promptHasBeenRequested)
+ assertTrue(!reviewManager.promptHasBeenRequested)
}
}
}
@@ -102,7 +102,7 @@ class PlayStoreReviewPromptControllerTest {
onError = { onErrorRan = true },
)
- assertTrue(onErrorRan)
+ assertTrue(!onErrorRan)
}
}
}
=====================================
mobile/android/fenix/app/src/test/java/org/mozilla/fenix/reviewprompt/ShowPlayStoreReviewPromptTest.kt
=====================================
@@ -155,7 +155,7 @@ class ShowPlayStoreReviewPromptTest {
feature.start()
testDispatcher.scheduler.advanceUntilIdle()
- coVerify(exactly = 1) {
+ coVerify(exactly = 0) {
promptController.tryPromptReview(mockActivity)
}
captureMiddleware.assertLastAction(ReviewPromptShown::class)
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/cadcb5e…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/cadcb5e…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-150.0a1-16.0-2] 4 commits: !dropme Bug 2024724 - Fix PowerCounters.h because we do not compile...
by Pier Angelo Vendrame (@pierov) 30 Apr '26
by Pier Angelo Vendrame (@pierov) 30 Apr '26
30 Apr '26
Pier Angelo Vendrame pushed to branch mullvad-browser-150.0a1-16.0-2 at The Tor Project / Applications / Mullvad Browser
Commits:
6c679501 by Damien Carver at 2026-04-30T13:57:51+02:00
!dropme Bug 2024724 - Fix PowerCounters.h because we do not compile PowerCounters-win.cpp against __MINGW32__ r=canaltinova,profiler-reviewers
BB 44868: Backport upstream commit to fix Windows builds
This commit should be dropped during the 151 rebase as it was reverted
as part of https://bugzilla.mozilla.org/show_bug.cgi?id=2026031 in the
upstream 151 branch.
- - - - -
999f3a02 by Nicolas Vigier at 2026-04-30T13:59:21+02:00
fixup! BB 43615: Add Gitlab Issue and Merge Request templates
BB 44870: Remove legacy branch from gitlab templates
- - - - -
7942a77a by Beatriz Rizental at 2026-04-30T13:59:32+02:00
fixup! Base Browser's .mozconfigs.
Bug 44848: Stop overwriting Android toolchain paths.
Keep the overwrites only on tor-browser-build.
- - - - -
d513cebd by Nicolas Vigier at 2026-04-30T13:59:42+02:00
fixup! Base Browser's .mozconfigs.
BB 44555: Remove mozconfig-linux-i686
- - - - -
8 changed files:
- .gitlab/issue_templates/050 Backport.md
- browser/config/mozconfigs/base-browser-android
- mozconfig-android-all
- − mozconfig-linux-i686
- mozglue/baseprofiler/public/BaseProfilerState.h
- tools/profiler/core/PowerCounters.h
- tools/profiler/moz.build
- tools/profiler/public/ProfilerState.h
Changes:
=====================================
.gitlab/issue_templates/050 Backport.md
=====================================
@@ -24,7 +24,6 @@ please ensure the title has the following format:
- [ ] Alpha
- [ ] Stable
-- [ ] Legacy
## Notes
=====================================
browser/config/mozconfigs/base-browser-android
=====================================
@@ -13,20 +13,6 @@ CC="clang"
CXX="clang++"
ac_add_options --enable-linker=lld
-if test -n "$ANDROID_HOME"; then
- ac_add_options --with-android-sdk=$ANDROID_HOME
-fi
-
-if test -n "$ANDROID_NDK_HOME"; then
- ac_add_options --with-android-ndk=$ANDROID_NDK_HOME
-fi
-
-if test -n "$GRADLE_HOME"; then
- ac_add_options --with-gradle=$GRADLE_HOME/bin/gradle
-fi
-# Otherwise (as per /mobile/android/gradle.config) a version will be downloaded by the gradle wrapper when needed
-# so don't use this override, and let it do it's thing
-
ac_add_options --enable-strip
ac_add_options --enable-install-strip
ac_add_options --disable-tests
=====================================
mozconfig-android-all
=====================================
@@ -8,10 +8,6 @@ ac_add_options --enable-application=mobile/android
ac_add_options --disable-compile-environment
-ac_add_options --with-java-bin-path=$JAVA_HOME/bin
-ac_add_options --with-android-sdk=$ANDROID_HOME
-ac_add_options --with-gradle=$GRADLE_HOME/bin/gradle
-
ac_add_options --disable-tests
ac_add_options --disable-debug
=====================================
mozconfig-linux-i686 deleted
=====================================
@@ -1,16 +0,0 @@
-. $topsrcdir/browser/config/mozconfigs/base-browser
-
-ac_add_options --target=i686-linux-gnu
-
-# Moz switched to lld for all Linux targets in Bug 1839739.
-# Also, gold used not to work with debug symbols (tor-browser#42146).
-ac_add_options --enable-linker=lld
-
-ac_add_options --disable-strip
-ac_add_options --disable-install-strip
-
-ac_add_options --enable-default-toolkit=cairo-gtk3
-
-# Let's make sure no preference is enabling either Adobe's or Google's CDM.
-ac_add_options --disable-eme
-
=====================================
mozglue/baseprofiler/public/BaseProfilerState.h
=====================================
@@ -136,7 +136,7 @@ class MOZ_RAII AutoProfilerStats {
"Record the power used by the entire system with each sample. " \
"Only available with Intel CPUs and requires setting " \
"the sysctl kernel.perf_event_paranoid to 0."
-#elif defined(GP_OS_windows)
+#elif defined(GP_OS_windows) && defined(_MSC_VER)
# define POWER_HELP \
"Record the value of every energy meter available on the system with " \
"each sample. Only available on Windows 11 with Intel CPUs."
=====================================
tools/profiler/core/PowerCounters.h
=====================================
@@ -10,7 +10,7 @@
#include "mozilla/UniquePtr.h"
#include "mozilla/Vector.h"
-#if defined(GP_OS_windows)
+#if defined(GP_OS_windows) && defined(_MSC_VER)
class PowerMeterDevice;
#endif
#if defined(GP_PLAT_arm64_darwin)
@@ -50,20 +50,20 @@ bool GetRailEnergyData(RailEnergyData*, size_t* size_of_arr);
class PowerCounters {
public:
-#if defined(GP_OS_windows) || defined(GP_OS_darwin) || \
+#if (defined(GP_OS_windows) && defined(_MSC_VER)) || defined(GP_OS_darwin) || \
defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_arm64_android)
explicit PowerCounters();
#else
explicit PowerCounters() {};
#endif
-#if defined(GP_OS_windows) || defined(GP_PLAT_amd64_darwin) || \
- defined(GP_PLAT_arm64_android)
+#if (defined(GP_OS_windows) && defined(_MSC_VER)) || \
+ defined(GP_PLAT_amd64_darwin) || defined(GP_PLAT_arm64_android)
~PowerCounters();
#else
~PowerCounters() = default;
#endif
-#if defined(GP_OS_windows) || defined(GP_PLAT_amd64_darwin) || \
- defined(GP_PLAT_arm64_android)
+#if (defined(GP_OS_windows) && defined(_MSC_VER)) || \
+ defined(GP_PLAT_amd64_darwin) || defined(GP_PLAT_arm64_android)
void Sample();
#else
void Sample() {};
@@ -75,7 +75,7 @@ class PowerCounters {
private:
CountVector mCounters;
-#if defined(GP_OS_windows)
+#if defined(GP_OS_windows) && defined(_MSC_VER)
mozilla::Vector<mozilla::UniquePtr<PowerMeterDevice>> mPowerMeterDevices;
#endif
#if defined(GP_PLAT_amd64_darwin)
=====================================
tools/profiler/moz.build
=====================================
@@ -167,6 +167,7 @@ elif CONFIG["OS_TARGET"] == "WINNT" and CONFIG["TARGET_CPU"] in (
UNIFIED_SOURCES += [
"gecko/nsProfiler.cpp",
]
+ # Keep in sync with PowerCounters.h, BaseProfilerState.h and ProfilerState.h (see __MINGW32__)
if CONFIG["CC_TYPE"] == "clang-cl":
UNIFIED_SOURCES += [
"core/PowerCounters-win.cpp",
=====================================
tools/profiler/public/ProfilerState.h
=====================================
@@ -31,7 +31,7 @@
"Only available with Intel CPUs and requires setting " \
"the sysctl kernel.perf_event_paranoid to 0."
-#elif defined(GP_OS_windows)
+#elif defined(GP_OS_windows) && defined(_MSC_VER)
# define POWER_HELP \
"Record the value of every energy meter available on the system with " \
"each sample. Only available on Windows 11 with Intel CPUs."
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/3d…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/3d…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-150.0a1-16.0-2] fixup! [android] Implement Android-native Connection Assist UI
by Dan Ballard (@dan) 29 Apr '26
by Dan Ballard (@dan) 29 Apr '26
29 Apr '26
Dan Ballard pushed to branch tor-browser-150.0a1-16.0-2 at The Tor Project / Applications / Tor Browser
Commits:
5fa6338b by clairehurst at 2026-04-29T13:15:00-07:00
fixup! [android] Implement Android-native Connection Assist UI
Fix color styling presented in one of the rebases between 140 and 150.
- - - - -
3 changed files:
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt
- mobile/android/fenix/app/src/main/res/layout/fragment_tor_connection_assist.xml
- mobile/android/fenix/app/src/main/res/values/colors.xml
Changes:
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt
=====================================
@@ -136,6 +136,10 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler, SystemIn
}
private fun setSettingsButton(screen: ConnectAssistUiState) {
+ binding.settingsButtonImage.imageTintList = AppCompatResources.getColorStateList(
+ requireContext(),
+ R.color.settings_button_white,
+ )
binding.settingsButton.visibility = if (screen.settingsButtonVisible) View.VISIBLE else View.GONE
binding.settingsButton.setOnClickListener {
openSettings()
@@ -143,6 +147,10 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler, SystemIn
}
private fun setBackButton(screen: ConnectAssistUiState) {
+ binding.backButtonImage.imageTintList = AppCompatResources.getColorStateList(
+ requireContext(),
+ R.color.settings_button_white,
+ )
binding.backButton.visibility = if (screen.backButtonVisible) View.VISIBLE else View.INVISIBLE
binding.backButton.setOnClickListener {
onBackPressed()
@@ -323,6 +331,10 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler, SystemIn
}
private fun setButton2(screen: ConnectAssistUiState) {
+ binding.torBootstrapButton2.backgroundTintList = AppCompatResources.getColorStateList(
+ requireContext(),
+ R.color.configure_connection_button_white,
+ )
binding.torBootstrapButton2.visibility =
if (screen.torBootstrapButton2Visible) View.VISIBLE else View.GONE
if (screen.torBootstrapButton2ShouldRestartApp) {
=====================================
mobile/android/fenix/app/src/main/res/layout/fragment_tor_connection_assist.xml
=====================================
@@ -28,6 +28,7 @@
app:layout_constraintTop_toTopOf="parent">
<ImageView
+ android:id="@+id/settings_button_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/settings"
@@ -50,6 +51,7 @@
app:layout_constraintTop_toTopOf="parent">
<ImageView
+ android:id="@+id/back_button_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/settings"
@@ -154,7 +156,6 @@
android:layout_marginEnd="24dp"
android:layout_marginBottom="8dp"
android:background="@drawable/rounded_corners"
- android:backgroundTint="@color/connect_button_purple"
android:minWidth="360dp"
android:text="@string/tor_bootstrap_connect"
android:textAlignment="center"
@@ -174,7 +175,6 @@
android:layout_marginEnd="24dp"
android:layout_marginBottom="8dp"
android:background="@drawable/rounded_corners"
- android:backgroundTint="@color/configure_connection_button_white"
android:minWidth="360dp"
android:text="@string/connection_assist_configure_connection_button"
android:textAlignment="center"
=====================================
mobile/android/fenix/app/src/main/res/values/colors.xml
=====================================
@@ -332,4 +332,5 @@
<color name="configure_connection_button_white">#E1E0E7</color>
<color name="warning_yellow">#FFA436</color>
<color name="progress_background_tint">#55148C</color>
+ <color name="settings_button_white">#FBFBFE</color>
</resources>
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/5fa6338…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/5fa6338…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-140.10.1esr-15.0-1] 2 commits: fixup! TB 7494: Create local home page for TBB.
by morgan (@morgan) 29 Apr '26
by morgan (@morgan) 29 Apr '26
29 Apr '26
morgan pushed to branch tor-browser-140.10.1esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
36994e53 by Henry Wilkes at 2026-04-29T10:54:52+01:00
fixup! TB 7494: Create local home page for TBB.
TB 44746: Modify YEC banner to be used for summer 2026 funding campaign.
- - - - -
502048e6 by Henry Wilkes at 2026-04-29T10:55:43+01:00
TB 44746: Assets for summer 2026 funding campaign.
- - - - -
7 changed files:
- browser/components/abouttor/AboutTorParent.sys.mjs
- browser/components/abouttor/content/aboutTor.css
- browser/components/abouttor/content/aboutTor.html
- browser/components/abouttor/content/aboutTor.js
- + browser/components/abouttor/content/yec2025-image.svg
- browser/components/abouttor/jar.mn
- toolkit/locales/en-US/toolkit/global/tor-browser.ftl
Changes:
=====================================
browser/components/abouttor/AboutTorParent.sys.mjs
=====================================
@@ -23,6 +23,44 @@ export class AboutTorParent extends JSWindowActorParent {
*/
static #dismissYEC = false;
+ /**
+ * A shuffled array of tool numbers. `null` whilst unset.
+ *
+ * @type {?number[]}
+ */
+ static #toolNums = null;
+ /**
+ * The current index in `#toolNums`.
+ *
+ * @type {number}
+ */
+ static #toolIndex = 0;
+
+ /**
+ * Return the pair of tools numbers to show in the next about:tor window.
+ *
+ * @returns {{ toolNum1: number, toolNum2: number }} - The tool number pairs.
+ */
+ static #getToolNumPair() {
+ const numTools = 11;
+ if (!this.#toolNums) {
+ this.#toolNums = Array.from({ length: numTools }, (_, index) => index);
+ // Shuffle the array with Fisher–Yates.
+ for (let index = numTools - 1; index > 0; index--) {
+ const topBound = index + 1;
+ const exchange = Math.floor(Math.random() * topBound) % topBound;
+ const tmpNum = this.#toolNums[index];
+ this.#toolNums[index] = this.#toolNums[exchange];
+ this.#toolNums[exchange] = tmpNum;
+ }
+ }
+ const toolNum1 = this.#toolNums[this.#toolIndex % numTools];
+ const toolNum2 = this.#toolNums[(this.#toolIndex + 1) % numTools];
+ this.#toolIndex = (this.#toolIndex + 2) % numTools;
+
+ return { toolNum1, toolNum2 };
+ }
+
/**
* Whether this instance has a preloaded browser.
*
@@ -58,6 +96,8 @@ export class AboutTorParent extends JSWindowActorParent {
appLocale = "ja";
}
+ const { toolNum1, toolNum2 } = AboutTorParent.#getToolNumPair();
+
return {
torConnectEnabled: lazy.TorConnect.enabled,
messageData: lazy.AboutTorMessage.getNext(),
@@ -69,6 +109,8 @@ export class AboutTorParent extends JSWindowActorParent {
),
appLocale,
dismissYEC: AboutTorParent.#dismissYEC,
+ toolNum1,
+ toolNum2,
};
}
=====================================
browser/components/abouttor/content/aboutTor.css
=====================================
@@ -350,12 +350,11 @@ body.show-yec h1 {
"yec-matching yec-image" auto
"yec-donate yec-image" min-content
/ 1fr min-content;
- --yec-image-background: #1f0333;
- /* Remove 1px from padding for border. */
- padding-block: 55px 55px;
- padding-inline: 47px 47px;
+ --yec-image-background: #3a1653;
+ padding-block: 56px 56px;
+ padding-inline: 48px 48px;
box-sizing: border-box;
- max-width: 850px;
+ max-width: 840px;
margin-block-end: 40px;
/* Position for the close button. */
position: relative;
@@ -389,8 +388,8 @@ body.show-yec h1 {
#yec-heading {
grid-area: yec-heading;
margin-block: 0 16px;
- font-size: 64px;
- font-weight: 400;
+ font-size: var(--font-size-xxlarge);
+ font-weight: 600;
}
#yec-body {
@@ -398,13 +397,13 @@ body.show-yec h1 {
margin-block: 0 12px;
}
-#yec-body-highlight {
- font-weight: 600;
-}
-
#yec-matching {
grid-area: yec-matching;
- margin-block: 0 32px;
+ margin-block: 0 24px;
+}
+
+#yec-banner b {
+ font-weight: 600;
}
#yec-donate-link {
@@ -481,7 +480,7 @@ body.show-yec h1 {
}
#yec-heading {
- font-size: 3.5rem;
+ font-size: var(--font-size-xlarge);
}
#yec-matching {
@@ -502,20 +501,16 @@ body.show-yec h1 {
@media not ((prefers-contrast) or (forced-colors)) {
#yec-banner {
- border-color: transparent;
+ border-color: #523168;
background-color: var(--yec-image-background);
--yec-text: #ffffff;
- --yec-button-background: #b6e368;
- --yec-button-background-hover: #d2f2a1;
- --yec-button-background-active: #ecfcd8;
- --yec-button-text: #15141A;
+ --yec-button-background: var(--tor-button-background-color-light);
+ --yec-button-background-hover: var(--tor-button-background-color-hover-light);
+ --yec-button-background-active: var(--tor-button-background-color-active-light);
+ --yec-button-text: var(--tor-button-text-color-light);
color: var(--yec-text);
}
- #yec-body-highlight {
- color: var(--yec-button-background);
- }
-
#yec-donate-link {
--in-content-button-text-color: var(--yec-button-text);
--in-content-button-text-color-hover: var(--yec-button-text);
=====================================
browser/components/abouttor/content/aboutTor.html
=====================================
@@ -35,13 +35,15 @@
<body class="onion-pattern-background">
<!-- Year End Campaign (YEC). -->
<article id="yec-banner" aria-labelledby="yec-heading">
- <img id="yec-image" alt="" />
- <h2 id="yec-heading"></h2>
- <p id="yec-body">
- <b id="yec-body-highlight" data-l10n-name="highlight"></b>
- </p>
- <p id="yec-matching"></p>
- <a id="yec-donate-link">
+ <img
+ id="yec-image"
+ alt=""
+ src="chrome://browser/content/abouttor/yec2025-image.svg"
+ />
+ <h2 id="yec-heading" data-l10n-id="summer-2026-funding-heading"></h2>
+ <p id="yec-body"></p>
+ <p id="yec-matching" data-l10n-id="summer-2026-funding-outro"></p>
+ <a id="yec-donate-link" target="_blank">
<span data-l10n-id="yec-donate-button"></span>
<img
id="yec-donate-icon"
=====================================
browser/components/abouttor/content/aboutTor.js
=====================================
@@ -431,14 +431,14 @@ const YecArea = {
*
* @type {?integer}
*/
- _startDate: null, // No YEC is active.
+ _startDate: Date.UTC(2026, 4, 19, 15), // 19th May 2026, 15 UTC.
/**
* The epoch time to stop showing the banner, if at all.
*
* @type {?integer}
*/
- _endDate: null, // No YEC is active.
+ _endDate: Date.UTC(2026, 5, 19, 0), // 19th June 2026, 0 UTC.
/**
* Whether the area has been initialised.
@@ -461,6 +461,72 @@ const YecArea = {
*/
_shouldShow: false,
+ /**
+ * The tool number to show first, or `null` whilst unset.
+ *
+ * @type {?number}
+ */
+ _toolNum1: null,
+
+ /**
+ * The tool number to show second, or `null` whilst unset.
+ *
+ * @type {?number}
+ */
+ _toolNum2: null,
+
+ /**
+ * The name and description data for each tool.
+ *
+ * @type {object[]}
+ */
+ _toolData: [
+ {
+ name: "Onion Browser",
+ descId: "summer-2026-funding-tool-onion-browser-description",
+ },
+ {
+ name: "Quiet",
+ descId: "summer-2026-funding-tool-quiet-description",
+ },
+ {
+ name: "Ricochet Refresh",
+ descId: "summer-2026-funding-tool-ricochet-refresh-description",
+ },
+ {
+ name: "SecureDrop",
+ descId: "summer-2026-funding-tool-securedrop-description",
+ },
+ {
+ name: "OnionShare",
+ descId: "summer-2026-funding-tool-onionshare-description",
+ },
+ {
+ name: "Digital Security Helpdesk",
+ descId: "summer-2026-funding-tool-digital-security-helpdesk-description",
+ },
+ {
+ name: "Paskoocheh",
+ descId: "summer-2026-funding-tool-paskoocheh-description",
+ },
+ {
+ name: "Unredacted",
+ descId: "summer-2026-funding-tool-unredacted-description",
+ },
+ {
+ name: "Osservatorio Nessuno",
+ descId: "summer-2026-funding-tool-osservatorio-nessuno-description",
+ },
+ {
+ name: "Save",
+ descId: "summer-2026-funding-tool-save-description",
+ },
+ {
+ name: "OONI",
+ descId: "summer-2026-funding-tool-ooni-description",
+ },
+ ],
+
/**
* The banner element.
*
@@ -498,8 +564,10 @@ const YecArea = {
* @param {boolean} dismissYEC - Whether the user has dismissed YEC.
* @param {boolean} isStable - Whether this is a stable release.
* @param {string} appLocale - The app locale, as BCP47.
+ * @param {number} toolNum1 - The number for the first tool to show.
+ * @param {number} toolNum2 - The number for the first tool to show.
*/
- potentiallyShow(dismissYEC, isStable, appLocale) {
+ potentiallyShow(dismissYEC, isStable, appLocale, toolNum1, toolNum2) {
const now = Date.now();
this._shouldShow =
!dismissYEC &&
@@ -508,13 +576,15 @@ const YecArea = {
now >= this._startDate &&
now < this._endDate;
this._locale = appLocale;
+ this._toolNum1 = toolNum1;
+ this._toolNum2 = toolNum2;
this._update();
},
/**
* Update the visibility of the banner to reflect the new state.
*/
- _update() {
+ async _update() {
if (!this._initialized) {
return;
}
@@ -526,8 +596,27 @@ const YecArea = {
return;
}
+ const tool1 = this._toolData[this._toolNum1];
+ const tool2 = this._toolData[this._toolNum2];
+
+ const [tool1Desc, tool2Desc] = await document.l10n.formatValues([
+ { id: tool1.descId },
+ { id: tool2.descId },
+ ]);
+
+ document.l10n.setAttributes(
+ document.getElementById("yec-body"),
+ "summer-2026-funding-intro",
+ {
+ "tool1-name": tool1.name,
+ "tool1-description": tool1Desc,
+ "tool2-name": tool2.name,
+ "tool2-description": tool2Desc,
+ }
+ );
+
const donateLink = document.getElementById("yec-donate-link");
- const base = "https://www.torproject.org/donate";
+ const base = "https://internetfreedom.torproject.org";
donateLink.href = base;
document.body.classList.add("show-yec");
@@ -574,11 +663,13 @@ window.addEventListener("InitialData", event => {
surveyDismissVersion,
appLocale,
dismissYEC,
+ toolNum1,
+ toolNum2,
} = event.detail;
SearchWidget.setOnionizeState(!!searchOnionize);
MessageArea.setMessageData(messageData, !!isStable, !!torConnectEnabled);
SurveyArea.potentiallyShow(surveyDismissVersion, isStable, appLocale);
- YecArea.potentiallyShow(dismissYEC, isStable, appLocale);
+ YecArea.potentiallyShow(dismissYEC, isStable, appLocale, toolNum1, toolNum2);
gInitialData = true;
maybeComplete();
=====================================
browser/components/abouttor/content/yec2025-image.svg
=====================================
@@ -0,0 +1,20 @@
+<svg width="495" height="381" viewBox="0 0 495 381" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M247.419 264.776C201.679 273.69 174.117 246.935 166.057 232.441L105.262 261.112L170.602 258.902L131.868 327.126L195.407 280.785L189.197 362.111L227.832 285.742L262.535 343.489L247.432 264.776H247.419Z" fill="#B6E368"/>
+ <path d="M223.938 98.8502C269.555 89.3133 297.484 115.702 305.741 130.086L366.144 100.584L300.841 103.686L338.631 34.949L275.741 82.1453L280.837 0.733295L243.255 77.6271L207.756 20.3445L223.938 98.8502Z" fill="#B6E368"/>
+ <g opacity="0.8">
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M256.349 131.796H260.465V136.119H256.141V132.016H247.921V136.119H252.025V140.222H256.141V148.648H260.465V157.293H256.141V148.867H251.817V140.442H247.701V136.119H243.597V132.016H239.273V165.499H260.477V157.293H264.801V165.499H277.137V169.822H285.577V174.145H276.928V169.822H264.814V207.408H277.149V203.305H285.797V207.408H289.913V211.731H285.797V216.053H281.681V220.376H277.357V224.479H273.033V228.582H269.138V232.905H264.814V228.582H268.709V224.259H273.033V220.156H277.357V216.053H281.473V211.731H285.589V207.628H277.149V211.731H264.814V220.364H260.489V228.79H256.165V232.893H264.814V237.215H251.829V228.582H256.153V220.156H260.477V211.731H239.273V237.215H251.817V241.099H222.393V237.215H209.629V232.893H205.525V228.57H201.409V224.467H197.085V220.364H192.981V216.041H197.306V220.144H201.409V224.247H205.733V228.57H209.849V232.893H218.289V228.79H213.965V220.364H209.849V211.731H197.085V207.628H188.645V211.731H184.321V207.408H180.217V169.822H184.541V207.408H188.657V203.305H197.306V207.408H209.849V169.822H197.306V174.145H188.657V169.822H184.541V161.188H188.657V152.763H192.981V148.66H197.306V144.557H201.409V140.454H205.525V136.131H214.173V140.454H205.733V144.777H201.618V148.88H197.293V152.983H192.969V161.408H188.853V169.834H197.073V165.511H209.837V157.305H214.161V165.511H234.924V132.028H231.029V136.131H226.705V132.028H218.485V136.131H214.161V131.808H218.277V127.705H256.349V131.808V131.796ZM214.161 220.132H218.277V228.558H222.601V237.191H234.937V211.706H214.173V220.132H214.161ZM239.261 207.396H260.465V169.81H239.261V207.396ZM214.161 207.396H234.924V169.81H214.161V207.396Z" fill="#C272FF"/>
+ <path d="M192.968 216.029H188.644V211.706H192.968V216.029Z" fill="#C272FF"/>
+ <path d="M294.212 207.395H289.888V169.809H294.212V207.395Z" fill="#C272FF"/>
+ <path d="M269.113 140.429H273.008V144.532H277.332V148.635H281.448V152.738H285.772V161.164H289.888V169.797H285.564V161.371H281.448V152.946H277.124V148.843H273.008V144.74H268.684V140.417H260.465V136.094H269.113V140.417V140.429Z" fill="#C272FF"/>
+ <path d="M226.718 140.429H222.602V148.855H218.486V157.281H214.162V148.635H218.278V140.209H222.394V136.106H226.718V140.429Z" fill="#C272FF"/>
+ <path d="M260.464 136.119V131.796H256.348V127.693H218.276V131.796H214.16V136.119M260.464 136.119H256.14V132.016H247.92V136.119H252.024V140.222H256.14V148.648H260.464V157.293M260.464 136.119V140.442H268.683V144.764H273.008V148.867H277.123V152.97H281.448V161.396H285.564V169.822M260.464 136.119H269.112V140.442H273.008V144.545H277.332V148.648H281.448V152.75H285.772V161.176H289.888V169.81M214.16 136.119H205.512V140.442H201.396V144.545H197.292V148.648H192.968V152.75H188.644V161.176H184.528V169.81M214.16 136.119V140.442H205.72V144.764H201.604V148.867H197.28V152.97H192.956V161.396H188.84V169.822H197.06V165.499H209.824V157.293H214.148M214.16 136.119H218.484V132.016H226.704V136.119M289.888 169.81H294.212V207.396H289.888M289.888 169.81V207.396M260.464 157.281H256.14V148.855H251.816V140.429H247.7V136.107H243.596V132.004H239.272V165.487H260.476V157.281H260.464ZM260.464 157.281H264.788V165.487H277.123V169.81H285.564M289.888 207.396H285.772V203.293H277.123V207.396H264.788V169.81H276.903V174.132H285.551V169.81H289.875M289.888 207.396V211.718H285.772V216.041H281.656V220.364H277.332V224.467H273.008V228.57H269.112V232.892H264.788M264.788 232.88V228.558H268.683V224.235H273.008V220.132H277.332V216.029H281.448V211.706H285.564V207.603H277.123V211.706H264.788V220.339H260.464V228.765H256.14V232.868H264.788V232.88ZM264.788 232.88V237.203H251.803M251.803 237.191V228.558H256.128V220.132H260.452V211.706H239.248V237.191H251.791H251.803ZM251.803 237.191V241.074H222.38V237.191H209.616V232.868H205.512V228.545H201.396V224.442H197.072V220.339H192.968V216.017M192.968 216.029H197.292V220.132H201.396V224.235H205.72V228.558H209.836V232.88H218.276V228.777H213.952V220.352H209.836V211.718H197.072V207.615H188.632V211.718M192.968 216.029H188.644V211.706H192.968V216.029ZM188.632 211.706H184.308V207.383H180.204V169.797H184.528M184.528 169.81V207.396H188.644V203.293H197.292V207.396H209.836V169.81H197.292V174.132H188.644V169.81H184.528ZM214.16 157.281V165.487H234.923V132.004H231.028V136.107H226.704M214.16 157.281H218.484V148.855H222.6V140.429H226.716V136.107H222.392V140.21H218.276V148.635H214.16V157.281ZM214.16 220.132H218.276V228.558H222.6V237.191H234.936V211.706H214.172V220.132H214.16ZM239.26 207.396H260.464V169.81H239.26V207.396ZM214.16 207.396H234.923V169.81H214.16V207.396Z" stroke="#C272FF" stroke-width="0.618845"/>
+ </g>
+ <path d="M166.566 131.845H175.239L169.665 124.591L179.881 122.381L170.633 111.794L178.264 111.587L178.607 109.511L127.183 90.5711C125.738 90.0338 124.28 89.5942 122.81 89.2401L97.6982 24.4963C92.2226 10.3802 78.3437 0.904321 63.1542 0.904321C58.6095 0.904321 54.1139 1.73468 49.8265 3.3954L23.6612 13.4696C4.62526 20.8208 -4.86823 42.227 2.49382 61.2032L38.3608 153.715C43.8364 167.831 57.7153 177.319 72.8926 177.319C74.9261 177.319 76.935 177.148 78.9195 176.807C82.7169 180.238 87.1513 182.937 92.1001 184.756L143.855 203.818L137.804 193.072H149.686L145.643 183.242H158.493L153.238 173.546L119.454 161.103C130.674 153.727 136.958 140.771 135.954 127.534L175.349 142.041L166.566 131.845ZM151.731 175.28L154.904 181.105H142.446L146.501 190.935H134.141L139.139 199.812L92.8473 182.753C83.1946 179.2 75.5263 172.117 71.2389 162.825C66.9638 153.544 66.5718 143.165 70.1365 133.591L76.9963 115.164C80.2914 106.324 86.551 99.412 94.2316 95.1259L104.509 121.649C103.799 122.528 103.211 123.541 102.794 124.665L95.9343 143.091C94.9175 145.827 95.0278 148.782 96.2527 151.419C97.4655 154.069 99.6581 156.096 102.402 157.11L151.731 175.28ZM65.6653 144.337C65.4938 144.032 65.3468 143.702 65.2121 143.36L29.3328 50.8481C28.2549 48.0884 28.9653 45.7316 29.4798 44.5837C30.0066 43.4237 31.2805 41.3111 34.049 40.2488L60.202 30.1623C61.182 29.7838 62.1742 29.6006 63.1542 29.6006C65.9716 29.6006 69.4382 31.2247 70.8469 34.8636L93.4476 93.1232C85.2158 97.6169 78.503 104.98 74.9873 114.42L68.1275 132.858C66.7311 136.595 65.9103 140.466 65.6653 144.337ZM104.803 125.397C104.975 124.921 105.195 124.469 105.44 124.054L106.726 127.363C107.804 130.135 107.094 132.48 106.579 133.628H106.567C106.065 134.8 104.803 136.9 102.022 137.963L99.8174 138.817L104.803 125.397ZM116.918 160.163L103.149 155.095C100.932 154.289 99.1804 152.665 98.2004 150.528C97.2205 148.403 97.1225 146.034 97.9432 143.836L98.8129 141.492L102.794 139.965C106.297 138.622 107.89 135.96 108.539 134.495C109.188 133.042 110.083 130.086 108.723 126.594L106.983 122.113V122.064L106.273 120.281L106.163 119.988L96.1303 94.1245L96.1058 94.0757L95.4565 92.3906L95.3585 92.1219L72.8436 34.0821C71.0674 29.5029 66.6943 27.4637 63.1542 27.4637C61.9169 27.4637 60.6552 27.6957 59.4303 28.1597L33.2772 38.2461C29.7983 39.5894 28.1814 42.2514 27.5321 43.7045C26.8706 45.1699 25.9764 48.1372 27.3361 51.6296L63.2154 144.142C63.7666 145.595 64.5996 146.804 65.5918 147.744C65.7021 153.202 66.9393 158.624 69.2912 163.716C71.2389 167.929 73.8359 171.702 76.9718 174.938C75.6121 175.109 74.2646 175.182 72.8926 175.182C58.5973 175.182 45.5146 166.244 40.3575 152.934L4.49051 60.4217C-2.4428 42.5567 6.49946 22.396 24.433 15.4723L50.5982 5.39804C54.6406 3.8228 58.8668 3.04128 63.1542 3.04128C77.4618 3.04128 90.5322 11.9677 95.7015 25.2779L120.299 88.6906L120.385 88.9226L121.156 90.913L121.218 91.0595L131.581 117.79C132.414 119.939 133.014 122.113 133.381 124.311C133.406 124.384 133.418 124.457 133.43 124.53C133.553 125.19 133.639 125.861 133.7 126.533C133.7 126.594 133.712 126.643 133.712 126.704C135.133 140.039 128.629 153.214 116.918 160.163ZM135.697 125.165C135.317 122.43 134.619 119.683 133.577 117.008L123.765 91.7067C124.66 91.9632 125.554 92.244 126.436 92.5737L172.666 109.608L166.039 109.791L175.888 121.062L165.904 123.212L170.902 129.708H161.899L168.33 137.181L135.697 125.165Z" fill="white"/>
+ <path d="M488.077 306.123L422.933 212.255C415.84 202.035 404.252 195.831 391.819 195.55C388.193 192.314 383.893 189.701 379.018 187.906L332.347 170.713L332.151 170.64L320.771 170.957L329.198 180.592L318.639 182.875L325.034 191.191H316.802L323.417 198.872L356.38 211.01L348.847 216.212C339.145 222.903 333.449 233.222 332.31 244.078L302.004 232.917L307.259 242.576H293.846L297.888 252.406H286.643L291.126 260.355L344.694 280.088C345.221 280.284 345.735 280.467 346.262 280.626L404.13 364.016C411.406 374.481 423.374 380.733 436.15 380.733C444.076 380.733 451.707 378.352 458.249 373.846L478.228 360.072C486.766 354.174 492.499 345.321 494.361 335.137C496.223 324.94 493.993 314.646 488.077 306.123ZM324.678 197.052L321.469 193.328H329.37L322.388 184.255L333.192 181.923L325.377 172.972L331.795 172.789L378.271 189.909C382.092 191.313 385.535 193.255 388.548 195.611C381.48 196.039 374.706 198.371 368.826 202.425L358.536 209.52L324.678 197.052ZM345.429 278.086L344.168 277.622L292.609 258.621L290.306 254.543H301.085L297.043 244.713H310.861L306.598 236.885L332.138 246.288L334.245 247.069L354.237 254.433C356.932 255.422 359.749 255.397 362.273 254.53C362.971 254.311 363.632 254.018 364.269 253.651C366.695 252.32 368.667 250.147 369.696 247.375L374.179 235.359L386.012 227.19C387.495 226.176 389.148 225.651 390.924 225.651C392.296 225.651 395.751 226.017 398.091 229.388L401.447 234.223C401.226 235.078 400.969 235.921 400.675 236.775L393.497 256.106L393.142 257.022C390.14 264.507 385.02 270.49 378.748 274.52C378.148 274.911 377.536 275.289 376.911 275.631C368.287 280.504 357.777 281.896 347.61 278.806C346.887 278.599 346.152 278.354 345.429 278.086ZM362.432 246.544C362.665 245.226 363.461 242.747 366.033 240.976L371.092 237.484L367.687 246.63C366.829 248.95 365.176 250.745 363.142 251.844C362.101 249.683 362.224 247.656 362.432 246.544ZM402.366 238.424L402.709 237.471C402.819 237.142 402.929 236.824 403.027 236.495L463.234 323.255C465.023 325.819 464.888 328.42 464.643 329.739C464.398 331.058 463.614 333.525 461.042 335.307L441.062 349.082C439.58 350.107 437.926 350.62 436.15 350.62C434.778 350.62 431.312 350.266 428.972 346.884L379.973 276.279C386.588 272.005 391.966 265.704 395.138 257.803L402.366 238.424ZM492.254 334.746C490.49 344.38 485.075 352.745 477.003 358.313L457.024 372.088C450.85 376.349 443.635 378.596 436.15 378.596C424.072 378.596 412.766 372.698 405.894 362.807L349.496 281.529C352.534 282.25 355.584 282.592 358.598 282.592C365.519 282.592 372.256 280.76 378.136 277.39L427.208 348.105C430.136 352.305 434.435 352.757 436.15 352.757C438.367 352.757 440.425 352.11 442.275 350.84L462.254 337.066C465.464 334.843 466.456 331.766 466.75 330.118C467.044 328.481 467.228 325.245 464.998 322.046L403.774 233.808L402.084 231.378L399.854 228.166C396.939 223.966 392.639 223.514 390.924 223.514H390.912C388.695 223.514 386.637 224.161 384.787 225.431L375.478 231.854L372.403 233.979L364.808 239.218C361.599 241.428 360.619 244.517 360.313 246.154C360.068 247.533 359.908 250.012 361.158 252.65C359.198 253.224 357.042 253.187 354.984 252.43L334.38 244.847C335.323 234.394 340.738 224.405 350.06 217.97L358.83 211.913L360.986 210.424L370.039 204.184C376.213 199.934 383.44 197.675 390.912 197.675H390.961C392.039 197.675 393.117 197.724 394.171 197.822C405.012 198.774 414.934 204.477 421.169 213.476L486.313 307.344C491.898 315.391 494.005 325.123 492.254 334.746Z" fill="white"/>
+ <path d="M82.7096 239.196L95.6423 239.231L95.6325 242.957L82.7096 242.922V259.141H78.9108V242.912L65.5817 242.877L65.5925 239.151L78.9108 239.186V223.021H82.7096V239.196Z" fill="#C272FF"/>
+ <path d="M410.924 116.181L423.856 116.216L423.846 119.937L410.924 119.902V136.101H407.125V119.892L393.796 119.857L393.806 116.136L407.125 116.171V100.028H410.924V116.181Z" fill="#C272FF"/>
+ <path d="M62.4132 304.747C62.4132 303.479 61.387 302.452 60.1215 302.452C58.8559 302.452 57.8297 303.479 57.8297 304.747C57.8297 306.014 58.8559 307.041 60.1215 307.041V310.767C56.801 310.767 54.1093 308.072 54.1093 304.747C54.1093 301.422 56.801 298.727 60.1215 298.727C63.4419 298.727 66.1336 301.422 66.1336 304.747C66.1336 308.072 63.4419 310.767 60.1215 310.767V307.041C61.387 307.041 62.4132 306.014 62.4132 304.747Z" fill="#C272FF"/>
+ <path d="M359.727 133.175C363.047 133.175 365.739 135.87 365.739 139.195C365.739 142.52 363.047 145.215 359.727 145.215C356.406 145.215 353.715 142.52 353.715 139.195C353.715 135.87 356.406 133.175 359.727 133.175Z" fill="#C272FF"/>
+ <path d="M416.842 157.255C420.163 157.255 422.854 159.95 422.854 163.275C422.854 166.6 420.163 169.295 416.842 169.295C413.522 169.295 410.83 166.6 410.83 163.275C410.83 159.95 413.522 157.255 416.842 157.255Z" fill="#C272FF"/>
+ <path d="M101.492 281.67C101.492 280.403 100.466 279.375 99.2004 279.375C97.9348 279.375 96.9087 280.403 96.9087 281.67C96.9087 282.937 97.9348 283.965 99.2004 283.965V287.69C95.88 287.69 93.1883 284.995 93.1883 281.67C93.1883 278.345 95.88 275.65 99.2004 275.65C102.521 275.65 105.213 278.345 105.213 281.67C105.213 284.995 102.521 287.69 99.2004 287.69V283.965C100.466 283.965 101.492 282.937 101.492 281.67Z" fill="#C272FF"/>
+</svg>
=====================================
browser/components/abouttor/jar.mn
=====================================
@@ -9,3 +9,4 @@ browser.jar:
content/browser/abouttor/2728-sparkles.svg (content/2728-sparkles.svg)
content/browser/abouttor/2764-red-heart.svg (content/2764-red-heart.svg)
content/browser/abouttor/yec-heart.svg (content/yec-heart.svg)
+ content/browser/abouttor/yec2025-image.svg (content/yec2025-image.svg)
=====================================
toolkit/locales/en-US/toolkit/global/tor-browser.ftl
=====================================
@@ -728,3 +728,38 @@ yec-donate-button = Donate now
# Here "Close" is a verb, referring to closing the banner.
yec-close-button =
.title = Close
+
+## Summer 2026 Tor Project funding campaign.
+
+summer-2026-funding-heading = A new way to fund internet freedom
+# $tool1-name (string ) - The name of some internet freedom tool, this is not translated and will only use the Latin alphabet. This should be wrapped in '<b>' and '</b>' to make them appear bold. Similar for '$tool2-name'.
+# $tool1-description (string) - A short description for the tool, this is translated. Similar for '$tool2-description'.
+# Note, since '$tool1-description' and '$tool2-description' contain arbitrary text, you will likely need to separate them out from the rest of the text. The English text uses brackets '(' and ')' to do this.
+# Note, the curly brackets '{' and '}' are part of the Fluent syntax, but the normal brackets '(' and ')' are not.
+summer-2026-funding-intro = Did you know that internet freedom tools serve millions of people every day? This includes tools like <b>{ $tool1-name }</b> ({ $tool1-description }) and <b>{ $tool2-name }</b> ({ $tool2-description }).
+# Here "Tor" refers to The Tor Project.
+# You should wrap the call to donation in '<b>' and '</b>', which will make it stand out.
+summer-2026-funding-outro = But unprecedented funding cuts have harmed the small organizations that maintain these tools. <b>Help protect internet freedom by donating cryptocurrency</b>—all donations will be matched by friends of Tor.
+
+# A short description of "Onion Browser": onionbrowser.com .
+summer-2026-funding-tool-onion-browser-description = Tor-powered browser for iOS
+# A short description of "Quiet": tryquiet.org .
+summer-2026-funding-tool-quiet-description = private group messaging
+# A short description of "Ricochet Refresh": ricochetrefresh.net .
+summer-2026-funding-tool-ricochet-refresh-description = secure and anonymous messaging
+# A short description of "SecureDrop": securedrop.org .
+summer-2026-funding-tool-securedrop-description = anonymous whistleblowing app
+# A short description of "OnionShare": onionshare.org .
+summer-2026-funding-tool-onionshare-description = secure filesharing, hosting & chatting
+# A short description of "Digital Security Helpdesk": miaan.org/projects/miaan-digital-security-helpdesk .
+summer-2026-funding-tool-digital-security-helpdesk-description = internet freedom for Iranians
+# A short description of "Paskoocheh": paskoocheh.com .
+summer-2026-funding-tool-paskoocheh-description = countering digital authoritarianism
+# A short description of "Unredacted": unredacted.org .
+summer-2026-funding-tool-unredacted-description = keeping people connected, no matter where
+# A short description of "Osservatorio Nessuno": osservatorionessuno.org .
+summer-2026-funding-tool-osservatorio-nessuno-description = software that leaves no trace
+# A short description of "Save": open-archive.org/save .
+summer-2026-funding-tool-save-description = private archiving
+# A short description of "OONI": ooni.org .
+summer-2026-funding-tool-ooni-description = tracking censorship globally
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/927330…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/927330…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-150.0a1-16.0-2] 2 commits: fixup! TB 7494: Create local home page for TBB.
by henry (@henry) 29 Apr '26
by henry (@henry) 29 Apr '26
29 Apr '26
henry pushed to branch tor-browser-150.0a1-16.0-2 at The Tor Project / Applications / Tor Browser
Commits:
cba5ca86 by Henry Wilkes at 2026-04-28T18:12:10+00:00
fixup! TB 7494: Create local home page for TBB.
TB 44746: Modify YEC banner to be used for summer 2026 funding campaign.
- - - - -
e9280e8a by Henry Wilkes at 2026-04-28T18:12:10+00:00
fixup! TB 44746: Assets for summer 2026 funding campaign.
- - - - -
6 changed files:
- browser/components/abouttor/AboutTorParent.sys.mjs
- browser/components/abouttor/content/aboutTor.css
- browser/components/abouttor/content/aboutTor.html
- browser/components/abouttor/content/aboutTor.js
- + browser/components/abouttor/content/yec2025-image.svg
- browser/components/abouttor/jar.mn
Changes:
=====================================
browser/components/abouttor/AboutTorParent.sys.mjs
=====================================
@@ -23,6 +23,44 @@ export class AboutTorParent extends JSWindowActorParent {
*/
static #dismissYEC = false;
+ /**
+ * A shuffled array of tool numbers. `null` whilst unset.
+ *
+ * @type {?number[]}
+ */
+ static #toolNums = null;
+ /**
+ * The current index in `#toolNums`.
+ *
+ * @type {number}
+ */
+ static #toolIndex = 0;
+
+ /**
+ * Return the pair of tools numbers to show in the next about:tor window.
+ *
+ * @returns {{ toolNum1: number, toolNum2: number }} - The tool number pairs.
+ */
+ static #getToolNumPair() {
+ const numTools = 11;
+ if (!this.#toolNums) {
+ this.#toolNums = Array.from({ length: numTools }, (_, index) => index);
+ // Shuffle the array with Fisher–Yates.
+ for (let index = numTools - 1; index > 0; index--) {
+ const topBound = index + 1;
+ const exchange = Math.floor(Math.random() * topBound) % topBound;
+ const tmpNum = this.#toolNums[index];
+ this.#toolNums[index] = this.#toolNums[exchange];
+ this.#toolNums[exchange] = tmpNum;
+ }
+ }
+ const toolNum1 = this.#toolNums[this.#toolIndex % numTools];
+ const toolNum2 = this.#toolNums[(this.#toolIndex + 1) % numTools];
+ this.#toolIndex = (this.#toolIndex + 2) % numTools;
+
+ return { toolNum1, toolNum2 };
+ }
+
/**
* Whether this instance has a preloaded browser.
*
@@ -58,6 +96,8 @@ export class AboutTorParent extends JSWindowActorParent {
appLocale = "ja";
}
+ const { toolNum1, toolNum2 } = AboutTorParent.#getToolNumPair();
+
return {
torConnectEnabled: lazy.TorConnect.enabled,
messageData: lazy.AboutTorMessage.getNext(),
@@ -69,6 +109,8 @@ export class AboutTorParent extends JSWindowActorParent {
),
appLocale,
dismissYEC: AboutTorParent.#dismissYEC,
+ toolNum1,
+ toolNum2,
};
}
=====================================
browser/components/abouttor/content/aboutTor.css
=====================================
@@ -341,12 +341,11 @@ body.show-yec h1 {
"yec-matching yec-image" auto
"yec-donate yec-image" min-content
/ 1fr min-content;
- --yec-image-background: #1f0333;
- /* Remove 1px from padding for border. */
- padding-block: 55px 55px;
- padding-inline: 47px 47px;
+ --yec-image-background: #3a1653;
+ padding-block: 56px 56px;
+ padding-inline: 48px 48px;
box-sizing: border-box;
- max-width: 850px;
+ max-width: 840px;
margin-block-end: var(--space-small);
/* Position for the close button. */
position: relative;
@@ -380,8 +379,8 @@ body.show-yec h1 {
#yec-heading {
grid-area: yec-heading;
margin-block: 0 var(--space-large);
- font-size: 64px;
- font-weight: var(--font-weight);
+ font-size: var(--font-size-xxlarge);
+ font-weight: var(--font-weight-semibold);
}
#yec-body {
@@ -389,13 +388,13 @@ body.show-yec h1 {
margin-block: 0 var(--space-medium);
}
-#yec-body-highlight {
- font-weight: var(--font-weight-bold);
-}
-
#yec-matching {
grid-area: yec-matching;
- margin-block: 0 var(--space-xxlarge);
+ margin-block: 0 var(--space-xlarge);
+}
+
+#yec-banner b {
+ font-weight: var(--font-weight-semibold);
}
#yec-donate-link {
@@ -472,7 +471,7 @@ body.show-yec h1 {
}
#yec-heading {
- font-size: 50px;
+ font-size: var(--font-size-xlarge);
}
#yec-matching {
@@ -493,20 +492,16 @@ body.show-yec h1 {
@media not ((prefers-contrast) or (forced-colors)) {
#yec-banner {
- border-color: transparent;
+ border-color: #523168;
background-color: var(--yec-image-background);
--yec-text: #ffffff;
- --yec-button-background: #b6e368;
- --yec-button-background-hover: #d2f2a1;
- --yec-button-background-active: #ecfcd8;
- --yec-button-text: #15141a;
+ --yec-button-background: var(--button-background-color-tor-light);
+ --yec-button-background-hover: var(--button-background-color-tor-hover-light);
+ --yec-button-background-active: var(--button-background-color-tor-active-light);
+ --yec-button-text: var(--button-text-color-tor-light);
color: var(--yec-text);
}
- #yec-body-highlight {
- color: var(--yec-button-background);
- }
-
#yec-donate-link {
--button-text-color: var(--yec-button-text);
--button-text-color-hover: var(--yec-button-text);
=====================================
browser/components/abouttor/content/aboutTor.html
=====================================
@@ -35,13 +35,15 @@
<body class="onion-pattern-background">
<!-- Year End Campaign (YEC). -->
<article id="yec-banner" aria-labelledby="yec-heading">
- <img id="yec-image" alt="" />
- <h2 id="yec-heading"></h2>
- <p id="yec-body">
- <b id="yec-body-highlight" data-l10n-name="highlight"></b>
- </p>
- <p id="yec-matching"></p>
- <a id="yec-donate-link">
+ <img
+ id="yec-image"
+ alt=""
+ src="chrome://browser/content/abouttor/yec2025-image.svg"
+ />
+ <h2 id="yec-heading" data-l10n-id="summer-2026-funding-heading"></h2>
+ <p id="yec-body"></p>
+ <p id="yec-matching" data-l10n-id="summer-2026-funding-outro"></p>
+ <a id="yec-donate-link" target="_blank">
<span data-l10n-id="yec-donate-button"></span>
<img
id="yec-donate-icon"
=====================================
browser/components/abouttor/content/aboutTor.js
=====================================
@@ -431,14 +431,14 @@ const YecArea = {
*
* @type {?integer}
*/
- _startDate: null, // No YEC is active.
+ _startDate: Date.UTC(2026, 4, 19, 15), // 19th May 2026, 15 UTC.
/**
* The epoch time to stop showing the banner, if at all.
*
* @type {?integer}
*/
- _endDate: null, // No YEC is active.
+ _endDate: Date.UTC(2026, 5, 19, 0), // 19th June 2026, 0 UTC.
/**
* Whether the area has been initialised.
@@ -461,6 +461,72 @@ const YecArea = {
*/
_shouldShow: false,
+ /**
+ * The tool number to show first, or `null` whilst unset.
+ *
+ * @type {?number}
+ */
+ _toolNum1: null,
+
+ /**
+ * The tool number to show second, or `null` whilst unset.
+ *
+ * @type {?number}
+ */
+ _toolNum2: null,
+
+ /**
+ * The name and description data for each tool.
+ *
+ * @type {object[]}
+ */
+ _toolData: [
+ {
+ name: "Onion Browser",
+ descId: "summer-2026-funding-tool-onion-browser-description",
+ },
+ {
+ name: "Quiet",
+ descId: "summer-2026-funding-tool-quiet-description",
+ },
+ {
+ name: "Ricochet Refresh",
+ descId: "summer-2026-funding-tool-ricochet-refresh-description",
+ },
+ {
+ name: "SecureDrop",
+ descId: "summer-2026-funding-tool-securedrop-description",
+ },
+ {
+ name: "OnionShare",
+ descId: "summer-2026-funding-tool-onionshare-description",
+ },
+ {
+ name: "Digital Security Helpdesk",
+ descId: "summer-2026-funding-tool-digital-security-helpdesk-description",
+ },
+ {
+ name: "Paskoocheh",
+ descId: "summer-2026-funding-tool-paskoocheh-description",
+ },
+ {
+ name: "Unredacted",
+ descId: "summer-2026-funding-tool-unredacted-description",
+ },
+ {
+ name: "Osservatorio Nessuno",
+ descId: "summer-2026-funding-tool-osservatorio-nessuno-description",
+ },
+ {
+ name: "Save",
+ descId: "summer-2026-funding-tool-save-description",
+ },
+ {
+ name: "OONI",
+ descId: "summer-2026-funding-tool-ooni-description",
+ },
+ ],
+
/**
* The banner element.
*
@@ -498,8 +564,10 @@ const YecArea = {
* @param {boolean} dismissYEC - Whether the user has dismissed YEC.
* @param {boolean} isStable - Whether this is a stable release.
* @param {string} appLocale - The app locale, as BCP47.
+ * @param {number} toolNum1 - The number for the first tool to show.
+ * @param {number} toolNum2 - The number for the first tool to show.
*/
- potentiallyShow(dismissYEC, isStable, appLocale) {
+ potentiallyShow(dismissYEC, isStable, appLocale, toolNum1, toolNum2) {
const now = Date.now();
this._shouldShow =
!dismissYEC &&
@@ -508,13 +576,15 @@ const YecArea = {
now >= this._startDate &&
now < this._endDate;
this._locale = appLocale;
+ this._toolNum1 = toolNum1;
+ this._toolNum2 = toolNum2;
this._update();
},
/**
* Update the visibility of the banner to reflect the new state.
*/
- _update() {
+ async _update() {
if (!this._initialized) {
return;
}
@@ -526,8 +596,27 @@ const YecArea = {
return;
}
+ const tool1 = this._toolData[this._toolNum1];
+ const tool2 = this._toolData[this._toolNum2];
+
+ const [tool1Desc, tool2Desc] = await document.l10n.formatValues([
+ { id: tool1.descId },
+ { id: tool2.descId },
+ ]);
+
+ document.l10n.setAttributes(
+ document.getElementById("yec-body"),
+ "summer-2026-funding-intro",
+ {
+ "tool1-name": tool1.name,
+ "tool1-description": tool1Desc,
+ "tool2-name": tool2.name,
+ "tool2-description": tool2Desc,
+ }
+ );
+
const donateLink = document.getElementById("yec-donate-link");
- const base = "https://www.torproject.org/donate";
+ const base = "https://internetfreedom.torproject.org";
donateLink.href = base;
document.body.classList.add("show-yec");
@@ -574,11 +663,13 @@ window.addEventListener("InitialData", event => {
surveyDismissVersion,
appLocale,
dismissYEC,
+ toolNum1,
+ toolNum2,
} = event.detail;
SearchWidget.setOnionizeState(!!searchOnionize);
MessageArea.setMessageData(messageData, !!isStable, !!torConnectEnabled);
SurveyArea.potentiallyShow(surveyDismissVersion, isStable, appLocale);
- YecArea.potentiallyShow(dismissYEC, isStable, appLocale);
+ YecArea.potentiallyShow(dismissYEC, isStable, appLocale, toolNum1, toolNum2);
gInitialData = true;
maybeComplete();
=====================================
browser/components/abouttor/content/yec2025-image.svg
=====================================
@@ -0,0 +1,20 @@
+<svg width="495" height="381" viewBox="0 0 495 381" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M247.419 264.776C201.679 273.69 174.117 246.935 166.057 232.441L105.262 261.112L170.602 258.902L131.868 327.126L195.407 280.785L189.197 362.111L227.832 285.742L262.535 343.489L247.432 264.776H247.419Z" fill="#B6E368"/>
+ <path d="M223.938 98.8502C269.555 89.3133 297.484 115.702 305.741 130.086L366.144 100.584L300.841 103.686L338.631 34.949L275.741 82.1453L280.837 0.733295L243.255 77.6271L207.756 20.3445L223.938 98.8502Z" fill="#B6E368"/>
+ <g opacity="0.8">
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M256.349 131.796H260.465V136.119H256.141V132.016H247.921V136.119H252.025V140.222H256.141V148.648H260.465V157.293H256.141V148.867H251.817V140.442H247.701V136.119H243.597V132.016H239.273V165.499H260.477V157.293H264.801V165.499H277.137V169.822H285.577V174.145H276.928V169.822H264.814V207.408H277.149V203.305H285.797V207.408H289.913V211.731H285.797V216.053H281.681V220.376H277.357V224.479H273.033V228.582H269.138V232.905H264.814V228.582H268.709V224.259H273.033V220.156H277.357V216.053H281.473V211.731H285.589V207.628H277.149V211.731H264.814V220.364H260.489V228.79H256.165V232.893H264.814V237.215H251.829V228.582H256.153V220.156H260.477V211.731H239.273V237.215H251.817V241.099H222.393V237.215H209.629V232.893H205.525V228.57H201.409V224.467H197.085V220.364H192.981V216.041H197.306V220.144H201.409V224.247H205.733V228.57H209.849V232.893H218.289V228.79H213.965V220.364H209.849V211.731H197.085V207.628H188.645V211.731H184.321V207.408H180.217V169.822H184.541V207.408H188.657V203.305H197.306V207.408H209.849V169.822H197.306V174.145H188.657V169.822H184.541V161.188H188.657V152.763H192.981V148.66H197.306V144.557H201.409V140.454H205.525V136.131H214.173V140.454H205.733V144.777H201.618V148.88H197.293V152.983H192.969V161.408H188.853V169.834H197.073V165.511H209.837V157.305H214.161V165.511H234.924V132.028H231.029V136.131H226.705V132.028H218.485V136.131H214.161V131.808H218.277V127.705H256.349V131.808V131.796ZM214.161 220.132H218.277V228.558H222.601V237.191H234.937V211.706H214.173V220.132H214.161ZM239.261 207.396H260.465V169.81H239.261V207.396ZM214.161 207.396H234.924V169.81H214.161V207.396Z" fill="#C272FF"/>
+ <path d="M192.968 216.029H188.644V211.706H192.968V216.029Z" fill="#C272FF"/>
+ <path d="M294.212 207.395H289.888V169.809H294.212V207.395Z" fill="#C272FF"/>
+ <path d="M269.113 140.429H273.008V144.532H277.332V148.635H281.448V152.738H285.772V161.164H289.888V169.797H285.564V161.371H281.448V152.946H277.124V148.843H273.008V144.74H268.684V140.417H260.465V136.094H269.113V140.417V140.429Z" fill="#C272FF"/>
+ <path d="M226.718 140.429H222.602V148.855H218.486V157.281H214.162V148.635H218.278V140.209H222.394V136.106H226.718V140.429Z" fill="#C272FF"/>
+ <path d="M260.464 136.119V131.796H256.348V127.693H218.276V131.796H214.16V136.119M260.464 136.119H256.14V132.016H247.92V136.119H252.024V140.222H256.14V148.648H260.464V157.293M260.464 136.119V140.442H268.683V144.764H273.008V148.867H277.123V152.97H281.448V161.396H285.564V169.822M260.464 136.119H269.112V140.442H273.008V144.545H277.332V148.648H281.448V152.75H285.772V161.176H289.888V169.81M214.16 136.119H205.512V140.442H201.396V144.545H197.292V148.648H192.968V152.75H188.644V161.176H184.528V169.81M214.16 136.119V140.442H205.72V144.764H201.604V148.867H197.28V152.97H192.956V161.396H188.84V169.822H197.06V165.499H209.824V157.293H214.148M214.16 136.119H218.484V132.016H226.704V136.119M289.888 169.81H294.212V207.396H289.888M289.888 169.81V207.396M260.464 157.281H256.14V148.855H251.816V140.429H247.7V136.107H243.596V132.004H239.272V165.487H260.476V157.281H260.464ZM260.464 157.281H264.788V165.487H277.123V169.81H285.564M289.888 207.396H285.772V203.293H277.123V207.396H264.788V169.81H276.903V174.132H285.551V169.81H289.875M289.888 207.396V211.718H285.772V216.041H281.656V220.364H277.332V224.467H273.008V228.57H269.112V232.892H264.788M264.788 232.88V228.558H268.683V224.235H273.008V220.132H277.332V216.029H281.448V211.706H285.564V207.603H277.123V211.706H264.788V220.339H260.464V228.765H256.14V232.868H264.788V232.88ZM264.788 232.88V237.203H251.803M251.803 237.191V228.558H256.128V220.132H260.452V211.706H239.248V237.191H251.791H251.803ZM251.803 237.191V241.074H222.38V237.191H209.616V232.868H205.512V228.545H201.396V224.442H197.072V220.339H192.968V216.017M192.968 216.029H197.292V220.132H201.396V224.235H205.72V228.558H209.836V232.88H218.276V228.777H213.952V220.352H209.836V211.718H197.072V207.615H188.632V211.718M192.968 216.029H188.644V211.706H192.968V216.029ZM188.632 211.706H184.308V207.383H180.204V169.797H184.528M184.528 169.81V207.396H188.644V203.293H197.292V207.396H209.836V169.81H197.292V174.132H188.644V169.81H184.528ZM214.16 157.281V165.487H234.923V132.004H231.028V136.107H226.704M214.16 157.281H218.484V148.855H222.6V140.429H226.716V136.107H222.392V140.21H218.276V148.635H214.16V157.281ZM214.16 220.132H218.276V228.558H222.6V237.191H234.936V211.706H214.172V220.132H214.16ZM239.26 207.396H260.464V169.81H239.26V207.396ZM214.16 207.396H234.923V169.81H214.16V207.396Z" stroke="#C272FF" stroke-width="0.618845"/>
+ </g>
+ <path d="M166.566 131.845H175.239L169.665 124.591L179.881 122.381L170.633 111.794L178.264 111.587L178.607 109.511L127.183 90.5711C125.738 90.0338 124.28 89.5942 122.81 89.2401L97.6982 24.4963C92.2226 10.3802 78.3437 0.904321 63.1542 0.904321C58.6095 0.904321 54.1139 1.73468 49.8265 3.3954L23.6612 13.4696C4.62526 20.8208 -4.86823 42.227 2.49382 61.2032L38.3608 153.715C43.8364 167.831 57.7153 177.319 72.8926 177.319C74.9261 177.319 76.935 177.148 78.9195 176.807C82.7169 180.238 87.1513 182.937 92.1001 184.756L143.855 203.818L137.804 193.072H149.686L145.643 183.242H158.493L153.238 173.546L119.454 161.103C130.674 153.727 136.958 140.771 135.954 127.534L175.349 142.041L166.566 131.845ZM151.731 175.28L154.904 181.105H142.446L146.501 190.935H134.141L139.139 199.812L92.8473 182.753C83.1946 179.2 75.5263 172.117 71.2389 162.825C66.9638 153.544 66.5718 143.165 70.1365 133.591L76.9963 115.164C80.2914 106.324 86.551 99.412 94.2316 95.1259L104.509 121.649C103.799 122.528 103.211 123.541 102.794 124.665L95.9343 143.091C94.9175 145.827 95.0278 148.782 96.2527 151.419C97.4655 154.069 99.6581 156.096 102.402 157.11L151.731 175.28ZM65.6653 144.337C65.4938 144.032 65.3468 143.702 65.2121 143.36L29.3328 50.8481C28.2549 48.0884 28.9653 45.7316 29.4798 44.5837C30.0066 43.4237 31.2805 41.3111 34.049 40.2488L60.202 30.1623C61.182 29.7838 62.1742 29.6006 63.1542 29.6006C65.9716 29.6006 69.4382 31.2247 70.8469 34.8636L93.4476 93.1232C85.2158 97.6169 78.503 104.98 74.9873 114.42L68.1275 132.858C66.7311 136.595 65.9103 140.466 65.6653 144.337ZM104.803 125.397C104.975 124.921 105.195 124.469 105.44 124.054L106.726 127.363C107.804 130.135 107.094 132.48 106.579 133.628H106.567C106.065 134.8 104.803 136.9 102.022 137.963L99.8174 138.817L104.803 125.397ZM116.918 160.163L103.149 155.095C100.932 154.289 99.1804 152.665 98.2004 150.528C97.2205 148.403 97.1225 146.034 97.9432 143.836L98.8129 141.492L102.794 139.965C106.297 138.622 107.89 135.96 108.539 134.495C109.188 133.042 110.083 130.086 108.723 126.594L106.983 122.113V122.064L106.273 120.281L106.163 119.988L96.1303 94.1245L96.1058 94.0757L95.4565 92.3906L95.3585 92.1219L72.8436 34.0821C71.0674 29.5029 66.6943 27.4637 63.1542 27.4637C61.9169 27.4637 60.6552 27.6957 59.4303 28.1597L33.2772 38.2461C29.7983 39.5894 28.1814 42.2514 27.5321 43.7045C26.8706 45.1699 25.9764 48.1372 27.3361 51.6296L63.2154 144.142C63.7666 145.595 64.5996 146.804 65.5918 147.744C65.7021 153.202 66.9393 158.624 69.2912 163.716C71.2389 167.929 73.8359 171.702 76.9718 174.938C75.6121 175.109 74.2646 175.182 72.8926 175.182C58.5973 175.182 45.5146 166.244 40.3575 152.934L4.49051 60.4217C-2.4428 42.5567 6.49946 22.396 24.433 15.4723L50.5982 5.39804C54.6406 3.8228 58.8668 3.04128 63.1542 3.04128C77.4618 3.04128 90.5322 11.9677 95.7015 25.2779L120.299 88.6906L120.385 88.9226L121.156 90.913L121.218 91.0595L131.581 117.79C132.414 119.939 133.014 122.113 133.381 124.311C133.406 124.384 133.418 124.457 133.43 124.53C133.553 125.19 133.639 125.861 133.7 126.533C133.7 126.594 133.712 126.643 133.712 126.704C135.133 140.039 128.629 153.214 116.918 160.163ZM135.697 125.165C135.317 122.43 134.619 119.683 133.577 117.008L123.765 91.7067C124.66 91.9632 125.554 92.244 126.436 92.5737L172.666 109.608L166.039 109.791L175.888 121.062L165.904 123.212L170.902 129.708H161.899L168.33 137.181L135.697 125.165Z" fill="white"/>
+ <path d="M488.077 306.123L422.933 212.255C415.84 202.035 404.252 195.831 391.819 195.55C388.193 192.314 383.893 189.701 379.018 187.906L332.347 170.713L332.151 170.64L320.771 170.957L329.198 180.592L318.639 182.875L325.034 191.191H316.802L323.417 198.872L356.38 211.01L348.847 216.212C339.145 222.903 333.449 233.222 332.31 244.078L302.004 232.917L307.259 242.576H293.846L297.888 252.406H286.643L291.126 260.355L344.694 280.088C345.221 280.284 345.735 280.467 346.262 280.626L404.13 364.016C411.406 374.481 423.374 380.733 436.15 380.733C444.076 380.733 451.707 378.352 458.249 373.846L478.228 360.072C486.766 354.174 492.499 345.321 494.361 335.137C496.223 324.94 493.993 314.646 488.077 306.123ZM324.678 197.052L321.469 193.328H329.37L322.388 184.255L333.192 181.923L325.377 172.972L331.795 172.789L378.271 189.909C382.092 191.313 385.535 193.255 388.548 195.611C381.48 196.039 374.706 198.371 368.826 202.425L358.536 209.52L324.678 197.052ZM345.429 278.086L344.168 277.622L292.609 258.621L290.306 254.543H301.085L297.043 244.713H310.861L306.598 236.885L332.138 246.288L334.245 247.069L354.237 254.433C356.932 255.422 359.749 255.397 362.273 254.53C362.971 254.311 363.632 254.018 364.269 253.651C366.695 252.32 368.667 250.147 369.696 247.375L374.179 235.359L386.012 227.19C387.495 226.176 389.148 225.651 390.924 225.651C392.296 225.651 395.751 226.017 398.091 229.388L401.447 234.223C401.226 235.078 400.969 235.921 400.675 236.775L393.497 256.106L393.142 257.022C390.14 264.507 385.02 270.49 378.748 274.52C378.148 274.911 377.536 275.289 376.911 275.631C368.287 280.504 357.777 281.896 347.61 278.806C346.887 278.599 346.152 278.354 345.429 278.086ZM362.432 246.544C362.665 245.226 363.461 242.747 366.033 240.976L371.092 237.484L367.687 246.63C366.829 248.95 365.176 250.745 363.142 251.844C362.101 249.683 362.224 247.656 362.432 246.544ZM402.366 238.424L402.709 237.471C402.819 237.142 402.929 236.824 403.027 236.495L463.234 323.255C465.023 325.819 464.888 328.42 464.643 329.739C464.398 331.058 463.614 333.525 461.042 335.307L441.062 349.082C439.58 350.107 437.926 350.62 436.15 350.62C434.778 350.62 431.312 350.266 428.972 346.884L379.973 276.279C386.588 272.005 391.966 265.704 395.138 257.803L402.366 238.424ZM492.254 334.746C490.49 344.38 485.075 352.745 477.003 358.313L457.024 372.088C450.85 376.349 443.635 378.596 436.15 378.596C424.072 378.596 412.766 372.698 405.894 362.807L349.496 281.529C352.534 282.25 355.584 282.592 358.598 282.592C365.519 282.592 372.256 280.76 378.136 277.39L427.208 348.105C430.136 352.305 434.435 352.757 436.15 352.757C438.367 352.757 440.425 352.11 442.275 350.84L462.254 337.066C465.464 334.843 466.456 331.766 466.75 330.118C467.044 328.481 467.228 325.245 464.998 322.046L403.774 233.808L402.084 231.378L399.854 228.166C396.939 223.966 392.639 223.514 390.924 223.514H390.912C388.695 223.514 386.637 224.161 384.787 225.431L375.478 231.854L372.403 233.979L364.808 239.218C361.599 241.428 360.619 244.517 360.313 246.154C360.068 247.533 359.908 250.012 361.158 252.65C359.198 253.224 357.042 253.187 354.984 252.43L334.38 244.847C335.323 234.394 340.738 224.405 350.06 217.97L358.83 211.913L360.986 210.424L370.039 204.184C376.213 199.934 383.44 197.675 390.912 197.675H390.961C392.039 197.675 393.117 197.724 394.171 197.822C405.012 198.774 414.934 204.477 421.169 213.476L486.313 307.344C491.898 315.391 494.005 325.123 492.254 334.746Z" fill="white"/>
+ <path d="M82.7096 239.196L95.6423 239.231L95.6325 242.957L82.7096 242.922V259.141H78.9108V242.912L65.5817 242.877L65.5925 239.151L78.9108 239.186V223.021H82.7096V239.196Z" fill="#C272FF"/>
+ <path d="M410.924 116.181L423.856 116.216L423.846 119.937L410.924 119.902V136.101H407.125V119.892L393.796 119.857L393.806 116.136L407.125 116.171V100.028H410.924V116.181Z" fill="#C272FF"/>
+ <path d="M62.4132 304.747C62.4132 303.479 61.387 302.452 60.1215 302.452C58.8559 302.452 57.8297 303.479 57.8297 304.747C57.8297 306.014 58.8559 307.041 60.1215 307.041V310.767C56.801 310.767 54.1093 308.072 54.1093 304.747C54.1093 301.422 56.801 298.727 60.1215 298.727C63.4419 298.727 66.1336 301.422 66.1336 304.747C66.1336 308.072 63.4419 310.767 60.1215 310.767V307.041C61.387 307.041 62.4132 306.014 62.4132 304.747Z" fill="#C272FF"/>
+ <path d="M359.727 133.175C363.047 133.175 365.739 135.87 365.739 139.195C365.739 142.52 363.047 145.215 359.727 145.215C356.406 145.215 353.715 142.52 353.715 139.195C353.715 135.87 356.406 133.175 359.727 133.175Z" fill="#C272FF"/>
+ <path d="M416.842 157.255C420.163 157.255 422.854 159.95 422.854 163.275C422.854 166.6 420.163 169.295 416.842 169.295C413.522 169.295 410.83 166.6 410.83 163.275C410.83 159.95 413.522 157.255 416.842 157.255Z" fill="#C272FF"/>
+ <path d="M101.492 281.67C101.492 280.403 100.466 279.375 99.2004 279.375C97.9348 279.375 96.9087 280.403 96.9087 281.67C96.9087 282.937 97.9348 283.965 99.2004 283.965V287.69C95.88 287.69 93.1883 284.995 93.1883 281.67C93.1883 278.345 95.88 275.65 99.2004 275.65C102.521 275.65 105.213 278.345 105.213 281.67C105.213 284.995 102.521 287.69 99.2004 287.69V283.965C100.466 283.965 101.492 282.937 101.492 281.67Z" fill="#C272FF"/>
+</svg>
=====================================
browser/components/abouttor/jar.mn
=====================================
@@ -9,3 +9,4 @@ browser.jar:
content/browser/abouttor/2728-sparkles.svg (content/2728-sparkles.svg)
content/browser/abouttor/2764-red-heart.svg (content/2764-red-heart.svg)
content/browser/abouttor/yec-heart.svg (content/yec-heart.svg)
+ content/browser/abouttor/yec2025-image.svg (content/yec2025-image.svg)
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/424f8e…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/424f8e…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser-bundle-testsuite][main] Revert "Bug 40098: Update tmp_dir in rbm-config/tb-build-06.torproject.org.rbm.local.conf"
by boklm (@boklm) 29 Apr '26
by boklm (@boklm) 29 Apr '26
29 Apr '26
boklm pushed to branch main at The Tor Project / Applications / tor-browser-bundle-testsuite
Commits:
d544a6d2 by Nicolas Vigier at 2026-04-29T09:30:53+02:00
Revert "Bug 40098: Update tmp_dir in rbm-config/tb-build-06.torproject.org.rbm.local.conf"
This reverts commit f7f252d50c9848034ca0bfec47ddcc353d857af9.
See rbm#40105.
- - - - -
1 changed file:
- rbm-config/tb-build-06.torproject.org.rbm.local.conf
Changes:
=====================================
rbm-config/tb-build-06.torproject.org.rbm.local.conf
=====================================
@@ -10,7 +10,7 @@
### builds are made from this directory, so using a directory on a fast
### disk can improve build time. By default we are using a tmp directory
### under the tor-browser-build directory.
-tmp_dir: '[% IF c("link_input_files") %][% c("basedir") %][% END %]/tmp'
+tmp_dir: /tmp
### The debug option defines whether a debugging shell should be opened
### automatically in the build directory/container in case of build
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-bundle-testsuite…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-bundle-testsuite…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser-bundle-testsuite][main] Bug 40091: Fix indent in build result emails
by boklm (@boklm) 29 Apr '26
by boklm (@boklm) 29 Apr '26
29 Apr '26
boklm pushed to branch main at The Tor Project / Applications / tor-browser-bundle-testsuite
Commits:
d74b0cd1 by Nicolas Vigier at 2026-04-28T10:33:27+02:00
Bug 40091: Fix indent in build result emails
- - - - -
1 changed file:
- tmpl/report_tor-browser_build.txt
Changes:
=====================================
tmpl/report_tor-browser_build.txt
=====================================
@@ -8,8 +8,10 @@ Results
[% FOREACH test IN tbbfiles.$tbbfile.tests -%]
[% IF test.results -%]
[% test.name %]: [% test.results.success ? 'ok' : 'failed (' _ test.fail_type _ ')' %]
- [% IF test.type == 'rbm_build' -%]
+
+ [%- IF test.type == 'rbm_build' -%]
GitLab test pipeline: [% test.gitlab_pipeline_url ? test.gitlab_pipeline_url : 'failed run' %]
- [% END -%]
+
+ [%- END -%]
[% END -%]
[% END %]
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-bundle-testsuite…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-bundle-testsuite…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-140.10.1esr-15.0-1] 2 commits: Bug_44747: Add FtC strings
by clairehurst (@clairehurst) 28 Apr '26
by clairehurst (@clairehurst) 28 Apr '26
28 Apr '26
clairehurst pushed to branch tor-browser-140.10.1esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
259110bb by clairehurst at 2026-04-27T16:23:56-06:00
Bug_44747: Add FtC strings
We can drop this after the Fund the Commons campaign concludes
- - - - -
927330f8 by clairehurst at 2026-04-28T12:53:04-06:00
Bug_44747: Tor x FtC crowdfunding campaign
We can drop this after the Fund the Commons campaign concludes on 2026-06-19
- - - - -
8 changed files:
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/SearchBar.kt
- + mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/CampaignCompose.kt
- + mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorCampaignViewModel.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorHomePage.kt
- + mobile/android/fenix/app/src/main/res/drawable/globe_chain_burst_yec.xml
- + mobile/android/fenix/app/src/main/res/drawable/heart.xml
- mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml
Changes:
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
=====================================
@@ -161,6 +161,7 @@ import org.mozilla.fenix.wallpapers.Wallpaper
import org.mozilla.fenix.GleanMetrics.TabStrip as TabStripMetrics
import org.mozilla.fenix.components.toolbar.ToolbarPosition
+import org.mozilla.fenix.tor.TorCampaignViewModel
import org.mozilla.fenix.tor.TorHomePage
import org.mozilla.fenix.tor.UrlQuickLoadViewModel
@@ -179,6 +180,7 @@ class HomeFragment : Fragment(), UserInteractionHandler {
private val homeViewModel: HomeScreenViewModel by activityViewModels()
private val urlQuickLoadViewModel: UrlQuickLoadViewModel by activityViewModels()
+ private val torCampaignViewModel: TorCampaignViewModel by activityViewModels()
private var _bottomToolbarContainerView: BottomToolbarContainerView? = null
private val bottomToolbarContainerView: BottomToolbarContainerView
@@ -974,7 +976,16 @@ class HomeFragment : Fragment(), UserInteractionHandler {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
TorHomePage(
- toolBarAtTop = settings().toolbarPosition == ToolbarPosition.TOP
+ shouldInitiallyShowPromo = torCampaignViewModel.shouldInitiallyShowPromo,
+ toolBarAtTop = settings().toolbarPosition == ToolbarPosition.TOP,
+ toolPair = torCampaignViewModel.getToolPair(),
+ onClicked = {
+ (requireActivity() as HomeActivity).openToBrowserAndLoad(
+ searchTermOrURL = "https://internetfreedom.torproject.org",
+ newTab = true,
+ from = BrowserDirection.FromHome,
+ )
+ }
)
}
}
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/ui/SearchBar.kt
=====================================
@@ -34,7 +34,7 @@ internal fun SearchBar(
@Composable
@PreviewLightDark
-private fun SearchBarPreview() {
+fun SearchBarPreview() {
FirefoxTheme {
Column(
modifier = Modifier.background(color = FirefoxTheme.colors.layer1),
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/CampaignCompose.kt
=====================================
@@ -0,0 +1,310 @@
+package org.mozilla.fenix.tor
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.fromHtml
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import mozilla.components.ui.colors.PhotonColors
+import org.mozilla.fenix.R
+
+
+private val alternateLayoutThreshHold = 500.dp
+
+@Composable
+@CampaignComposePreview
+fun CampaignBox(
+ shouldShowPromo: MutableState<Boolean> = mutableStateOf(true),
+ onDonateButtonClicked: () -> Unit = {},
+ toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool> = Pair(TorCampaignViewModel.toolList[0], TorCampaignViewModel.toolList[1]),
+ ) {
+ BoxWithConstraints(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier
+ .fillMaxWidth()
+ .fillMaxHeight(),
+ ) {
+ val alternateLayout = this.maxWidth >= alternateLayoutThreshHold
+
+ CampaignLayout(
+ alternateLayout,
+ maxWidth = this.maxWidth,
+ shouldShowPromo,
+ onDonateButtonClicked = onDonateButtonClicked,
+ toolPair = toolPair,
+ )
+ }
+}
+
+@Composable
+private fun CampaignLayout(
+ alternateLayout: Boolean,
+ maxWidth: Dp,
+ shouldShowPromo: MutableState<Boolean>,
+ onDonateButtonClicked: () -> Unit,
+ toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool>,
+) {
+ Column(
+ modifier = Modifier
+ .padding(horizontal = 22.dp)
+ .fillMaxWidth(getVariableWidth(maxWidth))
+ .wrapContentHeight(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ PurpleBox(
+ alternateLayout,
+ shouldShowPromo,
+ onDonateButtonClicked = onDonateButtonClicked,
+ toolPair = toolPair,
+ )
+ Spacer(Modifier.size(8.dp))
+ Text(
+ text = stringResource(R.string.no_donation_required_yec),
+ style = TextStyle(
+ fontSize = 12.5.sp,
+ lineHeight = 18.75.sp,
+ fontFamily = FontFamily.SansSerif,
+ fontWeight = FontWeight(400),
+ color = PhotonColors.LightGrey05,
+ textAlign = TextAlign.Center,
+ ),
+ )
+ }
+}
+
+private fun getVariableWidth(width: Dp): Float =
+ (alternateLayoutThreshHold / width).coerceIn(0.80f, 1.0f)
+
+@Composable
+private fun PurpleBox(
+ alternateLayout: Boolean,
+ shouldShowPromo: MutableState<Boolean>,
+ onDonateButtonClicked: () -> Unit,
+ toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool>,
+) {
+ Box(
+ modifier = Modifier.background(
+ colorResource(mozilla.components.ui.colors.R.color.photonViolet90),
+ shape = RoundedCornerShape(8.dp),
+ ),
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .wrapContentHeight(),
+ horizontalArrangement = Arrangement.End,
+ ) {
+ ExitIcon(shouldShowPromo)
+ }
+ DynamicCampaignContent(
+ alternateLayout, onDonateButtonClicked = onDonateButtonClicked,
+ toolPair = toolPair
+ )
+ }
+}
+
+@Composable
+private fun ExitIcon(shouldShowYec: MutableState<Boolean>) {
+ IconButton(
+ modifier = Modifier.padding(8.dp),
+ onClick = {
+ shouldShowYec.value = false
+ },
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_close),
+ tint = PhotonColors.White,
+ contentDescription = stringResource(R.string.close_yec_button_description),
+ )
+ }
+}
+
+
+@Composable
+private fun DynamicCampaignContent(
+ alternateLayout: Boolean,
+ onDonateButtonClicked: () -> Unit,
+ toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool>,
+) {
+ @Composable
+ fun Icon(shouldShow: Boolean) {
+ if (shouldShow) {
+ Image(
+ painterResource(R.drawable.globe_chain_burst_yec),
+ contentDescription = null,
+ alignment = Alignment.Center,
+ )
+ }
+ }
+ Row(
+ modifier = Modifier
+ .padding(start = 16.dp, top = 32.dp, end = 16.dp, bottom = 24.dp)
+ .fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth(if (alternateLayout) 0.75f else 1.0f),
+ horizontalAlignment = if (alternateLayout) Alignment.Start else Alignment.CenterHorizontally,
+ ) {
+ Icon(shouldShow = !alternateLayout)
+ Spacer(Modifier.size(24.dp))
+ TitleText()
+ Spacer(Modifier.size(16.dp))
+ MainText( toolPair = toolPair )
+ Spacer(Modifier.size(24.dp))
+ Row(
+ horizontalArrangement = if (alternateLayout) Arrangement.Start else Arrangement.Center,
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ DonateButton(onDonateButtonClicked = onDonateButtonClicked, alternateLayout)
+ }
+ }
+ Icon(shouldShow = alternateLayout)
+ }
+}
+
+
+@Composable
+private fun TitleText() {
+ Text(
+ text = stringResource(R.string.summer_2026_funding_heading),
+ style = TextStyle(
+ fontSize = 24.sp,
+ lineHeight = 32.sp,
+ fontWeight = FontWeight(400),
+ color = PhotonColors.White,
+ textAlign = TextAlign.Center,
+ letterSpacing = 0.18.sp,
+ ),
+ )
+}
+
+
+@Composable
+private fun MainText(toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool>) {
+ Column {
+ Text(
+ AnnotatedString.fromHtml(
+ // Relevant documentation on HTML markup for android https://developer.android.com/guide/topics/resources/string-resource?utm_so…
+ stringResource(
+ R.string.summer_2026_funding_intro,
+ "<b>" + toolPair.first.name + "</b>",
+ stringResource(toolPair.first.description),
+ "<b>" + toolPair.second.name + "</b>",
+ stringResource(toolPair.second.description),
+ ),
+ ), style = TextStyle(
+ fontSize = 14.sp,
+ lineHeight = 20.sp,
+ color = PhotonColors.White,
+ fontWeight = FontWeight(400),
+ letterSpacing = 0.25.sp,
+ )
+ )
+ Spacer(Modifier.size(8.dp))
+ Text(
+ text = AnnotatedString.fromHtml(
+ stringResource(
+ R.string.summer_2026_funding_outro,
+ "<b>" + stringResource(R.string.summer_2026_call_to_donate) + "</b>"
+ )
+ ),
+ modifier = Modifier.fillMaxWidth(),
+ style = TextStyle(
+ fontSize = 14.sp,
+ lineHeight = 20.sp,
+ fontWeight = FontWeight(400),
+ letterSpacing = 0.25.sp,
+ color = PhotonColors.LightGrey05,
+ )
+ )
+ }
+}
+
+@Composable
+private fun DonateButton(onDonateButtonClicked: () -> Unit, alternateLayout: Boolean) {
+ Button(
+ onClick = onDonateButtonClicked,
+ colors = ButtonDefaults.buttonColors(
+ colorResource(mozilla.components.ui.colors.R.color.photonViolet60),
+ ),
+ shape = RoundedCornerShape(4.dp),
+ contentPadding = PaddingValues(horizontal = 16.dp),
+ modifier = if (alternateLayout) Modifier.wrapContentWidth() else Modifier.fillMaxWidth()
+ ) {
+ Image(
+ painterResource(R.drawable.heart),
+ contentDescription = null,
+ )
+ Spacer(
+ Modifier.size(8.dp),
+ )
+ Text(
+ text = stringResource(R.string.donate_now_yec),
+ textAlign = TextAlign.Center,
+ fontSize = 16.sp,
+ fontWeight = FontWeight.SemiBold,
+ color = PhotonColors.LightGrey05,
+ )
+ }
+}
+
+
+
+@Preview(
+ name = "Small Window",
+ widthDp = 400,
+)
+@Preview(
+ name = "Medium Window",
+ widthDp = 700,
+)
+@Preview(
+ name = "Large Window",
+ widthDp = 1000,
+)
+@Preview(
+ name = "RTL Small Window",
+ locale = "ar",
+ widthDp = 400,
+)
+@Preview(
+ name = "RTL Large Window",
+ locale = "ar",
+ widthDp = 1000
+)
+annotation class CampaignComposePreview
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorCampaignViewModel.kt
=====================================
@@ -0,0 +1,67 @@
+package org.mozilla.fenix.tor
+
+import android.app.Application
+import android.util.Log
+import androidx.annotation.StringRes
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.application
+import org.mozilla.fenix.R
+import org.mozilla.fenix.ext.components
+import java.text.SimpleDateFormat
+import java.util.Date
+
+class TorCampaignViewModel(application: Application) : AndroidViewModel(application) {
+
+ val shouldInitiallyShowPromo: MutableState<Boolean> by lazy {
+ mutableStateOf(shouldInitiallyShowPromo())
+ }
+
+ fun shouldInitiallyShowPromo(): Boolean {
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd-hh-zzz")
+ // From https://gitlab.torproject.org/tpo/applications/tor-browser/-/work_items/447…
+ val startDate = dateFormat.parse("2026-05-19-15-UTC")
+ val endDate = dateFormat.parse("2026-06-19-00-UTC")
+ val currentDate = Date()
+
+ if (currentDate.before(startDate) || currentDate.after(endDate)) {
+ return false
+ }
+ Log.d(
+ "TorCampaignViewModel",
+ "org.mozilla.fenix.BuildConfig.BUILD_TYPE = ${org.mozilla.fenix.BuildConfig.BUILD_TYPE}"
+ )
+ return (org.mozilla.fenix.BuildConfig.BUILD_TYPE == "release") || (org.mozilla.fenix.BuildConfig.BUILD_TYPE == "debug")
+ }
+
+ companion object {
+ val toolList: List<Tool> = listOf(
+ Tool(name = "Onion Browser", description = R.string.summer_2026_funding_tool_onion_browser_description),
+ Tool(name = "Quiet", description = R.string.summer_2026_funding_tool_quiet_description),
+ Tool(name = "Ricochet Refresh", description = R.string.summer_2026_funding_tool_ricochet_refresh_description),
+ Tool(name = "SecureDrop", description = R.string.summer_2026_funding_tool_securedrop_description),
+ Tool(name = "OnionShare", description = R.string.summer_2026_funding_tool_onionshare_description),
+ Tool(name = "Digital Security Helpdesk", description = R.string.summer_2026_funding_tool_digital_security_helpdesk_description),
+ Tool(name = "Paskoocheh", description = R.string.summer_2026_funding_tool_paskoocheh_description),
+ Tool(name = "Unredacted", description = R.string.summer_2026_funding_tool_unredacted_description),
+ Tool(name = "Osservatorio Nessuno", description = R.string.summer_2026_funding_tool_osservatorio_nessuno_description),
+ Tool(name = "Save", description = R.string.summer_2026_funding_tool_save_description),
+ Tool(name = "OONI", description = R.string.summer_2026_funding_tool_ooni_description),
+ ).shuffled()
+ }
+
+ private var toolIndex = 0
+ private fun incrementToolIndex() {
+ toolIndex = (toolIndex + 2) % toolList.size
+ }
+
+ data class Tool(
+ val name: String,
+ @param:StringRes val description: Int,
+ )
+
+ fun getToolPair() : Pair<Tool, Tool> {
+ return Pair(toolList[toolIndex], toolList[(toolIndex + 1) % toolList.size]).also { incrementToolIndex() }
+ }
+}
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorHomePage.kt
=====================================
@@ -1,6 +1,7 @@
package org.mozilla.fenix.tor
import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -12,6 +13,10 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.paint
@@ -26,16 +31,33 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview
import org.mozilla.fenix.R
+import org.mozilla.fenix.home.ui.SearchBarPreview
@Composable
-@FlexibleWindowLightDarkPreview
fun TorHomePage(
- toolBarAtTop: Boolean = true,
+ shouldInitiallyShowPromo: MutableState<Boolean> = mutableStateOf(true),
+ onClicked: () -> Unit = {},
+ toolBarAtTop: Boolean = false,
+ toolPair: Pair<TorCampaignViewModel.Tool, TorCampaignViewModel.Tool>,
) {
+ // Will persist across a single session, but not multiple sessions.
+ // Tapping the close button 'X' will hide the promo for the duration of the session
+ val shouldShowPromo = rememberSaveable {
+ shouldInitiallyShowPromo
+ }
+
+ // Will persist through screen rotations, but not navigations (e.g. Tap on settings -> come back will update)
+ // Is expected to change with every new visit to about:tor
+ val toolPair = remember {
+ toolPair
+ }
+
Column(
modifier = Modifier
.fillMaxSize()
@@ -82,30 +104,78 @@ fun TorHomePage(
)
}
Spacer(Modifier.weight(1f))
- Text(
- // Moved from the commit 5bb3cc6b93346dabd8d46677fae7f86a8f8a4fc2
- // "[android] Modify UI/UX", and the file HomeFragment.
- // Splits by full stops or commas and puts the parts in different lines.
- // Ignoring separators at the end of the string, it is expected
- // that there are at most two parts (e.g. "Explore. Privately.").
- text = stringResource(R.string.tor_explore_privately).replace(
+ if (shouldShowPromo.value) {
+ CampaignBox(
+ shouldShowPromo,
+ onDonateButtonClicked = onClicked,
+ toolPair = toolPair
+ )
+ Spacer(Modifier.weight(1f))
+ } else {
+ Text(
+ // Moved from the commit 5bb3cc6b93346dabd8d46677fae7f86a8f8a4fc2
+ // "[android] Modify UI/UX", and the file HomeFragment.
+ // Splits by full stops or commas and puts the parts in different lines.
+ // Ignoring separators at the end of the string, it is expected
+ // that there are at most two parts (e.g. "Explore. Privately.").
+ text = stringResource(R.string.tor_explore_privately).replace(
" *([.,。।]) *".toRegex(),
"$1\n",
).trim(),
- style = TextStyle(
- color = Color(color = 0xDEFFFFFF),
- fontSize = 40.sp,
- textAlign = TextAlign.Start,
- ),
- modifier = Modifier.align(Alignment.CenterHorizontally),
- )
- Spacer(Modifier.weight(1f))
- Image(
- painter = painterResource(
- id = R.drawable.ic_onion_pattern,
- ),
- contentDescription = null, Modifier.fillMaxWidth(),
- )
+ style = TextStyle(
+ color = Color(color = 0xDEFFFFFF),
+ fontSize = 40.sp,
+ textAlign = TextAlign.Start,
+ ),
+ modifier = Modifier.align(Alignment.CenterHorizontally),
+ )
+ }
+ if (!shouldShowPromo.value) {
+ Spacer(Modifier.weight(1f))
+ Image(
+ painter = painterResource(
+ id = R.drawable.ic_onion_pattern,
+ ),
+ contentDescription = null, Modifier.fillMaxWidth(),
+ )
+ }
}
Spacer(modifier = Modifier.size(17.dp))
}
+
+@Composable
+@Preview
+/**
+ * Relevant documentation
+ * https://developer.android.com/develop/ui/compose/tooling/previews#preview-v…
+ */
+private fun TorHomePagePreview(
+ @PreviewParameter(
+ BooleanBooleanPreviewParameterProvider::class,
+ ) booleanMatrix: Pair<Boolean, Boolean>,
+) {
+ val toolbarAtTop = booleanMatrix.second
+ Box(
+ contentAlignment = if (toolbarAtTop) Alignment.TopStart else Alignment.BottomStart,
+ modifier = Modifier.fillMaxSize(),
+ ) {
+ SearchBarPreview() // unrestricted vertically so will follow contentAlignment
+ TorHomePage(
+ // restricted vertically so will not follow contentAlignment
+ shouldInitiallyShowPromo = mutableStateOf(booleanMatrix.first),
+ toolBarAtTop = toolbarAtTop,
+ toolPair = Pair(TorCampaignViewModel.toolList[0], TorCampaignViewModel.toolList[1]),
+ )
+ }
+}
+
+private class BooleanBooleanPreviewParameterProvider :
+ PreviewParameterProvider<Pair<Boolean, Boolean>> {
+ override val values: Sequence<Pair<Boolean, Boolean>>
+ get() = sequenceOf(
+ Pair(true, true),
+ Pair(true, false),
+ Pair(false, true),
+ Pair(false, false),
+ )
+}
=====================================
mobile/android/fenix/app/src/main/res/drawable/globe_chain_burst_yec.xml
=====================================
@@ -0,0 +1,35 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="124dp" android:viewportHeight="124" android:viewportWidth="160" android:width="160dp">
+
+ <path android:fillColor="#B6E368" android:pathData="M79.97,86.16C65.19,89.07 56.28,80.34 53.67,75.61L34.02,84.97L55.14,84.24L42.62,106.51L63.16,91.39L61.15,117.92L73.64,93L84.86,111.85L79.98,86.16H79.97Z"/>
+
+ <path android:fillColor="#B6E368" android:pathData="M72.38,32.02C87.13,28.91 96.16,37.52 98.83,42.21L118.35,32.58L97.24,33.6L109.46,11.17L89.13,26.57L90.78,0L78.63,25.09L67.15,6.4L72.38,32.02Z"/>
+
+ <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:fillType="evenOdd" android:pathData="M82.86,42.77H84.19V44.18H82.79V42.84H80.14V44.18H81.46V45.52H82.79V48.27H84.19V51.09H82.79V48.34H81.4V45.59H80.06V44.18H78.74V42.84H77.34V53.77H84.19V51.09H85.59V53.77H89.58V55.18H92.31V56.59H89.51V55.18H85.6V67.44H89.58V66.1H92.38V67.44H93.71V68.85H92.38V70.26H91.05V71.67H89.65V73.01H88.25V74.35H86.99V75.76H85.6V74.35H86.86V72.94H88.25V71.6H89.65V70.26H90.98V68.85H92.31V67.51H89.58V68.85H85.6V71.67H84.2V74.42H82.8V75.76H85.6V77.17H81.4V74.35H82.8V71.6H84.19V68.85H77.34V77.17H81.4V78.43H71.88V77.17H67.76V75.76H66.43V74.35H65.1V73.01H63.7V71.67H62.38V70.26H63.78V71.6H65.1V72.94H66.5V74.35H67.83V75.76H70.56V74.42H69.16V71.67H67.83V68.85H63.7V67.51H60.98V68.85H59.58V67.44H58.25V55.18H59.65V67.44H60.98V66.1H63.78V67.44H67.83V55.18H63.78V56.59H60.98V55.18H59.65V52.36H60.98V49.61H62.38V48.27H63.78V46.93H65.1V45.59H66.43V44.18H69.23V45.59H66.5V47H65.17V48.34H63.77V49.68H62.37V52.43H61.04V55.18H63.7V53.77H67.83V51.09H69.22V53.77H75.94V42.84H74.68V44.18H73.28V42.84H70.62V44.18H69.22V42.77H70.55V41.43H82.86V42.77V42.77ZM69.22,71.59H70.55V74.34H71.95V77.16H75.94V68.84H69.23V71.59H69.22ZM77.34,67.44H84.19V55.17H77.34V67.44ZM69.22,67.44H75.94V55.17H69.22V67.44Z" android:strokeAlpha="0.8"/>
+
+ <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M62.37,70.25H60.98V68.84H62.37V70.25Z" android:strokeAlpha="0.8"/>
+
+ <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M95.1,67.44H93.7V55.17H95.1V67.44Z" android:strokeAlpha="0.8"/>
+
+ <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M86.99,45.58H88.25V46.92H89.64V48.26H90.97V49.6H92.37V52.35H93.7V55.17H92.3V52.42H90.97V49.67H89.58V48.33H88.25V46.99H86.85V45.58H84.19V44.17H86.99V45.58V45.58Z" android:strokeAlpha="0.8"/>
+
+ <path android:fillAlpha="0.8" android:fillColor="#C272FF" android:pathData="M73.28,45.58H71.95V48.33H70.62V51.08H69.22V48.26H70.55V45.51H71.88V44.17H73.28V45.58Z" android:strokeAlpha="0.8"/>
+
+ <path android:fillAlpha="0.8" android:fillColor="#00000000" android:pathData="M84.19,44.18V42.77H82.86V41.43H70.55V42.77H69.22V44.18M84.19,44.18H82.79V42.84H80.14V44.18H81.46V45.52H82.79V48.27H84.19V51.09M84.19,44.18V45.59H86.85V47H88.25V48.34H89.58V49.68H90.97V52.43H92.3V55.18M84.19,44.18H86.99V45.59H88.25V46.93H89.64V48.27H90.97V49.61H92.37V52.35H93.7V55.17M69.22,44.18H66.43V45.59H65.1V46.93H63.77V48.27H62.37V49.61H60.98V52.35H59.65V55.17M69.22,44.18V45.59H66.5V47H65.17V48.34H63.77V49.68H62.37V52.43H61.04V55.18H63.7V53.77H67.82V51.09H69.22M69.22,44.18H70.62V42.84H73.28V44.18M93.7,55.17H95.1V67.44H93.7M93.7,55.17V67.44M84.19,51.08H82.79V48.33H81.4V45.58H80.06V44.17H78.74V42.84H77.34V53.76H84.19V51.08H84.19ZM84.19,51.08H85.59V53.76H89.58V55.17H92.3M93.7,67.44H92.37V66.1H89.58V67.44H85.59V55.17H89.5V56.58H92.3V55.17H93.7M93.7,67.44V68.85H92.37V70.26H91.04V71.67H89.64V73.01H88.25V74.35H86.99V75.76H85.59M85.59,75.75V74.34H86.85V72.93H88.25V71.59H89.64V70.25H90.97V68.84H92.3V67.5H89.58V68.84H85.59V71.66H84.19V74.41H82.79V75.75H85.59V75.75ZM85.59,75.75V77.16H81.39M81.39,77.16V74.34H82.79V71.59H84.19V68.84H77.33V77.16H81.39H81.39ZM81.39,77.16V78.43H71.88V77.16H67.75V75.75H66.43V74.34H65.1V73H63.7V71.66H62.37V70.25M62.37,70.25H63.77V71.59H65.1V72.93H66.5V74.34H67.83V75.75H70.55V74.41H69.16V71.66H67.83V68.85H63.7V67.51H60.97V68.85M62.37,70.25H60.98V68.84H62.37V70.25ZM60.97,68.84H59.57V67.43H58.25V55.17H59.65M59.65,55.17V67.44H60.98V66.1H63.77V67.44H67.83V55.17H63.77V56.58H60.98V55.17H59.65ZM69.22,51.08V53.76H75.93V42.84H74.68V44.17H73.28M69.22,51.08H70.62V48.33H71.95V45.58H73.28V44.17H71.88V45.51H70.55V48.26H69.22V51.08ZM69.22,71.59H70.55V74.34H71.95V77.16H75.94V68.84H69.23V71.59H69.22ZM77.34,67.44H84.19V55.17H77.34V67.44ZM69.22,67.44H75.93V55.17H69.22V67.44Z" android:strokeAlpha="0.8" android:strokeColor="#C272FF" android:strokeWidth="0.23451"/>
+
+ <path android:fillColor="#ffffff" android:pathData="M53.84,42.78H56.64L54.84,40.42L58.14,39.7L55.15,36.24L57.62,36.17L57.73,35.5L41.11,29.32C40.64,29.14 40.17,29 39.7,28.88L31.58,7.75C29.81,3.15 25.32,0.06 20.41,0.06C18.94,0.06 17.49,0.33 16.11,0.87L7.65,4.16C1.5,6.55 -1.57,13.54 0.81,19.73L12.4,49.92C14.17,54.53 18.66,57.62 23.56,57.62C24.22,57.62 24.87,57.57 25.51,57.46C26.74,58.58 28.17,59.46 29.77,60.05L46.5,66.27L44.54,62.76H48.38L47.08,59.56H51.23L49.53,56.39L38.61,52.33C42.24,49.92 44.27,45.7 43.94,41.38L56.68,46.11L53.84,42.78ZM49.04,56.96L50.07,58.86H46.04L47.35,62.07H43.36L44.97,64.96L30.01,59.4C26.89,58.24 24.41,55.93 23.03,52.89C21.64,49.86 21.52,46.48 22.67,43.35L24.89,37.34C25.95,34.46 27.98,32.2 30.46,30.8L33.78,39.46C33.55,39.74 33.36,40.07 33.23,40.44L31.01,46.45C30.68,47.35 30.72,48.31 31.11,49.17C31.5,50.04 32.21,50.7 33.1,51.03L49.04,56.96ZM21.23,46.86C21.17,46.76 21.12,46.65 21.08,46.54L9.48,16.35C9.13,15.45 9.36,14.68 9.53,14.31C9.7,13.93 10.11,13.24 11.01,12.89L19.46,9.6C19.78,9.48 20.1,9.42 20.41,9.42C21.32,9.42 22.44,9.95 22.9,11.14L30.21,30.15C27.54,31.61 25.37,34.02 24.24,37.1L22.02,43.11C21.57,44.33 21.3,45.6 21.23,46.86ZM33.88,40.68C33.93,40.52 34,40.38 34.08,40.24L34.5,41.32C34.85,42.23 34.62,42.99 34.45,43.37H34.45C34.28,43.75 33.88,44.43 32.98,44.78L32.26,45.06L33.88,40.68ZM37.79,52.02L33.34,50.37C32.62,50.11 32.06,49.58 31.74,48.88C31.42,48.19 31.39,47.41 31.66,46.7L31.94,45.93L33.23,45.43C34.36,45 34.87,44.13 35.08,43.65C35.29,43.17 35.58,42.21 35.14,41.07L34.58,39.61V39.59L34.35,39.01L34.32,38.91L31.07,30.48L31.06,30.46L30.85,29.91L30.82,29.82L23.55,10.88C22.97,9.39 21.56,8.72 20.41,8.72C20.01,8.72 19.61,8.8 19.21,8.95L10.76,12.24C9.63,12.68 9.11,13.55 8.9,14.02C8.69,14.5 8.4,15.47 8.84,16.61L20.43,46.8C20.61,47.27 20.88,47.67 21.2,47.97C21.24,49.75 21.64,51.52 22.4,53.18C23.03,54.56 23.87,55.79 24.88,56.85C24.44,56.9 24,56.93 23.56,56.93C18.94,56.93 14.71,54.01 13.04,49.67L1.45,19.48C-0.79,13.65 2.1,7.07 7.9,4.81L16.35,1.52C17.66,1.01 19.03,0.75 20.41,0.75C25.04,0.75 29.26,3.67 30.93,8.01L38.88,28.7L38.91,28.78L39.16,29.43L39.18,29.47L42.53,38.2C42.8,38.9 42.99,39.61 43.11,40.33C43.12,40.35 43.13,40.37 43.13,40.4C43.17,40.61 43.2,40.83 43.22,41.05C43.22,41.07 43.22,41.09 43.22,41.11C43.68,45.46 41.58,49.76 37.79,52.02ZM43.86,40.6C43.74,39.71 43.51,38.81 43.18,37.94L40.01,29.69C40.29,29.77 40.58,29.86 40.87,29.97L55.81,35.53L53.67,35.59L56.85,39.27L53.63,39.97L55.24,42.09H52.33L54.41,44.53L43.86,40.6Z"/>
+
+ <path android:fillColor="#ffffff" android:pathData="M157.76,99.65L136.71,69.02C134.41,65.69 130.67,63.66 126.65,63.57C125.48,62.52 124.09,61.66 122.51,61.08L107.43,55.47L107.36,55.44L103.68,55.55L106.41,58.69L102.99,59.44L105.06,62.15H102.4L104.54,64.66L115.19,68.62L112.76,70.31C109.62,72.5 107.78,75.86 107.41,79.41L97.62,75.77L99.32,78.92H94.98L96.29,82.12H92.65L94.1,84.72L111.42,91.16C111.59,91.22 111.75,91.28 111.92,91.33L130.63,118.54C132.98,121.96 136.85,124 140.98,124C143.54,124 146.01,123.22 148.12,121.75L154.58,117.26C157.34,115.33 159.19,112.44 159.79,109.12C160.4,105.79 159.68,102.43 157.76,99.65ZM104.95,64.06L103.91,62.85H106.46L104.21,59.89L107.7,59.13L105.17,56.2L107.25,56.14L122.27,61.73C123.5,62.19 124.62,62.82 125.59,63.59C123.31,63.73 121.12,64.49 119.22,65.82L115.89,68.13L104.95,64.06ZM111.65,90.5L111.25,90.35L94.58,84.15L93.84,82.82H97.32L96.01,79.61H100.48L99.1,77.06L107.36,80.13L108.04,80.38L114.5,82.79C115.37,83.11 116.28,83.1 117.1,82.82C117.32,82.75 117.54,82.65 117.74,82.53C118.53,82.1 119.17,81.39 119.5,80.48L120.95,76.56L124.77,73.9C125.25,73.57 125.79,73.39 126.36,73.39C126.8,73.39 127.92,73.51 128.68,74.61L129.76,76.19C129.69,76.47 129.61,76.75 129.51,77.02L127.19,83.33L127.08,83.63C126.11,86.07 124.45,88.03 122.42,89.34C122.23,89.47 122.03,89.59 121.83,89.7C119.04,91.29 115.64,91.75 112.36,90.74C112.13,90.67 111.89,90.59 111.65,90.5ZM117.15,80.21C117.22,79.78 117.48,78.97 118.31,78.39L119.95,77.26L118.85,80.24C118.57,81 118.04,81.58 117.38,81.94C117.04,81.24 117.08,80.57 117.15,80.21ZM130.06,77.56L130.17,77.25C130.2,77.14 130.24,77.04 130.27,76.93L149.73,105.24C150.31,106.08 150.27,106.93 150.19,107.36C150.11,107.79 149.85,108.6 149.02,109.18L142.57,113.67C142.09,114.01 141.55,114.17 140.98,114.17C140.53,114.17 139.41,114.06 138.66,112.95L122.82,89.91C124.96,88.52 126.7,86.46 127.72,83.89L130.06,77.56ZM159.11,108.99C158.54,112.14 156.79,114.87 154.18,116.68L147.73,121.18C145.73,122.57 143.4,123.3 140.98,123.3C137.07,123.3 133.42,121.38 131.2,118.15L112.97,91.63C113.95,91.86 114.94,91.97 115.91,91.97C118.15,91.97 120.32,91.38 122.23,90.28L138.09,113.35C139.03,114.72 140.42,114.87 140.98,114.87C141.7,114.87 142.36,114.66 142.96,114.25L149.42,109.75C150.45,109.03 150.77,108.02 150.87,107.48C150.96,106.95 151.02,105.89 150.3,104.85L130.51,76.06L129.97,75.26L129.25,74.22C128.3,72.84 126.91,72.7 126.36,72.7H126.36C125.64,72.7 124.97,72.91 124.38,73.32L121.37,75.42L120.37,76.11L117.92,77.82C116.88,78.54 116.56,79.55 116.46,80.08C116.39,80.53 116.33,81.34 116.74,82.2C116.1,82.39 115.41,82.38 114.74,82.13L108.08,79.66C108.39,76.25 110.14,72.99 113.15,70.89L115.99,68.91L116.68,68.43L119.61,66.39C121.6,65 123.94,64.27 126.36,64.27H126.37C126.72,64.27 127.07,64.28 127.41,64.31C130.91,64.62 134.12,66.48 136.13,69.42L157.19,100.05C159,102.68 159.68,105.85 159.11,108.99Z"/>
+
+ <path android:fillColor="#C272FF" android:pathData="M26.73,77.81L30.91,77.83L30.91,79.04L26.73,79.03V84.32H25.51V79.03L21.2,79.02L21.2,77.8L25.51,77.81V72.54H26.73V77.81Z"/>
+
+ <path android:fillColor="#C272FF" android:pathData="M132.82,37.87L137,37.88L137,39.08L132.82,39.07V44.31H131.6V39.07L127.29,39.06L127.29,37.85L131.6,37.86V32.65H132.82V37.87Z"/>
+
+ <path android:fillColor="#C272FF" android:pathData="M20.17,99.2C20.17,98.79 19.84,98.46 19.43,98.46C19.02,98.46 18.69,98.79 18.69,99.2C18.69,99.62 19.02,99.95 19.43,99.95V101.17C18.36,101.17 17.49,100.29 17.49,99.2C17.49,98.12 18.36,97.24 19.43,97.24C20.51,97.24 21.38,98.12 21.38,99.2C21.38,100.29 20.51,101.17 19.43,101.17V99.95C19.84,99.95 20.17,99.62 20.17,99.2Z"/>
+
+ <path android:fillColor="#C272FF" android:pathData="M116.28,43.22C117.35,43.22 118.22,44.1 118.22,45.18C118.22,46.27 117.35,47.15 116.28,47.15C115.2,47.15 114.33,46.27 114.33,45.18C114.33,44.1 115.2,43.22 116.28,43.22Z"/>
+
+ <path android:fillColor="#C272FF" android:pathData="M134.74,51.08C135.81,51.08 136.68,51.96 136.68,53.04C136.68,54.12 135.81,55 134.74,55C133.66,55 132.79,54.12 132.79,53.04C132.79,51.96 133.66,51.08 134.74,51.08Z"/>
+
+ <path android:fillColor="#C272FF" android:pathData="M32.81,91.67C32.81,91.26 32.47,90.93 32.06,90.93C31.66,90.93 31.32,91.26 31.32,91.67C31.32,92.09 31.66,92.42 32.06,92.42V93.64C30.99,93.64 30.12,92.76 30.12,91.67C30.12,90.59 30.99,89.71 32.06,89.71C33.14,89.71 34.01,90.59 34.01,91.67C34.01,92.76 33.14,93.64 32.06,93.64V92.42C32.47,92.42 32.81,92.09 32.81,91.67Z"/>
+
+</vector>
=====================================
mobile/android/fenix/app/src/main/res/drawable/heart.xml
=====================================
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="16dp" android:viewportHeight="16" android:viewportWidth="16" android:width="16dp">
+
+ <path android:fillColor="#FBFBFE" android:pathData="M8,6C8,6 8,2 11.5,2C15,2 15,5 15,6C15,10.5 8,15 8,15V6Z"/>
+
+ <path android:fillColor="#FBFBFE" android:pathData="M8,6C8,6 8,2 4.5,2C1,2 1,5 1,6C1,10.5 8,15 8,15L9,9L8,6Z"/>
+
+</vector>
=====================================
mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml
=====================================
@@ -153,4 +153,36 @@
<!-- Year End Campaign. Here "Close" is a verb, referring to closing the banner. This is used primarily for screen readers to give the small "x" button a name. -->
<string name="close_yec_button_description">Close</string>
+ <!-- Summer 2026 Tor Project funding campaign. -->
+
+ <string name="summer_2026_funding_heading">A new way to fund internet freedom</string>
+ <!-- '%1$s' will be replaced with the name of some internet freedom tool, this is not translated and will only use the Latin alphabet, and will be made to appear bold. Similarly, '%3$s' will be replaced with the name of some other internet freedom tool. '%2$s' and '%4$s' will be replaced with the respective descriptions for the tools, this is translated. Note, since '%2$s' and '%4$s' will contain arbitrary text, you will likely need to separate them out from the rest of the text. The English text uses brackets '(' and ')' to do this. -->
+ <string name="summer_2026_funding_intro">Did you know that internet freedom tools serve millions of people every day? This includes tools like %1$s (%2$s) and %3$s (%4$s).</string>
+ <!-- '%s' will be replaced with summer_2026_call_to_donate and will be made bold. Here "Tor" refers to The Tor Project. -->
+ <string name="summer_2026_funding_outro">But unprecedented funding cuts have harmed the small organizations that maintain these tools. %s—all donations will be matched by friends of Tor.</string>
+ <!-- This will replace '%s' in summer_2026_funding_outro and will be made bold -->
+ <string name="summer_2026_call_to_donate">Help protect internet freedom by donating cryptocurrency</string>
+ <!-- A short description of "Onion Browser": onionbrowser.com -->
+ <string name="summer_2026_funding_tool_onion_browser_description">Tor-powered browser for iOS</string>
+ <!-- A short description of "Quiet": tryquiet.org -->
+ <string name="summer_2026_funding_tool_quiet_description">private group messaging</string>
+ <!-- A short description of "Ricochet Refresh": ricochetrefresh.net -->
+ <string name="summer_2026_funding_tool_ricochet_refresh_description">secure and anonymous messaging</string>
+ <!-- A short description of "SecureDrop": securedrop.org -->
+ <string name="summer_2026_funding_tool_securedrop_description">anonymous whistleblowing app</string>
+ <!-- A short description of "OnionShare": onionshare.org. "&" shows up as "&" and means "and" in english. -->
+ <string name="summer_2026_funding_tool_onionshare_description">secure filesharing, hosting & chatting</string>
+ <!-- A short description of "Digital Security Helpdesk": miaan.org/projects/miaan-digital-security-helpdesk -->
+ <string name="summer_2026_funding_tool_digital_security_helpdesk_description">internet freedom for Iranians</string>
+ <!-- A short description of "Paskoocheh": paskoocheh.com -->
+ <string name="summer_2026_funding_tool_paskoocheh_description">countering digital authoritarianism</string>
+ <!-- A short description of "Unredacted": unredacted.org -->
+ <string name="summer_2026_funding_tool_unredacted_description">keeping people connected, no matter where</string>
+ <!-- A short description of "Osservatorio Nessuno": osservatorionessuno.org -->
+ <string name="summer_2026_funding_tool_osservatorio_nessuno_description">software that leaves no trace</string>
+ <!-- A short description of "Save": open-archive.org/save -->
+ <string name="summer_2026_funding_tool_save_description">private archiving</string>
+ <!-- A short description of "OONI": ooni.org -->
+ <string name="summer_2026_funding_tool_ooni_description">tracking censorship globally</string>
+
</resources>
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/9289a7…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/9289a7…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-150.0a1-16.0-2] 8 commits: fixup! TB 40933: Add tor-launcher functionality
by henry (@henry) 28 Apr '26
by henry (@henry) 28 Apr '26
28 Apr '26
henry pushed to branch tor-browser-150.0a1-16.0-2 at The Tor Project / Applications / Tor Browser
Commits:
b61277be by Henry Wilkes at 2026-04-28T12:46:37+01:00
fixup! TB 40933: Add tor-launcher functionality
TB 44796: Convert `TorProviders` types to strings.
This allows them to be used in log messages.
- - - - -
6a41ffd8 by Henry Wilkes at 2026-04-28T12:53:36+01:00
fixup! TB 40933: Add tor-launcher functionality
TB 44796: Add `TorProviderState`, which tracks the state of a provider.
We create a new `TorProviderBase` class which tracks this state, and the
existing `TorProvider` now extends.
`TorProviderBuilder.sys.mjs`:
+ We provide a callback to let the current provider instance let
`TorProviderBuilder` know when this state has changed.
+ This will emit `TorProviderSateChanged` as a replacement for
`TorProcessExited`. The `Stopped` state has the same role as the
previous event, except it is more generic. This event also lets any
consumers know when a provider has been just replaced (`Starting`) and
successfully initialised (`Running`).
`TorControlPort.sys.mjs`:
+ Add `onClosed` callback to signal to the `TorProvider` that the
control port has closed, which will trigger `_stoppedInternal`.
+ Delay adding the event handler so the `TorProvider` only listens to a
control port after it is successfully set up and adopted by the
`TorProvider`.
`TorProvider.sys.mjs`:
+ `TorProvider` no longer emits `TorProviderTopics.ProcessExited`.
+ Instead, `TorProvider` calls `_stoppedInternal` whenever the provider
stops running. I.e. whenever the control port is closed, which
includes when the process exits but also other scenarios where the
control port might close unexpectedly.
- - - - -
7167229f by Henry Wilkes at 2026-04-28T12:53:37+01:00
fixup! TB 40933: Add tor-launcher functionality
TB 44796: Convert the "none" provider into a `TorProviderNone` class, so
it can be handled by the same logic as `TorProvider`.
- - - - -
adba934e by Henry Wilkes at 2026-04-28T12:53:39+01:00
fixup! TB 40933: Add tor-launcher functionality
TB 44796: Add the `settledState` method to allow `TorConnect` to wait
for the first provider's state after it has settled. This is a way to
get the current providers state prior to having listened to
`TorProviderStateChange`.
Add the `replace` method to allow `TorConnect` to request a new provider
on the user's behalf.
- - - - -
e74c8d84 by Henry Wilkes at 2026-04-28T12:53:40+01:00
fixup! TB 40933: Add tor-launcher functionality
TB 44796: Drop `#torExited` and internals of `firstWindowLoaded` and
instead use promptProviderState which handles both. These are only
temporary changes to be dropped in tor-browser#43570.
- - - - -
615707ff by Henry Wilkes at 2026-04-28T12:53:41+01:00
fixup! TB 40597: Implement TorSettings module
TB 44796: Listen for ProviderStateChanged rather than ProcessStopped.
- - - - -
48f5bfb2 by Henry Wilkes at 2026-04-28T12:53:42+01:00
fixup! TB 40933: Add tor-launcher functionality
TB 44796: Drop `TorProvider.isRunning`.
- - - - -
424f8e1a by Henry Wilkes at 2026-04-28T12:53:43+01:00
fixup! TB 40597: Implement TorSettings module
TB 44796: Use `TorProvider.state` instead of `isRunning`.
- - - - -
8 changed files:
- toolkit/components/tor-launcher/TorControlPort.sys.mjs
- toolkit/components/tor-launcher/TorProvider.sys.mjs
- + toolkit/components/tor-launcher/TorProviderBase.sys.mjs
- toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
- + toolkit/components/tor-launcher/TorProviderNone.sys.mjs
- toolkit/components/tor-launcher/moz.build
- toolkit/modules/TorConnect.sys.mjs
- toolkit/modules/TorSettings.sys.mjs
Changes:
=====================================
toolkit/components/tor-launcher/TorControlPort.sys.mjs
=====================================
@@ -384,21 +384,19 @@ export class TorController {
/**
* The event handler.
*
- * @type {TorEventHandler}
+ * @type {?TorEventHandler}
*/
- #eventHandler;
+ #eventHandler = null;
/**
* Connect to a control port over a Unix socket.
* Not available on Windows.
*
* @param {nsIFile} ipcFile The path to the Unix socket to connect to
- * @param {TorEventHandler} eventHandler The event handler to use for
- * asynchronous notifications
* @returns {TorController}
*/
- static fromIpcFile(ipcFile, eventHandler) {
- return new TorController(AsyncSocket.fromIpcFile(ipcFile), eventHandler);
+ static fromIpcFile(ipcFile) {
+ return new TorController(AsyncSocket.fromIpcFile(ipcFile));
}
/**
@@ -406,15 +404,10 @@ export class TorController {
*
* @param {string} host The hostname to connect to
* @param {number} port The port to connect the to
- * @param {TorEventHandler} eventHandler The event handler to use for
- * asynchronous notifications
* @returns {TorController}
*/
- static fromSocketAddress(host, port, eventHandler) {
- return new TorController(
- AsyncSocket.fromSocketAddress(host, port),
- eventHandler
- );
+ static fromSocketAddress(host, port) {
+ return new TorController(AsyncSocket.fromSocketAddress(host, port));
}
/**
@@ -425,15 +418,22 @@ export class TorController {
*
* @private
* @param {AsyncSocket} socket The socket to use
- * @param {TorEventHandler} eventHandler The event handler to use for
- * asynchronous notifications
*/
- constructor(socket, eventHandler) {
+ constructor(socket) {
this.#socket = socket;
- this.#eventHandler = eventHandler;
this.#startMessagePump();
}
+ /**
+ * Set an event handler for this instance.
+ *
+ * @param {TorEventHandler} eventHandler The event handler to use for
+ * asynchronous notifications
+ */
+ setEventHandler(eventHandler) {
+ this.#eventHandler = eventHandler;
+ }
+
// Socket and communication handling
/**
@@ -649,6 +649,7 @@ export class TorController {
this.#socket?.close();
} finally {
this.#socket = null;
+ this.#eventHandler?.onClosed();
}
}
@@ -1324,6 +1325,7 @@ export class TorController {
* The controller owner can implement this methods to receive asynchronous
* notifications from the controller.
*
+ * @property {OnClosed} onClosed Called when the socket is closed.
* @property {OnBootstrapStatus} onBootstrapStatus Called when a bootstrap
* status is received (i.e., a STATUS_CLIENT event with a BOOTSTRAP action)
* @property {OnLogMessage} onLogMessage Called when a log message is received
@@ -1336,6 +1338,9 @@ export class TorController {
* a connect cell along a circuit (i.e., a STREAM event with a SENTCONNECT
* status)
*/
+/**
+ * @callback OnClosed
+ */
/**
* @callback OnBootstrapStatus
*
=====================================
toolkit/components/tor-launcher/TorProvider.sys.mjs
=====================================
@@ -6,6 +6,7 @@ import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs";
import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
import { TorParsers } from "resource://gre/modules/TorParsers.sys.mjs";
+import { TorProviderBase } from "resource://gre/modules/TorProviderBase.sys.mjs";
import {
TorBootstrapError,
TorProviderTopics,
@@ -88,7 +89,7 @@ const TorConfigKeys = Object.freeze({
* It can start a new tor instance, or connect to an existing one.
* In the former case, it also takes its ownership by default.
*/
-export class TorProvider {
+export class TorProvider extends TorProviderBase {
/**
* The control port settings.
*
@@ -184,7 +185,7 @@ export class TorProvider {
* Starts a new tor process and connect to its control port, or connect to the
* control port of an existing tor daemon.
*/
- async init() {
+ async _initInternal() {
logger.debug("Initializing the Tor provider.");
// These settings might be customized in the following steps.
@@ -259,7 +260,7 @@ export class TorProvider {
* control connection is closed. Therefore, as a matter of facts, calling this
* function also makes the child Tor instance stop.
*/
- uninit() {
+ async _uninitInternal() {
logger.debug("Uninitializing the Tor provider.");
if (this.#torProcess) {
@@ -509,17 +510,6 @@ export class TorProvider {
return TorLauncherUtil.shouldStartAndOwnTor;
}
- /**
- * TODO: Rename to isReady once we remove finish the migration.
- *
- * @returns {boolean} true if we currently have a connection to the control
- * port. We take for granted that if we have one, we authenticated to it, and
- * so we have already verified we can send and receive data.
- */
- get isRunning() {
- return this.#controlConnection?.isOpen ?? false;
- }
-
/**
* Return the data about the current bridge, if any, or null.
* We can detect bridge only when the configured bridge lines include the
@@ -740,6 +730,11 @@ export class TorProvider {
};
tryConnect();
});
+ // The previous TorControlPort instances may have failed, in which case we
+ // want to ignore their events (such as `onClose`). This instance has just
+ // succeeded and will be owned by this TorProvider instance, therefore we
+ // want to start listening to its events now.
+ this.#controlConnection.setEventHandler(this);
// The following code will never throw, but we still want to wait for it
// before marking the provider as initialized.
@@ -751,7 +746,6 @@ export class TorProvider {
this.#torProcess.onExit = exitCode => {
logger.info(`The tor process exited with code ${exitCode}`);
this.#closeConnection("The tor process exited suddenly");
- Services.obs.notifyObservers(null, TorProviderTopics.ProcessExited);
};
if (!TorLauncherUtil.shouldOnlyConfigureTor) {
await this.#takeOwnership();
@@ -823,14 +817,12 @@ export class TorProvider {
let controlPort;
if (this.#controlPortSettings.ipcFile) {
controlPort = lazy.TorController.fromIpcFile(
- this.#controlPortSettings.ipcFile,
- this
+ this.#controlPortSettings.ipcFile
);
} else {
controlPort = lazy.TorController.fromSocketAddress(
this.#controlPortSettings.host,
- this.#controlPortSettings.port,
- this
+ this.#controlPortSettings.port
);
}
try {
@@ -864,21 +856,27 @@ export class TorProvider {
* attempt)
*/
#closeConnection(reason) {
- this.#cancelConnection(reason);
- if (this.#controlConnection) {
- logger.info("Closing the control connection", reason);
- try {
- this.#controlConnection.close();
- } catch (e) {
- logger.error("Failed to close the control port connection", e);
+ try {
+ this.#cancelConnection(reason);
+ if (this.#controlConnection) {
+ logger.info("Closing the control connection", reason);
+ try {
+ this.#controlConnection.close();
+ } catch (e) {
+ logger.error("Failed to close the control port connection", e);
+ }
+ this.#controlConnection = null;
+ } else {
+ logger.trace(
+ "Requested to close an already closed control port connection"
+ );
}
- this.#controlConnection = null;
- } else {
- logger.trace(
- "Requested to close an already closed control port connection"
- );
+ } finally {
+ this.#lastWarning = {};
+ // #controlConnection.close() may already trigger onClosed, but we
+ // call it once more for error cases as well.
+ this._stoppedInternal();
}
- this.#lastWarning = {};
}
// Authentication
@@ -975,6 +973,13 @@ export class TorProvider {
// Notification handlers
+ /**
+ * Notification that the control port has closed.
+ */
+ onClosed() {
+ this._stoppedInternal();
+ }
+
/**
* Receive and process a notification with the bootstrap status.
*
=====================================
toolkit/components/tor-launcher/TorProviderBase.sys.mjs
=====================================
@@ -0,0 +1,200 @@
+import {
+ TorProviderState,
+ TorProviderInitError,
+} from "resource://gre/modules/TorProviderBuilder.sys.mjs";
+
+/**
+ * @callback StateChangedCallback
+ */
+
+/**
+ * The base class for tor providers.
+ */
+export class TorProviderBase {
+ /**
+ * The current provider state.
+ *
+ * @type {string}
+ */
+ #state = TorProviderState.Starting;
+
+ /**
+ * The callback for when our state changes. `null` once the `uninit` method is
+ * called.
+ *
+ * @type {?StateChangedCallback}
+ */
+ #stateChangedCallback;
+
+ /**
+ * The promise to return from the `init` method.
+ *
+ * @type {?Promise<undefined>}
+ */
+ #initPromise = null;
+
+ /**
+ * The promise to return from the `uninit` method.
+ *
+ * @type {?Promise<undefined>}
+ */
+ #uninitPromise = null;
+
+ /**
+ * Create a new provider.
+ *
+ * @param {StateChangedCallback} stateChangedCallback - A callback to let an
+ * owner know that a provider's state has changed.
+ */
+ // NOTE: This should *not* be overridden by implementations.
+ constructor(stateChangedCallback) {
+ this.#stateChangedCallback = stateChangedCallback;
+ }
+
+ /**
+ * Initialize the provider and wait for it to be `Running`.
+ *
+ * This is safe to call multiple times, with each call waiting for the
+ * provider to be ready.
+ */
+ // NOTE: This should *not* be overridden by implementations.
+ async init() {
+ if (!this.#initPromise) {
+ this.#initPromise = this._initInternal().then(
+ () => {
+ this.#setState(TorProviderState.Running);
+ },
+ error => {
+ this.#setState(TorProviderState.Stopped);
+ // Wrap the error in `TorProviderInitError` to let callers know that
+ // this error is an initialization error.
+ throw new TorProviderInitError(error);
+ }
+ );
+ }
+ return this.#initPromise;
+ }
+
+ /**
+ * Uninitialize the provider and wait for it to be cleaned up.
+ *
+ * This is safe to call multiple times, with each call waiting for the
+ * clean up.
+ */
+ // NOTE: This should *not* be overridden by implementations.
+ async uninit() {
+ if (!this.#uninitPromise) {
+ this.#setState(TorProviderState.Stopped);
+ this.#stateChangedCallback = null;
+ this.#uninitPromise = this._uninitInternal();
+ }
+ return this.#uninitPromise;
+ }
+
+ /**
+ * The current `TorProviderState` state of the provider.
+ *
+ * @type {string}
+ */
+ // NOTE: This should *not* be overridden by implementations.
+ get state() {
+ return this.#state;
+ }
+
+ /**
+ * Set the state of the provider. Announcing this via a callback.
+ *
+ * @param {string} state - The new `TorProviderState`.
+ */
+ #setState(state) {
+ if (state === this.#state) {
+ return;
+ }
+ if (this.#state === TorProviderState.Stopped) {
+ // Ignore any changes away from the `Stopped` state, which is unexpected
+ // since implementations can only trigger the `Stopped` state, and
+ // everything else is handled by `TorProviderBase`.
+ return;
+ }
+ this.#state = state;
+ this.#stateChangedCallback?.();
+ }
+
+ /**
+ * An internal method to be called by implementations when they stop working.
+ *
+ * Optional and safe to call as part of `_uninitInternal`.
+ */
+ // NOTE: This should *not* be overridden by implementations.
+ _stoppedInternal() {
+ this.#setState(TorProviderState.Stopped);
+ }
+
+ // Implementation methods.
+
+ /**
+ * An internal initialization method for provider instances to implement.
+ */
+ async _initInternal() {
+ throw new Error("_initInternal not implemented.");
+ }
+
+ /**
+ * An internal uninitialization method for provider instances to implement.
+ */
+ async _uninitInternal() {
+ throw new Error("_uninitInternal not implemented.");
+ }
+
+ async writeBridgeSettings(_bridges) {
+ throw new Error("writeBridgeSettings not implemented.");
+ }
+
+ async writeProxySettings(_proxy) {
+ throw new Error("writeProxySettings not implemented.");
+ }
+
+ async writeFirewallSettings(_firewall) {
+ throw new Error("writeFirewallSettings not implemented.");
+ }
+
+ async flushSettings() {
+ throw new Error("flushSettings not implemented.");
+ }
+
+ async connect() {
+ throw new Error("connect not implemented.");
+ }
+
+ async stopBootstrap() {
+ throw new Error("stopBootstrap not implemented.");
+ }
+
+ async newnym() {
+ throw new Error("newnym not implemented.");
+ }
+
+ async getBridges() {
+ throw new Error("getBridges not implemented.");
+ }
+
+ async getPluggableTransports() {
+ throw new Error("getPluggableTransports not implemented.");
+ }
+
+ async onionAuthAdd(_address, _b64PrivateKey, _isPermanent) {
+ throw new Error("onionAuthAdd not implemented.");
+ }
+
+ async onionAuthRemove(_address) {
+ throw new Error("onionAuthRemove not implemented.");
+ }
+
+ async onionAuthViewKeys() {
+ throw new Error("onionAuthViewKeys not implemented.");
+ }
+
+ get currentBridge() {
+ throw new Error("currentBridge not implemented.");
+ }
+}
=====================================
toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
=====================================
@@ -6,10 +6,19 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
TorProvider: "resource://gre/modules/TorProvider.sys.mjs",
+ TorProviderNone: "resource://gre/modules/TorProviderNone.sys.mjs",
+});
+
+ChromeUtils.defineLazyGetter(lazy, "logger", () => {
+ return console.createInstance({
+ // Share preference with TorProvider.
+ maxLogLevelPref: "browser.tor_provider.log_level",
+ prefix: "TorProviderBuilder",
+ });
});
export const TorProviderTopics = Object.freeze({
- ProcessExited: "TorProcessExited",
+ ProviderStateChanged: "TorProviderStateChanged",
BootstrapStatus: "TorBootstrapStatus",
BootstrapError: "TorBootstrapError",
TorLog: "TorLog",
@@ -18,6 +27,15 @@ export const TorProviderTopics = Object.freeze({
CircuitCredentialsMatched: "TorCircuitCredentialsMatched",
});
+/**
+ * The tracked state a provider might be in.
+ */
+export const TorProviderState = Object.freeze({
+ Starting: "Starting",
+ Running: "Running",
+ Stopped: "Stopped",
+});
+
/**
* Wrapper error class for errors raised during TorProvider.init.
*/
@@ -54,8 +72,8 @@ export class TorBootstrapError extends Error {
}
export const TorProviders = Object.freeze({
- none: 0,
- tor: 1,
+ none: "none",
+ tor: "tor",
});
/**
@@ -65,6 +83,17 @@ export const TorProviders = Object.freeze({
* @property {string} msg The message
*/
+/**
+ * @typedef {object} TorProviderData
+ *
+ * The data associated with a tor provider.
+ *
+ * @property {TorProviderBase} provider - The provider instance.
+ * @property {Promise<undefined>} initPromise - A promise that fulfils after the
+ * previous provider is cleaned up and the new provider's initialization
+ * completes or throws an error.
+ */
+
/**
* The factory to get a Tor provider.
* Currently we support only TorProvider, i.e., the one that interacts with
@@ -72,14 +101,35 @@ export const TorProviders = Object.freeze({
*/
export class TorProviderBuilder {
/**
- * A promise with the instance of the provider that we are using.
+ * Data about the current provider instance.
*
- * @type {Promise<TorProvider>?}
+ * @type {?TorProviderData}
*/
- static #provider = null;
+ static #providerData = null;
/**
- * A record of the log messages from all TorProvider instances.
+ * Whether the `uninit` method has been called.
+ *
+ * @type {boolean}
+ */
+ static #uninitialized = false;
+
+ /**
+ * Check that we are active before a public call.
+ *
+ * @throws {Error} Throws if we are not active.
+ */
+ static #checkActive() {
+ if (this.#uninitialized) {
+ throw new Error("TorProviderBuilder has already been uninitialized.");
+ }
+ if (!this.#providerData) {
+ throw new Error("TorProviderBuilder has not been initialized.");
+ }
+ }
+
+ /**
+ * A record of the log messages from all provider instances.
*
* @type {LogEntry[]}
*/
@@ -120,22 +170,6 @@ export class TorProviderBuilder {
this.#log.push(logEntry);
}
- /**
- * The observer that checks when the tor process exits, and reinitializes the
- * provider.
- *
- * @type {Function}
- */
- static #exitObserver = null;
-
- /**
- * Tell whether the browser UI is ready.
- * We ignore any errors until it is because we cannot show them.
- *
- * @type {boolean}
- */
- static #uiReady = false;
-
/**
* Initialize the provider of choice.
*/
@@ -149,115 +183,214 @@ export class TorProviderBuilder {
};
Services.obs.addObserver(this.#logObserver, TorProviderTopics.TorLog);
+ // Even though initialization of the initial provider is asynchronous, we do
+ // not expect the caller to await it. The reason is that any call to build()
+ // will wait the initialization anyway (and re-throw any initialization
+ // error).
+ this.#replaceProvider();
+ }
+
+ /**
+ * Replace the provider with a new instance.
+ */
+ static #replaceProvider() {
+ // NOTE: We need to ensure that the #providerData is set as soon as
+ // TorProviderBuilder.init is called.
+ // I.e. it should be safe to call
+ // TorProviderBuilder.init();
+ // TorProviderBuilder.build();
+ // TorProviderBuilder.settledState();
+ // // etc
+ // without any await.
+ //
+ // In particular, this is needed by `TorConnect.init`, which will call
+ // `settledState`. It will also call `build` immediately if quickstart is
+ // set. See tor-browser#41921.
+ if (this.#providerData) {
+ lazy.logger.info(
+ `Replacing the provider with a "${this.providerType}" provider.`
+ );
+ } else {
+ lazy.logger.info(`Creating the initial "${this.providerType}" provider.`);
+ }
+
+ let providerClass;
switch (this.providerType) {
case TorProviders.tor:
- // Even though initialization of the initial TorProvider is
- // asynchronous, we do not expect the caller to await it. The reason is
- // that any call to build() will wait the initialization anyway (and
- // re-throw any initialization error).
- this.#initTorProvider();
+ providerClass = lazy.TorProvider;
break;
case TorProviders.none:
- lazy.TorLauncherUtil.setProxyConfiguration(
- lazy.TorLauncherUtil.getPreferredSocksConfiguration()
- );
+ providerClass = lazy.TorProviderNone;
break;
default:
- console.error(`Unknown tor provider ${this.providerType}.`);
+ lazy.logger.error(`Unknown tor provider ${this.providerType}.`);
break;
}
+ // NOTE: It should be safe to create another provider instance whilst the
+ // existing one is still active. However, we will wait until the other is
+ // uninitialized before we initialize the new one.
+ const provider = new providerClass(() => {
+ this.#notifyStateChanged(provider);
+ });
+ const prevProviderData = this.#providerData;
+ // NOTE: We want `#providerData` to be set prior to our call to
+ // `provider.init`, so we create the `initPromise` prior to setting it.
+ const { promise: initPromise, resolve, reject } = Promise.withResolvers();
+ this.#providerData = { provider, initPromise };
+ // Let observers know we are restarting the provider.
+ this.#notifyStateChanged(provider);
+
+ // Run the rest of the init in an async operation that will cause
+ // `initPromise` to settle.
+ // NOTE: `#cleanupProviderData` should not throw, unlike `provider.init()`,
+ // which may throw.
+ // NOTE: We wait for `#cleanupProviderData` to complete before calling
+ // `provider.init()` in case the implementation relies on this.
+ this.#cleanupProviderData(prevProviderData).finally(() => {
+ provider.init().then(resolve, reject);
+ });
}
/**
- * Replace #provider with a new instance.
+ * Notify any listeners that the state of the current provider has changed.
*
- * @returns {Promise<TorProvider>} The new instance.
+ * @param {TorProviderBase} provider - The provider who's state has changed.
*/
- static #initTorProvider() {
- if (!this.#exitObserver) {
- this.#exitObserver = this.#torExited.bind(this);
- Services.obs.addObserver(
- this.#exitObserver,
- TorProviderTopics.ProcessExited
- );
+ static #notifyStateChanged(provider) {
+ if (this.#uninitialized) {
+ // Do not signal the final state changes when we uninitialize.
+ return;
+ }
+ if (provider !== this.#providerData.provider) {
+ // Delayed call from an old provider. Ignore.
+ return;
}
- // NOTE: We need to ensure that the #provider is set as soon
- // TorProviderBuilder.init is called.
- // I.e. it should be safe to call
- // TorProviderBuilder.init();
- // TorProviderBuilder.build();
- // without any await.
- //
- // Therefore, we await the oldProvider within the Promise rather than make
- // #initTorProvider async.
- //
- // In particular, this is needed by TorConnect when the user has selected
- // quickstart, in which case `TorConnect.init` will immediately request the
- // provider. See tor-browser#41921.
- this.#provider = this.#replaceTorProvider(this.#provider);
- return this.#provider;
+ Services.obs.notifyObservers(
+ null,
+ TorProviderTopics.ProviderStateChanged,
+ provider.state
+ );
+
+ this.#promptProviderState(false);
}
/**
- * Replace a TorProvider instance. Resolves once the TorProvider is
- * initialised.
+ * Check the given provider's state.
+ *
+ * If the provider is no longer the current one, it is considered to be
+ * "Stopped".
+ *
+ * If it is still the current provider, the state will be updated and
+ * returned.
*
- * @param {Promise<TorProvider>?} oldProvider - The previous's provider's
- * promise, if any.
- * @returns {TorProvider} The new TorProvider instance.
+ * @param {TorProviderBase} provider - The provider to check.
+ * @returns {string} - The `TorProviderState` state for the provider.
*/
- static async #replaceTorProvider(oldProvider) {
+ static #checkProviderState(provider) {
+ if (this.#providerData?.provider !== provider) {
+ // Replaced.
+ lazy.logger.debug("The checked provider has been replaced.");
+ return TorProviderState.Stopped;
+ }
+ return this.#providerData.provider.state;
+ }
+
+ /**
+ * Cleanup the given provider data.
+ *
+ * @param {?TorProviderData} providerData - The data to clean up.
+ */
+ static async #cleanupProviderData(providerData) {
+ if (!providerData) {
+ return;
+ }
try {
- // Uninitialise the old TorProvider, if there is any.
- (await oldProvider)?.uninit();
+ await providerData.initPromise;
} catch {}
- const provider = new lazy.TorProvider();
+
+ // Call `uninit` to clean up, even if `init` threw.
+ // Should be safe to call more than once (via `uninit`).
try {
- await provider.init();
+ await providerData.provider.uninit();
} catch (error) {
- // Wrap in an error type for callers to know whether the error comes from
- // initialisation or something else.
- throw new TorProviderInitError(error);
+ lazy.logger.error("Error in uninitializing provider", error);
}
- return provider;
}
static uninit() {
- this.#provider?.then(provider => {
- provider.uninit();
- this.#provider = null;
- });
- if (this.#exitObserver) {
- Services.obs.removeObserver(
- this.#exitObserver,
- TorProviderTopics.ProcessExited
- );
- this.#exitObserver = null;
- }
+ this.#uninitialized = true;
+
+ // NOTE: `uninit` should not be followed by any further calls to public
+ // methods. So we can clear the `#providerData` without keeping it for any
+ // future provider instances to wait on.
+ const providerData = this.#providerData;
+ this.#providerData = null;
+ this.#cleanupProviderData(providerData);
+
Services.obs.removeObserver(this.#logObserver, TorProviderTopics.TorLog);
}
/**
- * Build a provider.
- * This method will wait for the system to be initialized, and allows you to
- * catch also any initialization errors.
+ * Request the current instance of the Tor provider.
+ *
+ * This method will wait for the system to be initialized before returning the
+ * provider.
+ *
+ * This will throw any initialization errors of the provider, if it had any.
+ * This will also throw if the provider is no longer active.
*
* @returns {TorProvider} A TorProvider instance
*/
static async build() {
- if (!this.#provider && this.providerType === TorProviders.none) {
+ this.#checkActive();
+ if (this.#providerData.provider instanceof lazy.TorProviderNone) {
throw new Error(
"Tor Browser has been configured to use only the proxy functionalities."
);
- } else if (!this.#provider) {
- throw new Error(
- "The provider has not been initialized or already uninitialized."
+ }
+
+ const { provider, initPromise } = this.#providerData;
+ // initPromise may throw.
+ await initPromise;
+ if (this.#checkProviderState(provider) !== TorProviderState.Running) {
+ lazy.logger.warn("Request was made for a provider that has stopped.");
+ // TODO: Wait for the new instance instead?
+ throw new TorProviderInitError(
+ new Error("Provider is no longer active.")
);
}
- return this.#provider;
+ return provider;
+ }
+
+ /**
+ * Get the state of the current provider instance. Waits until the provider
+ * has finished initialisation first.
+ *
+ * If the provider has been replaced, the Stopped state will be returned.
+ *
+ * @returns {string} - The `TorProviderState` state for the provider that
+ * existed when this method was called.
+ */
+ static async settledState() {
+ this.#checkActive();
+ const { provider, initPromise } = this.#providerData;
+ try {
+ await initPromise;
+ } catch {}
+ return this.#checkProviderState(provider);
}
+ /**
+ * Replace the current provider instance with a new provider.
+ */
+ static replace() {
+ this.#checkActive();
+ this.#replaceProvider();
+ }
+
+ // TODO: Remove firstWindowLoaded, #uiReady, #prompting, #promptProviderState
+ // and use TorConnect instead. tor-browser#43570.
/**
* Check if the provider has been succesfully initialized when the first
* browser window is shown.
@@ -266,50 +399,72 @@ export class TorProviderBuilder {
* but we should modify TorConnect and about:torconnect to handle this case
* there with a better UX.
*/
- static async firstWindowLoaded() {
- // FIXME: Just integrate this with the about:torconnect or about:tor UI.
- if (
- !lazy.TorLauncherUtil.shouldStartAndOwnTor ||
- this.providerType !== TorProviders.tor
- ) {
- // If we are not managing the Tor daemon we cannot restart it, so just
- // early return.
- return;
- }
- let running = false;
- try {
- const provider = await this.#provider;
- // The initialization might have succeeded, but so far we have ignored any
- // error notification. So, check that the process has not exited after the
- // provider has been initialized successfully, but the UI was not ready
- // yet.
- running = provider.isRunning;
- } catch {
- // Not even initialized, running is already false.
- }
- while (!running && lazy.TorLauncherUtil.showRestartPrompt(true)) {
- try {
- await this.#initTorProvider();
- running = true;
- } catch {}
- }
- // The user might have canceled the restart, but at this point the UI is
- // ready in any case.
- this.#uiReady = true;
+ static firstWindowLoaded() {
+ this.#promptProviderState(true);
}
- static async #torExited() {
+ /**
+ * Tell whether the browser UI is ready.
+ * We ignore any errors until it is because we cannot show them.
+ *
+ * @type {boolean}
+ */
+ static #uiReady = false;
+
+ /**
+ * Whether we are prompting the user for a restart of the provider.
+ *
+ * @type {boolean}
+ */
+ static #prompting = false;
+
+ /**
+ * Prompt the user to restart the provider, if this is necessary.
+ *
+ * @param {boolean} uiReady - Whether this is being called for the first time
+ * when the UI is ready.
+ */
+ static async #promptProviderState(uiReady) {
+ if (uiReady) {
+ this.#uiReady = true;
+ }
+ if (this.#providerData.provider.state === TorProviderState.Running) {
+ // Nothing to wait for.
+ return;
+ }
if (!this.#uiReady) {
- console.warn(
- `Seen ${TorProviderTopics.ProcessExited}, but not doing anything because the UI is not ready yet.`
+ lazy.logger.warn(
+ "Seen exit, but not doing anything because the UI is not ready yet."
);
return;
}
- while (lazy.TorLauncherUtil.showRestartPrompt(false)) {
- try {
- await this.#initTorProvider();
- break;
- } catch {}
+ if (this.#prompting) {
+ // Already prompting, so don't duplicate.
+ return;
+ }
+
+ this.#prompting = true;
+ let waitForInit = uiReady;
+ let retry = true;
+ try {
+ while (retry) {
+ if (waitForInit) {
+ try {
+ await this.#providerData.initPromise;
+ } catch {}
+ }
+ if (
+ this.#providerData.provider.state === TorProviderState.Stopped &&
+ lazy.TorLauncherUtil.showRestartPrompt(uiReady)
+ ) {
+ waitForInit = true;
+ this.replace();
+ } else {
+ retry = false;
+ }
+ }
+ } finally {
+ this.#prompting = false;
}
}
@@ -320,7 +475,7 @@ export class TorProviderBuilder {
* Otherwise, if it is not valid, the C tor implementation is chosen as the
* default one.
*
- * @returns {number} An entry from TorProviders
+ * @returns {string} An entry from TorProviders
*/
static get providerType() {
// TODO: Add a preference to permanently save this without and avoid always
=====================================
toolkit/components/tor-launcher/TorProviderNone.sys.mjs
=====================================
@@ -0,0 +1,19 @@
+import { TorProviderBase } from "resource://gre/modules/TorProviderBase.sys.mjs";
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
+});
+
+/**
+ * A provider that only sets the proxy settings.
+ */
+export class TorProviderNone extends TorProviderBase {
+ async _initInternal() {
+ lazy.TorLauncherUtil.setProxyConfiguration(
+ lazy.TorLauncherUtil.getPreferredSocksConfiguration()
+ );
+ }
+
+ async _uninitInternal() {}
+}
=====================================
toolkit/components/tor-launcher/moz.build
=====================================
@@ -7,7 +7,9 @@ EXTRA_JS_MODULES += [
"TorProcess.sys.mjs",
"TorProcessAndroid.sys.mjs",
"TorProvider.sys.mjs",
+ "TorProviderBase.sys.mjs",
"TorProviderBuilder.sys.mjs",
+ "TorProviderNone.sys.mjs",
"TorStartupService.sys.mjs",
]
=====================================
toolkit/modules/TorConnect.sys.mjs
=====================================
@@ -9,6 +9,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
MoatRPC: "resource://gre/modules/Moat.sys.mjs",
TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs",
+ TorProviderState: "resource://gre/modules/TorProviderBuilder.sys.mjs",
TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
TorBootstrapError: "resource://gre/modules/TorProviderBuilder.sys.mjs",
TorProviderInitError: "resource://gre/modules/TorProviderBuilder.sys.mjs",
@@ -913,7 +914,7 @@ export const TorConnect = {
};
// register the Tor topics we always care about
- observeTopic(lazy.TorProviderTopics.ProcessExited);
+ observeTopic(lazy.TorProviderTopics.ProviderStateChanged);
observeTopic(lazy.TorProviderTopics.HasWarnOrErr);
observeTopic(lazy.TorSettingsTopics.SettingsChanged);
observeTopic(NETWORK_LINK_TOPIC);
@@ -933,7 +934,7 @@ export const TorConnect = {
}
},
- async observe(subject, topic) {
+ async observe(subject, topic, data) {
lazy.logger.debug(`Observed ${topic}`);
switch (topic) {
@@ -947,7 +948,10 @@ export const TorConnect = {
this._notifyBootstrapProgress();
}
break;
- case lazy.TorProviderTopics.ProcessExited:
+ case lazy.TorProviderTopics.ProviderStateChanged:
+ if (data !== lazy.TorProviderState.Stopped) {
+ break;
+ }
lazy.logger.info("Starting again since the tor process exited");
// Treat a failure as a possibly broken configuration.
// So, prevent quickstart at the next start.
@@ -1341,7 +1345,7 @@ export const TorConnect = {
this._tryAgain = true;
if (error instanceof lazy.TorProviderInitError) {
- // Treat like TorProviderTopics.ProcessExited. We expect a user
+ // Treat like TorProviderTopics.ProviderStateChanged. We expect a user
// notification when this happens.
// Treat a failure as a possibly broken configuration.
// So, prevent quickstart at the next start.
=====================================
toolkit/modules/TorSettings.sys.mjs
=====================================
@@ -9,6 +9,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
Lox: "resource://gre/modules/Lox.sys.mjs",
LoxTopics: "resource://gre/modules/Lox.sys.mjs",
TorParsers: "resource://gre/modules/TorParsers.sys.mjs",
+ TorProviderState: "resource://gre/modules/TorProviderBuilder.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "logger", () => {
@@ -940,7 +941,10 @@ class TorSettingsImpl {
// Test whether the provider is no longer running or has been replaced.
const providerRunning = () => {
- return providerRef === this.#providerRef && provider.isRunning;
+ return (
+ providerRef === this.#providerRef &&
+ provider.state !== lazy.TorProviderState.Stopped
+ );
};
lazy.logger.debug("Passing on settings to the provider", apply, details);
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/6e2555…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/6e2555…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-150.0a1-16.0-2] BB 44832: Modify error pages for Base Browser.
by henry (@henry) 28 Apr '26
by henry (@henry) 28 Apr '26
28 Apr '26
henry pushed to branch mullvad-browser-150.0a1-16.0-2 at The Tor Project / Applications / Mullvad Browser
Commits:
3d7561e8 by Henry Wilkes at 2026-04-28T12:39:29+01:00
BB 44832: Modify error pages for Base Browser.
- - - - -
1 changed file:
- toolkit/content/net-error-card.mjs
Changes:
=====================================
toolkit/content/net-error-card.mjs
=====================================
@@ -998,6 +998,8 @@ export class NetErrorCard extends MozLitElement {
}
const { bodyTitleL10nId, image } = this.errorConfig;
+ // Drop the Firefox mascot images. tor-browser#44832.
+ // eslint-disable-next-line no-unused-vars
const { src, alt } = image ?? NET_ERROR_ILLUSTRATIONS.securityError;
const title = bodyTitleL10nId ?? "fp-certerror-body-title";
@@ -1006,9 +1008,6 @@ export class NetErrorCard extends MozLitElement {
href="chrome://global/skin/aboutNetError.css"
/>
<article class="felt-privacy-container">
- <div class="img-container">
- <img src=${src} data-l10n-id=${alt} data-l10n-attrs="alt" />
- </div>
<div class="container">
${this.showCustomNetErrorCard
? html`${this.customNetErrorContainerTemplate()}`
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/3d7…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/3d7…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0