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

Keyboard Shortcuts

Thread View

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

tbb-commits

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

  • 1 participants
  • 20430 discussions
[Git][tpo/applications/tor-browser][base-browser-128.4.0esr-14.0-1] fixup! Firefox preference overrides.
by morgan (@morgan) 13 Nov '24

13 Nov '24
morgan pushed to branch base-browser-128.4.0esr-14.0-1 at The Tor Project / Applications / Tor Browser Commits: ccf8f463 by Pier Angelo Vendrame at 2024-11-13T19:49:07+00:00 fixup! Firefox preference overrides. Bug 43165: Disable Microsoft SSO on macOS. - - - - - 1 changed file: - browser/app/profile/001-base-profile.js Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -447,6 +447,9 @@ pref("network.http.referer.defaultPolicy.pbmode", 2); pref("network.http.referer.XOriginTrimmingPolicy", 2); // Bug 17228: Force trim referer to scheme+host+port in cross-origin requests // Bug 40463: Disable Windows SSO pref("network.http.windows-sso.enabled", false, locked); +// Bug 43165: Disable Microsoft SSO on macOS +pref("network.http.microsoft-entra-sso.enabled", false); +pref("network.microsoft-sso-authority-list", ""); // tor-browser#40424 pref("pdfjs.enableScripting", false); #if MOZ_UPDATE_CHANNEL == release View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/ccf8f46… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/ccf8f46… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-128.4.0esr-14.5-1] fixup! Firefox preference overrides.
by morgan (@morgan) 13 Nov '24

13 Nov '24
morgan pushed to branch base-browser-128.4.0esr-14.5-1 at The Tor Project / Applications / Tor Browser Commits: 729f901e by Pier Angelo Vendrame at 2024-11-13T19:47:49+00:00 fixup! Firefox preference overrides. Bug 43165: Disable Microsoft SSO on macOS. - - - - - 1 changed file: - browser/app/profile/001-base-profile.js Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -455,6 +455,9 @@ pref("network.http.referer.defaultPolicy.pbmode", 2); pref("network.http.referer.XOriginTrimmingPolicy", 2); // Bug 17228: Force trim referer to scheme+host+port in cross-origin requests // Bug 40463: Disable Windows SSO pref("network.http.windows-sso.enabled", false, locked); +// Bug 43165: Disable Microsoft SSO on macOS +pref("network.http.microsoft-entra-sso.enabled", false); +pref("network.microsoft-sso-authority-list", ""); // tor-browser#40424 pref("pdfjs.enableScripting", false); #if MOZ_UPDATE_CHANNEL == release View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/729f901… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/729f901… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-128.4.0esr-14.5-1] fixup! Firefox preference overrides.
by morgan (@morgan) 13 Nov '24

13 Nov '24
morgan pushed to branch tor-browser-128.4.0esr-14.5-1 at The Tor Project / Applications / Tor Browser Commits: b66037f7 by Pier Angelo Vendrame at 2024-11-13T19:41:55+00:00 fixup! Firefox preference overrides. Bug 43165: Disable Microsoft SSO on macOS. - - - - - 1 changed file: - browser/app/profile/001-base-profile.js Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -452,6 +452,9 @@ pref("network.http.referer.defaultPolicy.pbmode", 2); pref("network.http.referer.XOriginTrimmingPolicy", 2); // Bug 17228: Force trim referer to scheme+host+port in cross-origin requests // Bug 40463: Disable Windows SSO pref("network.http.windows-sso.enabled", false, locked); +// Bug 43165: Disable Microsoft SSO on macOS +pref("network.http.microsoft-entra-sso.enabled", false); +pref("network.microsoft-sso-authority-list", ""); // tor-browser#40424 pref("pdfjs.enableScripting", false); #if MOZ_UPDATE_CHANNEL == release View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/b66037f… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/b66037f… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser-build] Pushed new tag mb-14.0-build1
by morgan (@morgan) 13 Nov '24

13 Nov '24
morgan pushed new tag mb-14.0-build1 at The Tor Project / Applications / tor-browser-build -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/mb-… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser-build][maint-14.0] Bug 41299: Prepare Mullvad Browser 14.0
by morgan (@morgan) 13 Nov '24

13 Nov '24
morgan pushed to branch maint-14.0 at The Tor Project / Applications / tor-browser-build Commits: 0665c022 by Morgan at 2024-11-13T18:48:38+00:00 Bug 41299: Prepare Mullvad Browser 14.0 - - - - - 4 changed files: - projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt - projects/browser/config - projects/firefox/config - rbm.conf Changes: ===================================== projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt ===================================== @@ -1,3 +1,114 @@ +Mullvad Browser 14.0 - November 13 2024 + * All Platforms + * Updated Firefox to 128.4.0esr + * Updated NoScript to 11.5.2 + * Updated Mullvad Browser Extension to 0.9.3 + * Bug 328: Provide search engine icons [mullvad-browser] + * Bug 329: Remove the Security Levels icon from the toolbar [mullvad-browser] + * Bug 344: set media.navigator.enabled = true [mullvad-browser] + * Bug 349: Tidy up mullvad Fluent files [mullvad-browser] + * Bug 30543: compat: make spoofed orientation reflect spoofed screen dimensions [1607032 + 1918202] [tor-browser] + * Bug 30862: 10ms time precision via EXSLT date-time function [tor-browser] + * Bug 32668: NoScript default whitelist re-appears on clicking NoScript Options / Reset [tor-browser] + * Bug 40147: Re-enable Picture-in-Picture mode [tor-browser] + * Bug 41309: Re-enable screenshots component [tor-browser] + * Bug 41817: Add more color aliases that take dark mode into account [tor-browser] + * Bug 42070: Backport Bugzilla 1834307 and hide smooth-scroll UX [tor-browser] + * Bug 42255: pdfjs.disabled used to be part of RFP until Bug 1838415; lock pref to false in stable [tor-browser] + * Bug 42356: Review 000-tor-browser.js and 001-base-profile.js for 128 [tor-browser] + * Bug 42362: "New window" missing from File menu [tor-browser] + * Bug 42596: Several console errors: Console.maxLogLevelPref used with a non-existing pref: [tor-browser] + * Bug 42601: Check Bug 1894779: Allow font-face urls to be resource:// urls and relax CORS for resource:// URLs [tor-browser] + * Bug 42603: Remove safebrowsing URLs [tor-browser] + * Bug 42611: Set clipboard.imageAsFile.enabled to false [tor-browser] + * Bug 42617: Restore the HTML form on DDG when using safest in 128 [tor-browser] + * Bug 42630: Review LaterRun in 128 [tor-browser] + * Bug 42640: Disable Firefox Flame button due to unknown interactions with New Identity [tor-browser] + * Bug 42641: Move from panel-footer class to moz-button-group [tor-browser] + * Bug 42644: toolbar rules in panelUI-shared.css are unneccessary [tor-browser] + * Bug 42646: Remove migrations for security.certerrors.mitm.auto_enable_enterprise_roots [tor-browser] + * Bug 42647: "Switching to a new device" regressed on 128 [tor-browser] + * Bug 42653: The Neterror page has a checkbox to report iframe origin errors to TPO [tor-browser] + * Bug 42665: Drop "Learn More" spacing [tor-browser] + * Bug 42667: Add description-deemphasized class to our additions to about:preferences [tor-browser] + * Bug 42679: Use a more robust approach to hide the "tracking protection" urlbar button [tor-browser] + * Bug 42683: Create script to generate issue triage csv's from bugzilla query and git scraping [tor-browser] + * Bug 42684: Disable network prefetch [tor-browser] + * Bug 42685: compat: ESR128: enable textmetrics [tor-browser] + * Bug 42687: Disable Privacy-Preserving Attribution [tor-browser] + * Bug 42699: Drop level="top" attribute from panels [tor-browser] + * Bug 42704: Drop the badged="true" attribute from security level button [tor-browser] + * Bug 42705: Update our preferences to account for new line height [tor-browser] + * Bug 42718: Remove the firefox-view button from UI, even when always-on private-browsing mode is disabled [tor-browser] + * Bug 42730: Make RemoteSettings use only local dumps [tor-browser] + * Bug 42735: Disable recent search suggestions [tor-browser] + * Bug 42740: Stop trying to hide "Restore previous session" [tor-browser] + * Bug 42742: Inconsistent use of "New private window" vs "New window" [tor-browser] + * Bug 42745: Remove some residuals from update scripts [tor-browser] + * Bug 42764: Unconditionally disable find-bar transition animation [tor-browser] + * Bug 42777: Remove 'Website Privacy Preferences' and ensure sensible default prefs [tor-browser] + * Bug 42814: Opt out from Firefox relay by default. [tor-browser] + * Bug 42831: Remove the shopping components [tor-browser] + * Bug 42867: Disable contentRelevancy component [tor-browser] + * Bug 42872: Disable translations until audited and solved the UX problems [tor-browser] + * Bug 43054: check bounceTrackingProtection in PB mode does not persist to disk [tor-browser] + * Bug 43072: moz-message-bar does not get announced on Orca screen-reader [tor-browser] + * Bug 43083: Backport fix for Mozilla 1436462 [tor-browser] + * Bug 43103: Verify whether an update is unsupported before choosing one [tor-browser] + * Bug 43109: Remove mention of Firefox Relay from settings [tor-browser] + * Bug 43117: Hide 'Always underline links' option [tor-browser] + * Bug 43134: Backport Bugzilla 1436226 Hardcode VP8/VP9 [tor-browser] + * Bug 43144: Ensure non-privacy browsing also sets the GPC header [tor-browser] + * Bug 43163: Disable offscreen canvas until verified it is not fingerprintable [tor-browser] + * Bug 43164: Prevent search-bar from being auto-hidden when not used for awhile [tor-browser] + * Bug 43178: Audit fingerprinting overrides (MozBug 1834274) [tor-browser] + * Bug 43184: Backport Bugzilla 1922294: RFP: fixup square spoofed orientation [tor-browser] + * Bug 43197: Disable automatic exception for HTTPS-First [tor-browser] + * Bug 43209: UI freezes when clipboard is empty after screen lock [tor-browser] + * Bug 43217: Fullscreen videos have rounded letterboxing corners [tor-browser] + * Bug 43257: NoScript-blocked content placeholders causing slow downs [tor-browser] + * Bug 43258: NoScript Lifecycle error on extension updates [tor-browser] + * Bug 41248: Check and update bundled font versions [tor-browser-build] + * Windows + macOS + * Bug 43021: Revert the OS deprecation notification introduced in #42347 [tor-browser] + * Windows + * Bug 43051: windows: remove UI for "open Tor Browser automatically when computer starts" [tor-browser] + * macOS + * Bug 42494: mac: add Arial Black and Arial Narrow to allowlist [tor-browser] + * Linux + * Bug 42773: Replace ~ with the original HOME [tor-browser] + * Bug 43092: Disable Wayland by default in 14.0 [tor-browser] + * Bug 43101: Security features warning links to Firefox installation support page with incomplete info [tor-browser] + * Bug 43141: Hardcode Arimo as a system-ui font [tor-browser] + * Bug 43196: Remove the vendor name from the "is playing media" notification on Linux [tor-browser] + * Bug 41237: Add some aliases to our Linux font config for compatibility [tor-browser-build] + * Build System + * All Platforms + * Bug 43157: Move tb-dev to base-browser [tor-browser] + * Bug 41096: Set SOURCE_DATE_EPOCH in the default env variables [tor-browser-build] + * Bug 41155: Update toolchains for ESR128 [tor-browser-build] + * Bug 41156: Split the Rust configuration options [tor-browser-build] + * Bug 41176: Update list of people with github commit access in MB issue templates [tor-browser-build] + * Bug 41188: Upgrade binutils to 2.41 [tor-browser-build] + * Bug 41236: Remove binutils when not needed [tor-browser-build] + * Bug 41256: tools/signing/upload-update_responses-to-staticiforme should regenerate update-responses when it already exists [tor-browser-build] + * Bug 41259: Skip versions which don't set incremental_from when generating incrementals [tor-browser-build] + * Bug 41260: Don't set legacy version for Mullvad Browser [tor-browser-build] + * Bug 41274: Improve fetch_changelogs.py for major releases [tor-browser-build] + * Bug 41279: Add @pierov and @ma1 as new signers [tor-browser-build] + * Bug 41289: Fix single-browser in relprep.py [tor-browser-build] + * Windows + macOS + * Bug 41197: Modify update-responses to prevent upgrades on unsupported Windows and macOS versions [tor-browser-build] + * Windows + * Bug 29318: Drop mingw-w64/gcc toolchain [tor-browser-build] + * Bug 29320: Use mingw-w64/clang toolchain to build Rust [tor-browser-build] + * Bug 41296: Implement missing Windows headers required for building cross-compiling WebRTC with mingw [tor-browser-build] + * Linux + * Bug 41013: Add a README to each project [tor-browser-build] + * Bug 41222: link_old_mar_filenames still referenced in torbrowser-incrementals-{release,alpha}-unsigned [tor-browser-build] + * Bug 41243: Add own apparmor profile to deb package [tor-browser-build] + * Bug 41282: Add SSL to our custom Python for MozBug 1924022 [tor-browser-build] + Mullvad Browser 14.0a10 - November 01 2024 * All Platforms * Updated Firefox to 128.4.0esr ===================================== projects/browser/config ===================================== @@ -115,9 +115,9 @@ input_files: name: ublock-origin sha256sum: e2cda9b2a1b0a7f6e5ef0da9f87f28df52f8560587ba2e51a3003121cfb81600 enable: '[% c("var/mullvad-browser") %]' - - URL: https://cdn.mullvad.net/browser-extension/0.9.0/mullvad-browser-extension-0… + - URL: https://cdn.mullvad.net/browser-extension/0.9.3/mullvad-browser-extension-0… name: mullvad-extension - sha256sum: 65bf235aa1015054ae0a54a40c5a663e67fe1d0f0799e7b4726f98cccc7f3eab + sha256sum: fc6bc7c850adf8845fec15b7ea06324f65f843155e2cc5dbd8719e34436512af enable: '[% c("var/mullvad-browser") %]' - filename: 'gtk3-settings.ini' enable: '[% c("var/linux") %]' ===================================== projects/firefox/config ===================================== @@ -107,6 +107,7 @@ targets: gitlab_project: https://gitlab.torproject.org/tpo/applications/mullvad-browser updater_url: 'https://cdn.mullvad.net/browser/update_responses/update_1/' nightly_updates_publish_dir_prefix: mullvadbrowser- + browser_build: 2 linux-x86_64: var: ===================================== rbm.conf ===================================== @@ -73,21 +73,20 @@ buildconf: git_signtag_opt: '-s' var: - torbrowser_version: '[% IF c("var/tor-browser") %]14.0.2[% ELSE %]14.0a10[% END %]' + torbrowser_version: '[% IF c("var/tor-browser") %]14.0.2[% ELSE %]14.0[% END %]' torbrowser_build: 'build1' # This should be the date of when the build is started. For the build # to be reproducible, browser_release_date should always be in the past. - browser_release_date: '2024/11/12 18:50:24' + browser_release_date: '2024/11/12 20:16:21' browser_release_date_timestamp: '[% USE date; date.format(c("var/browser_release_date"), "%s") %]' updater_enabled: 1 build_mar: 1 torbrowser_incremental_from: - - '[% IF c("var/mullvad-browser") %]14.0a9[% END %]' - - '[% IF c("var/mullvad-browser") %]14.0a8[% END %]' - - '[% IF c("var/mullvad-browser") %]14.0a7[% END %]' - '[% IF c("var/tor-browser") %]14.0.1[% END %]' - '[% IF c("var/tor-browser") %]14.0[% END %]' - - '[% IF c("var/tor-browser") %]13.5.7[% END %]' + - '[% IF c("var/mullvad-browser") %]13.5.9[% END %]' + - '13.5.7' + - '[% IF c("var/mullvad-browser") %]13.5.6[% END %]' mar_channel_id: '[% c("var/projectname") %]-torproject-[% c("var/channel") %]' torbrowser_legacy_version: 13.5.9 View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/0… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/0… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser-build][maint-14.0] Bug 41299: Prepare Mullvad Browser 14.0.2
by morgan (@morgan) 13 Nov '24

13 Nov '24
morgan pushed to branch maint-14.0 at The Tor Project / Applications / tor-browser-build Commits: fd194a5d by Morgan at 2024-11-13T18:08:55+00:00 Bug 41299: Prepare Mullvad Browser 14.0.2 - - - - - 4 changed files: - projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt - projects/browser/config - projects/firefox/config - rbm.conf Changes: ===================================== projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt ===================================== @@ -1,3 +1,114 @@ +Mullvad Browser 14.0 - November 13 2024 + * All Platforms + * Updated Firefox to 128.4.0esr + * Updated NoScript to 11.5.2 + * Updated Mullvad Browser Extension to 0.9.3 + * Bug 328: Provide search engine icons [mullvad-browser] + * Bug 329: Remove the Security Levels icon from the toolbar [mullvad-browser] + * Bug 344: set media.navigator.enabled = true [mullvad-browser] + * Bug 349: Tidy up mullvad Fluent files [mullvad-browser] + * Bug 30543: compat: make spoofed orientation reflect spoofed screen dimensions [1607032 + 1918202] [tor-browser] + * Bug 30862: 10ms time precision via EXSLT date-time function [tor-browser] + * Bug 32668: NoScript default whitelist re-appears on clicking NoScript Options / Reset [tor-browser] + * Bug 40147: Re-enable Picture-in-Picture mode [tor-browser] + * Bug 41309: Re-enable screenshots component [tor-browser] + * Bug 41817: Add more color aliases that take dark mode into account [tor-browser] + * Bug 42070: Backport Bugzilla 1834307 and hide smooth-scroll UX [tor-browser] + * Bug 42255: pdfjs.disabled used to be part of RFP until Bug 1838415; lock pref to false in stable [tor-browser] + * Bug 42356: Review 000-tor-browser.js and 001-base-profile.js for 128 [tor-browser] + * Bug 42362: "New window" missing from File menu [tor-browser] + * Bug 42596: Several console errors: Console.maxLogLevelPref used with a non-existing pref: [tor-browser] + * Bug 42601: Check Bug 1894779: Allow font-face urls to be resource:// urls and relax CORS for resource:// URLs [tor-browser] + * Bug 42603: Remove safebrowsing URLs [tor-browser] + * Bug 42611: Set clipboard.imageAsFile.enabled to false [tor-browser] + * Bug 42617: Restore the HTML form on DDG when using safest in 128 [tor-browser] + * Bug 42630: Review LaterRun in 128 [tor-browser] + * Bug 42640: Disable Firefox Flame button due to unknown interactions with New Identity [tor-browser] + * Bug 42641: Move from panel-footer class to moz-button-group [tor-browser] + * Bug 42644: toolbar rules in panelUI-shared.css are unneccessary [tor-browser] + * Bug 42646: Remove migrations for security.certerrors.mitm.auto_enable_enterprise_roots [tor-browser] + * Bug 42647: "Switching to a new device" regressed on 128 [tor-browser] + * Bug 42653: The Neterror page has a checkbox to report iframe origin errors to TPO [tor-browser] + * Bug 42665: Drop "Learn More" spacing [tor-browser] + * Bug 42667: Add description-deemphasized class to our additions to about:preferences [tor-browser] + * Bug 42679: Use a more robust approach to hide the "tracking protection" urlbar button [tor-browser] + * Bug 42683: Create script to generate issue triage csv's from bugzilla query and git scraping [tor-browser] + * Bug 42684: Disable network prefetch [tor-browser] + * Bug 42685: compat: ESR128: enable textmetrics [tor-browser] + * Bug 42687: Disable Privacy-Preserving Attribution [tor-browser] + * Bug 42699: Drop level="top" attribute from panels [tor-browser] + * Bug 42704: Drop the badged="true" attribute from security level button [tor-browser] + * Bug 42705: Update our preferences to account for new line height [tor-browser] + * Bug 42718: Remove the firefox-view button from UI, even when always-on private-browsing mode is disabled [tor-browser] + * Bug 42730: Make RemoteSettings use only local dumps [tor-browser] + * Bug 42735: Disable recent search suggestions [tor-browser] + * Bug 42740: Stop trying to hide "Restore previous session" [tor-browser] + * Bug 42742: Inconsistent use of "New private window" vs "New window" [tor-browser] + * Bug 42745: Remove some residuals from update scripts [tor-browser] + * Bug 42764: Unconditionally disable find-bar transition animation [tor-browser] + * Bug 42777: Remove 'Website Privacy Preferences' and ensure sensible default prefs [tor-browser] + * Bug 42814: Opt out from Firefox relay by default. [tor-browser] + * Bug 42831: Remove the shopping components [tor-browser] + * Bug 42867: Disable contentRelevancy component [tor-browser] + * Bug 42872: Disable translations until audited and solved the UX problems [tor-browser] + * Bug 43054: check bounceTrackingProtection in PB mode does not persist to disk [tor-browser] + * Bug 43072: moz-message-bar does not get announced on Orca screen-reader [tor-browser] + * Bug 43083: Backport fix for Mozilla 1436462 [tor-browser] + * Bug 43103: Verify whether an update is unsupported before choosing one [tor-browser] + * Bug 43109: Remove mention of Firefox Relay from settings [tor-browser] + * Bug 43117: Hide 'Always underline links' option [tor-browser] + * Bug 43134: Backport Bugzilla 1436226 Hardcode VP8/VP9 [tor-browser] + * Bug 43144: Ensure non-privacy browsing also sets the GPC header [tor-browser] + * Bug 43163: Disable offscreen canvas until verified it is not fingerprintable [tor-browser] + * Bug 43164: Prevent search-bar from being auto-hidden when not used for awhile [tor-browser] + * Bug 43178: Audit fingerprinting overrides (MozBug 1834274) [tor-browser] + * Bug 43184: Backport Bugzilla 1922294: RFP: fixup square spoofed orientation [tor-browser] + * Bug 43197: Disable automatic exception for HTTPS-First [tor-browser] + * Bug 43209: UI freezes when clipboard is empty after screen lock [tor-browser] + * Bug 43217: Fullscreen videos have rounded letterboxing corners [tor-browser] + * Bug 43257: NoScript-blocked content placeholders causing slow downs [tor-browser] + * Bug 43258: NoScript Lifecycle error on extension updates [tor-browser] + * Bug 41248: Check and update bundled font versions [tor-browser-build] + * Windows + macOS + * Bug 43021: Revert the OS deprecation notification introduced in #42347 [tor-browser] + * Windows + * Bug 43051: windows: remove UI for "open Tor Browser automatically when computer starts" [tor-browser] + * macOS + * Bug 42494: mac: add Arial Black and Arial Narrow to allowlist [tor-browser] + * Linux + * Bug 42773: Replace ~ with the original HOME [tor-browser] + * Bug 43092: Disable Wayland by default in 14.0 [tor-browser] + * Bug 43101: Security features warning links to Firefox installation support page with incomplete info [tor-browser] + * Bug 43141: Hardcode Arimo as a system-ui font [tor-browser] + * Bug 43196: Remove the vendor name from the "is playing media" notification on Linux [tor-browser] + * Bug 41237: Add some aliases to our Linux font config for compatibility [tor-browser-build] + * Build System + * All Platforms + * Bug 43157: Move tb-dev to base-browser [tor-browser] + * Bug 41096: Set SOURCE_DATE_EPOCH in the default env variables [tor-browser-build] + * Bug 41155: Update toolchains for ESR128 [tor-browser-build] + * Bug 41156: Split the Rust configuration options [tor-browser-build] + * Bug 41176: Update list of people with github commit access in MB issue templates [tor-browser-build] + * Bug 41188: Upgrade binutils to 2.41 [tor-browser-build] + * Bug 41236: Remove binutils when not needed [tor-browser-build] + * Bug 41256: tools/signing/upload-update_responses-to-staticiforme should regenerate update-responses when it already exists [tor-browser-build] + * Bug 41259: Skip versions which don't set incremental_from when generating incrementals [tor-browser-build] + * Bug 41260: Don't set legacy version for Mullvad Browser [tor-browser-build] + * Bug 41274: Improve fetch_changelogs.py for major releases [tor-browser-build] + * Bug 41279: Add @pierov and @ma1 as new signers [tor-browser-build] + * Bug 41289: Fix single-browser in relprep.py [tor-browser-build] + * Windows + macOS + * Bug 41197: Modify update-responses to prevent upgrades on unsupported Windows and macOS versions [tor-browser-build] + * Windows + * Bug 29318: Drop mingw-w64/gcc toolchain [tor-browser-build] + * Bug 29320: Use mingw-w64/clang toolchain to build Rust [tor-browser-build] + * Bug 41296: Implement missing Windows headers required for building cross-compiling WebRTC with mingw [tor-browser-build] + * Linux + * Bug 41013: Add a README to each project [tor-browser-build] + * Bug 41222: link_old_mar_filenames still referenced in torbrowser-incrementals-{release,alpha}-unsigned [tor-browser-build] + * Bug 41243: Add own apparmor profile to deb package [tor-browser-build] + * Bug 41282: Add SSL to our custom Python for MozBug 1924022 [tor-browser-build] + Mullvad Browser 14.0a10 - November 01 2024 * All Platforms * Updated Firefox to 128.4.0esr ===================================== projects/browser/config ===================================== @@ -115,9 +115,9 @@ input_files: name: ublock-origin sha256sum: e2cda9b2a1b0a7f6e5ef0da9f87f28df52f8560587ba2e51a3003121cfb81600 enable: '[% c("var/mullvad-browser") %]' - - URL: https://cdn.mullvad.net/browser-extension/0.9.0/mullvad-browser-extension-0… + - URL: https://cdn.mullvad.net/browser-extension/0.9.3/mullvad-browser-extension-0… name: mullvad-extension - sha256sum: 65bf235aa1015054ae0a54a40c5a663e67fe1d0f0799e7b4726f98cccc7f3eab + sha256sum: fc6bc7c850adf8845fec15b7ea06324f65f843155e2cc5dbd8719e34436512af enable: '[% c("var/mullvad-browser") %]' - filename: 'gtk3-settings.ini' enable: '[% c("var/linux") %]' ===================================== projects/firefox/config ===================================== @@ -107,6 +107,7 @@ targets: gitlab_project: https://gitlab.torproject.org/tpo/applications/mullvad-browser updater_url: 'https://cdn.mullvad.net/browser/update_responses/update_1/' nightly_updates_publish_dir_prefix: mullvadbrowser- + browser_build: 2 linux-x86_64: var: ===================================== rbm.conf ===================================== @@ -73,21 +73,20 @@ buildconf: git_signtag_opt: '-s' var: - torbrowser_version: '[% IF c("var/tor-browser") %]14.0.2[% ELSE %]14.0a10[% END %]' + torbrowser_version: '[% IF c("var/tor-browser") %]14.0.2[% ELSE %]14.0[% END %]' torbrowser_build: 'build1' # This should be the date of when the build is started. For the build # to be reproducible, browser_release_date should always be in the past. - browser_release_date: '2024/11/12 18:50:24' + browser_release_date: '2024/11/12 20:16:21' browser_release_date_timestamp: '[% USE date; date.format(c("var/browser_release_date"), "%s") %]' updater_enabled: 1 build_mar: 1 torbrowser_incremental_from: - - '[% IF c("var/mullvad-browser") %]14.0a9[% END %]' - - '[% IF c("var/mullvad-browser") %]14.0a8[% END %]' - - '[% IF c("var/mullvad-browser") %]14.0a7[% END %]' - '[% IF c("var/tor-browser") %]14.0.1[% END %]' - '[% IF c("var/tor-browser") %]14.0[% END %]' - - '[% IF c("var/tor-browser") %]13.5.7[% END %]' + - '[% IF c("var/mullvad-browser") %]13.5.9[% END %]' + - '13.5.7' + - '[% IF c("var/mullvad-browser") %]13.5.6[% END %]' mar_channel_id: '[% c("var/projectname") %]-torproject-[% c("var/channel") %]' torbrowser_legacy_version: 13.5.9 View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/f… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/f… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser-update-responses][main] release: new version, 14.0.2
by morgan (@morgan) 13 Nov '24

13 Nov '24
morgan pushed to branch main at The Tor Project / Applications / Tor Browser update responses Commits: 1d015e75 by Morgan at 2024-11-13T17:55:05+00:00 release: new version, 14.0.2 - - - - - 30 changed files: - update_3/release/.htaccess - update_3/release/13.5.5-14.0.1+13.5.9-linux-i686-ALL.xml → update_3/release/13.5.5-14.0.2+13.5.9-linux-i686-ALL.xml - update_3/release/13.5.5-14.0.1+13.5.9-linux-x86_64-ALL.xml → update_3/release/13.5.5-14.0.2+13.5.9-linux-x86_64-ALL.xml - update_3/release/13.5.5-14.0.1+13.5.9-macos-ALL.xml → update_3/release/13.5.5-14.0.2+13.5.9-macos-ALL.xml - update_3/release/13.5.5-14.0.1+13.5.9-windows-i686-ALL.xml → update_3/release/13.5.5-14.0.2+13.5.9-windows-i686-ALL.xml - update_3/release/13.5.5-14.0.1+13.5.9-windows-x86_64-ALL.xml → update_3/release/13.5.5-14.0.2+13.5.9-windows-x86_64-ALL.xml - update_3/release/13.5.6-14.0.1+13.5.9-linux-i686-ALL.xml → update_3/release/13.5.6-14.0.2+13.5.9-linux-i686-ALL.xml - update_3/release/13.5.6-14.0.1+13.5.9-linux-x86_64-ALL.xml → update_3/release/13.5.6-14.0.2+13.5.9-linux-x86_64-ALL.xml - update_3/release/13.5.6-14.0.1+13.5.9-macos-ALL.xml → update_3/release/13.5.6-14.0.2+13.5.9-macos-ALL.xml - update_3/release/13.5.6-14.0.1+13.5.9-windows-i686-ALL.xml → update_3/release/13.5.6-14.0.2+13.5.9-windows-i686-ALL.xml - update_3/release/13.5.6-14.0.1+13.5.9-windows-x86_64-ALL.xml → update_3/release/13.5.6-14.0.2+13.5.9-windows-x86_64-ALL.xml - update_3/release/13.5.7-14.0.1+13.5.9-linux-i686-ALL.xml → update_3/release/13.5.7-14.0.2+13.5.9-linux-i686-ALL.xml - update_3/release/13.5.7-14.0.1+13.5.9-linux-x86_64-ALL.xml → update_3/release/13.5.7-14.0.2+13.5.9-linux-x86_64-ALL.xml - update_3/release/13.5.7-14.0.1+13.5.9-macos-ALL.xml → update_3/release/13.5.7-14.0.2+13.5.9-macos-ALL.xml - update_3/release/13.5.7-14.0.1+13.5.9-windows-i686-ALL.xml → update_3/release/13.5.7-14.0.2+13.5.9-windows-i686-ALL.xml - update_3/release/13.5.7-14.0.1+13.5.9-windows-x86_64-ALL.xml → update_3/release/13.5.7-14.0.2+13.5.9-windows-x86_64-ALL.xml - update_3/release/14.0-14.0.1+13.5.9-linux-i686-ALL.xml → update_3/release/14.0-14.0.2+13.5.9-linux-i686-ALL.xml - update_3/release/14.0-14.0.1+13.5.9-linux-x86_64-ALL.xml → update_3/release/14.0-14.0.2+13.5.9-linux-x86_64-ALL.xml - update_3/release/14.0-14.0.1+13.5.9-macos-ALL.xml → update_3/release/14.0-14.0.2+13.5.9-macos-ALL.xml - update_3/release/14.0-14.0.1+13.5.9-windows-i686-ALL.xml → update_3/release/14.0-14.0.2+13.5.9-windows-i686-ALL.xml - update_3/release/14.0-14.0.1+13.5.9-windows-x86_64-ALL.xml → update_3/release/14.0-14.0.2+13.5.9-windows-x86_64-ALL.xml - + update_3/release/14.0.1-14.0.2+13.5.9-linux-i686-ALL.xml - + update_3/release/14.0.1-14.0.2+13.5.9-linux-x86_64-ALL.xml - + update_3/release/14.0.1-14.0.2+13.5.9-macos-ALL.xml - + update_3/release/14.0.1-14.0.2+13.5.9-windows-i686-ALL.xml - + update_3/release/14.0.1-14.0.2+13.5.9-windows-x86_64-ALL.xml - update_3/release/14.0.1+13.5.9-linux-i686-ALL.xml → update_3/release/14.0.2+13.5.9-linux-i686-ALL.xml - update_3/release/14.0.1+13.5.9-linux-x86_64-ALL.xml → update_3/release/14.0.2+13.5.9-linux-x86_64-ALL.xml - update_3/release/14.0.1+13.5.9-macos-ALL.xml → update_3/release/14.0.2+13.5.9-macos-ALL.xml - update_3/release/14.0.1+13.5.9-windows-i686-ALL.xml → update_3/release/14.0.2+13.5.9-windows-i686-ALL.xml The diff was not included because it is too large. View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-update-responses… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-update-responses… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-128.4.0esr-14.5-1] 2 commits: fixup! Bug 40458: Implement .tor.onion aliases
by Pier Angelo Vendrame (@pierov) 13 Nov '24

13 Nov '24
Pier Angelo Vendrame pushed to branch tor-browser-128.4.0esr-14.5-1 at The Tor Project / Applications / Tor Browser Commits: 2ec9bfd0 by Henry Wilkes at 2024-11-13T16:42:16+00:00 fixup! Bug 40458: Implement .tor.onion aliases Bug 43294: Replace willDestroy with didDestroy. - - - - - 1d5bf97c by Henry Wilkes at 2024-11-13T16:42:46+00:00 fixup! Bug 27476: Implement about:torconnect captive portal within Tor Browser Bug 43294: Replace willDestroy with didDestroy. - - - - - 2 changed files: - browser/components/rulesets/RulesetsParent.sys.mjs - toolkit/components/torconnect/TorConnectParent.sys.mjs Changes: ===================================== browser/components/rulesets/RulesetsParent.sys.mjs ===================================== @@ -30,7 +30,7 @@ export class RulesetsParent extends JSWindowActorParent { ); } - willDestroy() { + didDestroy() { Services.obs.removeObserver( this.observer, OnionAliasStoreTopics.ChannelsChanged ===================================== toolkit/components/torconnect/TorConnectParent.sys.mjs ===================================== @@ -77,7 +77,7 @@ export class TorConnectParent extends JSWindowActorParent { ); } - willDestroy() { + didDestroy() { Services.obs.removeObserver( this.torConnectObserver, TorConnectTopics.StageChange View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/1f6fc0… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/1f6fc0… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-128.4.0esr-14.5-1] 17 commits: fixup! Bug 40597: Implement TorSettings module
by Pier Angelo Vendrame (@pierov) 13 Nov '24

13 Nov '24
Pier Angelo Vendrame pushed to branch tor-browser-128.4.0esr-14.5-1 at The Tor Project / Applications / Tor Browser Commits: 8fee92cd by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 40597: Implement TorSettings module Bug 41710: Move bootstrapping attempts into a new class. We copy the logic from &quot;BootstrappingState&quot; and &quot;AutoBootstrappingState&quot; into &quot;BootstrappingAttempt&quot; and &quot;AutoBootstrappingAttempt&quot;. The main difference is that we can do the following: ``` bootstrapAttempt = new BootstrapAttempt(); bootstrapResult = await bootstrapAttempt.run(); // ... bootstrapAttempt.cancel(); ``` rather than using the &quot;StateCallback&quot; class, which requires some complicated state management. Moreover, &quot;AutoBootstrappingAttempt&quot; will use &quot;BootstrappingAttempt&quot; for each of its attempts. So the logic for bootstrapping can be kept in one place. Some other changes: 1. Censorship simulation will no longer necessarily avoid the Moat calls, so these can be tested. 2. It would be possible to perform the internet test when auto-bootstrapping as well. 3. When auto-bootstrapping, if &quot;Bootstrapping&quot; produces an error other than a &quot;BootstrapError&quot;, we can end it early. 4. No longer set TorConnect internals. 5. More fine-grained control over censorship simulation and offline simulation. - - - - - 1086febd by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 40597: Implement TorSettings module Bug 41710: Remove StateCallback. - - - - - 55d915e5 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 40597: Implement TorSettings module Bug 41710: Return early from TorConnect.init if not enabled. - - - - - 802af522 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 40597: Implement TorSettings module Bug 41710: Replace StateCallback in TorConnect. Instead of managing the abstract &quot;State&quot; we directly manager the user &quot;Stage&quot;. We provide backward compatibility with the &quot;State&quot; for android and about:torconnect. Eventually this logic can be dropped from these endpoints and they can listen for changes in the &quot;Stage&quot; instead. The behaviour for about:torconnect is mostly the same as before, with some exceptions: 1. If the user sees the &quot;Offline&quot; state, and starts and cancels the bootstrap, they should return the to the &quot;Offline&quot; state, rather than &quot;ConnectToTor&quot;. 2. Trying to start a bootstrap via the UI before the settings have loaded will do nothing. 3. Pressing a breadcrumb whilst bootstrapping will now also cancel the bootstrap. - - - - - cc17defe by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 27476: Implement about:torconnect captive portal within Tor Browser Bug 42550 - Switch about:torconnect to use TorConnect.stage to control the shown stage and sync pages. Now TorConnect entirely controls which stage should be shown to the user, and &quot;about:torconnect&quot; simply relays the user actions up to TorConnect to handle. In particular, we stop sending out &quot;torconnect:broadcast-user-action&quot; to sync pages. We also show &quot;Try Again&quot; if the user cancels the first bootstrap attempt without an error. We also do not try and sync the selected region between pages. However all pages should still show the *actually* submitted region after a bootstrap fails. E.g. to confirm their location. We also allow the user to re-select &quot;Automatic&quot; when they use breadcrumbs to go back a stage. Also change gTorConnectTitlebarStatus and gTorConnectUrlbarButton to use TorConnectStage. - - - - - 771381c5 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 40597: Implement TorSettings module Bug 41710: Switch TorConnect.openTorConnect to use new methods. - - - - - fafc6ff3 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection Bug 41710: Switch from TorConnect.state to TorConnect.stage. - - - - - 2ddd2029 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 40597: Implement TorSettings module Bug 41710: Remove unused TorConnect properties. - - - - - 3a27b920 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 42247: Android helpers for the TorProvider Bug 41710: Switch Android to new TorConnect methods and add TODOs. - - - - - 4aa64ccc by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Lox integration Bug 41710: Switch from TorConnectState to TorConnectStage. - - - - - 56837486 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 27476: Implement about:torconnect captive portal within Tor Browser Bug 41710: Move viewTorLogs and openTorPreferences to TorConnectParent. - - - - - ebe8a652 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 40597: Implement TorSettings module Bug 41710: Move viewTorLogs to TorConnectParent. - - - - - 8f95d948 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! [android] Enable the connect assist experiments on alpha Bug 41710: Remove onSettingsRequested. - - - - - bba68db8 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! [android] Add Tor integration and UI Bug 41710: Remove onSettingsRequested. - - - - - a6be0160 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Temporary changes to about:torconnect for Android. Bug 41710: Remove onSettingsRequested. TorConnect.openTorPreferences was removed. - - - - - be125ecd by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! Bug 40597: Implement TorSettings module Bug 41710: Remove the beginBootstrap, beginAutoBootstrap, and cancelBootstrap methods. - - - - - 1f6fc087 by Henry Wilkes at 2024-11-13T08:23:22+00:00 fixup! [android] Add Tor integration and UI Bug 41710: Fix onBootstrapProgress for new TorConnect. The bootstrap progress signal is now released every time the stage changes. - - - - - 18 changed files: - browser/base/content/browser.js - browser/base/content/browser.js.globals - browser/components/torpreferences/content/builtinBridgeDialog.js - browser/components/torpreferences/content/connectionPane.js - browser/components/torpreferences/content/provideBridgeDialog.js - browser/components/torpreferences/content/requestBridgeDialog.js - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerGV.kt - mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java - toolkit/components/lox/Lox.sys.mjs - toolkit/components/torconnect/TorConnectChild.sys.mjs - toolkit/components/torconnect/TorConnectParent.sys.mjs - toolkit/components/torconnect/content/aboutTorConnect.js - toolkit/components/torconnect/content/torConnectTitlebarStatus.js - toolkit/components/torconnect/content/torConnectUrlbarButton.js - toolkit/modules/RemotePageAccessManager.sys.mjs - toolkit/modules/TorAndroidIntegration.sys.mjs - toolkit/modules/TorConnect.sys.mjs Changes: ===================================== browser/base/content/browser.js ===================================== @@ -85,7 +85,7 @@ ChromeUtils.defineESModuleGetters(this, { TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.sys.mjs", TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs", TorConnect: "resource://gre/modules/TorConnect.sys.mjs", - TorConnectState: "resource://gre/modules/TorConnect.sys.mjs", + TorConnectStage: "resource://gre/modules/TorConnect.sys.mjs", TorConnectTopics: "resource://gre/modules/TorConnect.sys.mjs", TorUIUtils: "resource:///modules/TorUIUtils.sys.mjs", TranslationsParent: "resource://gre/actors/TranslationsParent.sys.mjs", ===================================== browser/base/content/browser.js.globals ===================================== @@ -276,7 +276,7 @@ "TorDomainIsolator", "gTorCircuitPanel", "TorConnect", - "TorConnectState", + "TorConnectStage", "TorConnectTopics", "gTorConnectUrlbarButton", "gTorConnectTitlebarStatus", ===================================== browser/components/torpreferences/content/builtinBridgeDialog.js ===================================== @@ -79,14 +79,14 @@ const gBuiltinBridgeDialog = { this._acceptButton = dialog.getButton("accept"); - Services.obs.addObserver(this, TorConnectTopics.StateChange); + Services.obs.addObserver(this, TorConnectTopics.StageChange); this.onSelectChange(); this.onAcceptStateChange(); }, uninit() { - Services.obs.removeObserver(this, TorConnectTopics.StateChange); + Services.obs.removeObserver(this, TorConnectTopics.StageChange); }, onSelectChange() { @@ -107,7 +107,7 @@ const gBuiltinBridgeDialog = { observe(subject, topic) { switch (topic) { - case TorConnectTopics.StateChange: + case TorConnectTopics.StageChange: this.onAcceptStateChange(); break; } ===================================== browser/components/torpreferences/content/connectionPane.js ===================================== @@ -22,7 +22,7 @@ const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule( "resource://gre/modules/TorProviderBuilder.sys.mjs" ); -const { TorConnect, TorConnectTopics, TorConnectState, TorCensorshipLevel } = +const { TorConnect, TorConnectTopics, TorConnectStage, TorCensorshipLevel } = ChromeUtils.importESModule("resource://gre/modules/TorConnect.sys.mjs"); const { MoatRPC } = ChromeUtils.importESModule( @@ -2195,18 +2195,7 @@ const gBridgeSettings = { // Start Bootstrapping, which should use the configured bridges. // NOTE: We do this regardless of any previous TorConnect Error. - if (TorConnect.canBeginBootstrap) { - TorConnect.beginBootstrap(); - } - // Open "about:torconnect". - // FIXME: If there has been a previous bootstrapping error then - // "about:torconnect" will be trying to get the user to use - // AutoBootstrapping. It is not set up to handle a forced direct - // entry to plain Bootstrapping from this dialog so the UI will - // not be aligned. In particular the - // AboutTorConnect.uiState.bootstrapCause will be aligned to - // whatever was shown previously in "about:torconnect" instead. - TorConnect.openTorConnect(); + TorConnect.openTorConnect({ beginBootstrapping: "hard" }); }); }, // closedCallback should be called after gSubDialog has already @@ -2322,27 +2311,27 @@ const gNetworkStatus = { "network-status-tor-connect-button" ); this._torConnectButton.addEventListener("click", () => { - TorConnect.openTorConnect({ beginBootstrap: true }); + TorConnect.openTorConnect({ beginBootstrapping: "soft" }); }); this._updateInternetStatus("unknown"); this._updateTorConnectionStatus(); - Services.obs.addObserver(this, TorConnectTopics.StateChange); + Services.obs.addObserver(this, TorConnectTopics.StageChange); }, /** * Un-initialize the area. */ uninit() { - Services.obs.removeObserver(this, TorConnectTopics.StateChange); + Services.obs.removeObserver(this, TorConnectTopics.StageChange); }, observe(subject, topic) { switch (topic) { // triggered when tor connect state changes and we may // need to update the messagebox - case TorConnectTopics.StateChange: { + case TorConnectTopics.StageChange: { this._updateTorConnectionStatus(); break; } @@ -2433,7 +2422,8 @@ const gNetworkStatus = { const buttonHadFocus = this._torConnectButton.contains( document.activeElement ); - const isBootstrapped = TorConnect.state === TorConnectState.Bootstrapped; + const isBootstrapped = + TorConnect.stageName === TorConnectStage.Bootstrapped; const isBlocked = !isBootstrapped && TorConnect.potentiallyBlocked; let l10nId; if (isBootstrapped) { @@ -2527,7 +2517,8 @@ const gConnectionPane = (function () { ); chooseForMe.addEventListener("command", () => { TorConnect.openTorConnect({ - beginAutoBootstrap: location.value, + beginBootstrapping: "hard", + regionCode: location.value, }); }); this._populateLocations = () => { @@ -2558,7 +2549,7 @@ const gConnectionPane = (function () { locationEntries.append(...items); }; locationEntries.append( - createItem("", TorStrings.settings.bridgeLocationAutomatic) + createItem("automatic", TorStrings.settings.bridgeLocationAutomatic) ); if (TorConnect.countryCodes.length) { locationEntries.append( @@ -2607,7 +2598,7 @@ const gConnectionPane = (function () { this.onViewTorLogs(); }); - Services.obs.addObserver(this, TorConnectTopics.StateChange); + Services.obs.addObserver(this, TorConnectTopics.StageChange); }, init() { @@ -2629,7 +2620,7 @@ const gConnectionPane = (function () { // unregister our observer topics Services.obs.removeObserver(this, TorSettingsTopics.SettingsChanged); - Services.obs.removeObserver(this, TorConnectTopics.StateChange); + Services.obs.removeObserver(this, TorConnectTopics.StageChange); }, // whether the page should be present in about:preferences @@ -2653,7 +2644,7 @@ const gConnectionPane = (function () { } // triggered when tor connect state changes and we may // need to update the messagebox - case TorConnectTopics.StateChange: { + case TorConnectTopics.StageChange: { this._showAutoconfiguration(); break; } ===================================== browser/components/torpreferences/content/provideBridgeDialog.js ===================================== @@ -128,14 +128,14 @@ const gProvideBridgeDialog = { this.onDialogAccept(event) ); - Services.obs.addObserver(this, TorConnectTopics.StateChange); + Services.obs.addObserver(this, TorConnectTopics.StageChange); this.setPage("entry"); this.checkValue(); }, uninit() { - Services.obs.removeObserver(this, TorConnectTopics.StateChange); + Services.obs.removeObserver(this, TorConnectTopics.StageChange); }, /** @@ -512,7 +512,7 @@ const gProvideBridgeDialog = { observe(subject, topic) { switch (topic) { - case TorConnectTopics.StateChange: + case TorConnectTopics.StageChange: this.onAcceptStateChange(); break; } ===================================== browser/components/torpreferences/content/requestBridgeDialog.js ===================================== @@ -91,14 +91,14 @@ const gRequestBridgeDialog = { selectors.incorrectCaptchaHbox ); - Services.obs.addObserver(this, TorConnectTopics.StateChange); + Services.obs.addObserver(this, TorConnectTopics.StageChange); this.onAcceptStateChange(); }, uninit() { BridgeDB.close(); // Unregister our observer topics. - Services.obs.removeObserver(this, TorConnectTopics.StateChange); + Services.obs.removeObserver(this, TorConnectTopics.StageChange); }, onAcceptStateChange() { @@ -113,7 +113,7 @@ const gRequestBridgeDialog = { observe(subject, topic) { switch (topic) { - case TorConnectTopics.StateChange: + case TorConnectTopics.StageChange: this.onAcceptStateChange(); break; } ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt ===================================== @@ -1438,7 +1438,4 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorIn navHost.navController.navigate(NavGraphDirections.actionStartupHome()) } override fun onBootstrapError(code: String?, message: String?, phase: String?, reason: String?) = Unit - override fun onSettingsRequested() { - navHost.navController.navigate(NavGraphDirections.actionGlobalSettingsFragment()) - } } ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerGV.kt ===================================== @@ -332,20 +332,28 @@ class TorControllerGV( // TorEventsBootstrapStateChangeListener override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) { Log.d(TAG, "onBootstrapProgress($progress, $hasWarnings)") + // TODO: onBootstrapProgress should only be used to change the shown + // bootstrap percentage or a Tor log option during a "Bootstrapping" + // stage. + // The progress value should not be used to change the `lastKnownStatus` + // value or determine if a bootstrap has started or completed. The + // TorConnectStage should be used instead. if (progress == 100.0) { lastKnownStatus = TorConnectState.Bootstrapped wasTorBootstrapped = true onTorConnected() - } else { - lastKnownStatus = TorConnectState.Bootstrapping + } else if (lastKnownStatus == TorConnectState.Bootstrapping) { onTorConnecting() - } onTorStatusUpdate("", lastKnownStatus.toTorStatus().status, progress) } // TorEventsBootstrapStateChangeListener override fun onBootstrapComplete() { + // TODO: There should be no need to respond to the BootstrapComplete + // event if we are already handling TorConnectStage.Bootstrapped. + // In particular, `lastKnownStatus` and onTorConnected should be set in + // response to a change in TorConnectStage instead. lastKnownStatus = TorConnectState.Bootstrapped this.onTorConnected() } @@ -354,9 +362,4 @@ class TorControllerGV( override fun onBootstrapError(code: String?, message: String?, phase: String?, reason: String?) { lastKnownError = TorError(code ?: "", message ?: "", phase ?: "", reason ?: "") } - - // TorEventsBootstrapStateChangeListener - override fun onSettingsRequested() { - // noop - } } ===================================== mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java ===================================== @@ -44,7 +44,6 @@ public class TorIntegrationAndroid implements BundleEventListener { private static final String EVENT_TOR_LOGS = "GeckoView:Tor:Logs"; private static final String EVENT_SETTINGS_READY = "GeckoView:Tor:SettingsReady"; private static final String EVENT_SETTINGS_CHANGED = "GeckoView:Tor:SettingsChanged"; - private static final String EVENT_SETTINGS_OPEN = "GeckoView:Tor:OpenSettings"; // Events we emit private static final String EVENT_SETTINGS_GET = "GeckoView:Tor:SettingsGet"; @@ -118,8 +117,7 @@ public class TorIntegrationAndroid implements BundleEventListener { EVENT_CONNECT_ERROR, EVENT_BOOTSTRAP_PROGRESS, EVENT_BOOTSTRAP_COMPLETE, - EVENT_TOR_LOGS, - EVENT_SETTINGS_OPEN); + EVENT_TOR_LOGS); } @Override // BundleEventListener @@ -176,10 +174,6 @@ public class TorIntegrationAndroid implements BundleEventListener { for (TorLogListener listener : mLogListeners) { listener.onLog(type, msg); } - } else if (EVENT_SETTINGS_OPEN.equals(event)) { - for (BootstrapStateChangeListener listener : mBootstrapStateListeners) { - listener.onSettingsRequested(); - } } } @@ -641,8 +635,6 @@ public class TorIntegrationAndroid implements BundleEventListener { void onBootstrapComplete(); void onBootstrapError(String code, String message, String phase, String reason); - - void onSettingsRequested(); } public interface TorLogListener { ===================================== toolkit/components/lox/Lox.sys.mjs ===================================== @@ -23,7 +23,7 @@ ChromeUtils.defineESModuleGetters(lazy, { DomainFrontRequestResponseError: "resource://gre/modules/DomainFrontedRequests.sys.mjs", TorConnect: "resource://gre/modules/TorConnect.sys.mjs", - TorConnectState: "resource://gre/modules/TorConnect.sys.mjs", + TorConnectStage: "resource://gre/modules/TorConnect.sys.mjs", TorSettings: "resource://gre/modules/TorSettings.sys.mjs", TorSettingsTopics: "resource://gre/modules/TorSettings.sys.mjs", TorBridgeSource: "resource://gre/modules/TorSettings.sys.mjs", @@ -1049,7 +1049,7 @@ class LoxImpl { const method = "POST"; const contentType = "application/vnd.api+json"; - if (lazy.TorConnect.state === lazy.TorConnectState.Bootstrapped) { + if (lazy.TorConnect.stageName === lazy.TorConnectStage.Bootstrapped) { let request; try { request = await fetch(url, { ===================================== toolkit/components/torconnect/TorConnectChild.sys.mjs ===================================== @@ -77,7 +77,7 @@ export class TorConnectChild extends RemotePageChild { receiveMessage(message) { super.receiveMessage(message); - if (message.name === "torconnect:state-change") { + if (message.name === "torconnect:stage-change") { this.#maybeRedirect(); } } ===================================== toolkit/components/torconnect/TorConnectParent.sys.mjs ===================================== @@ -2,29 +2,20 @@ import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs"; import { - InternetStatus, TorConnect, TorConnectTopics, - TorConnectState, } from "resource://gre/modules/TorConnect.sys.mjs"; import { TorSettings, TorSettingsTopics, } from "resource://gre/modules/TorSettings.sys.mjs"; -const BroadcastTopic = "about-torconnect:broadcast"; - const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { HomePage: "resource:///modules/HomePage.sys.jsm", }); -const log = console.createInstance({ - maxLogLevel: "Warn", - prefix: "TorConnectParent", -}); - /* This object is basically a marshalling interface between the TorConnect module and a particular about:torconnect page @@ -40,31 +31,6 @@ export class TorConnectParent extends JSWindowActorParent { const self = this; - this.state = { - State: TorConnect.state, - StateChanged: false, - PreviousState: TorConnectState.Initial, - ErrorCode: TorConnect.errorCode, - ErrorDetails: TorConnect.errorDetails, - BootstrapProgress: TorConnect.bootstrapProgress, - InternetStatus: TorConnect.internetStatus, - DetectedLocation: TorConnect.detectedLocation, - ShowViewLog: TorConnect.logHasWarningOrError, - HasEverFailed: TorConnect.hasEverFailed, - UIState: TorConnect.uiState, - }; - - // Workaround for a race condition, but we should fix it asap. - // about:torconnect is loaded before TorSettings is actually initialized. - // The getter might throw and the page not loaded correctly as a result. - // Silence any warning for now, but we should really fix it. - // See also tor-browser#41921. - try { - this.state.QuickStartEnabled = TorSettings.quickstart.enabled; - } catch (e) { - this.state.QuickStartEnabled = false; - } - // JSWindowActiveParent derived objects cannot observe directly, so create a // member object to do our observing for us. // @@ -72,103 +38,54 @@ export class TorConnectParent extends JSWindowActorParent { // module, and maintains a state object which we pass down to our // about:torconnect page, which uses the state object to update its UI. this.torConnectObserver = { - observe(aSubject, aTopic) { - let obj = aSubject?.wrappedJSObject; - - // Update our state struct based on received torconnect topics and - // forward on to aboutTorConnect.js. - self.state.StateChanged = false; - switch (aTopic) { - case TorConnectTopics.StateChange: { - self.state.PreviousState = self.state.State; - self.state.State = obj.state; - self.state.StateChanged = true; - // Clear any previous error information if we are bootstrapping. - if (self.state.State === TorConnectState.Bootstrapping) { - self.state.ErrorCode = null; - self.state.ErrorDetails = null; - } - self.state.BootstrapProgress = TorConnect.bootstrapProgress; - self.state.ShowViewLog = TorConnect.logHasWarningOrError; - self.state.HasEverFailed = TorConnect.hasEverFailed; - break; - } - case TorConnectTopics.BootstrapProgress: { - self.state.BootstrapProgress = obj.progress; - self.state.ShowViewLog = obj.hasWarnings; - break; - } - case TorConnectTopics.BootstrapComplete: { - // noop + observe(subject, topic) { + const obj = subject?.wrappedJSObject; + switch (topic) { + case TorConnectTopics.StageChange: + self.sendAsyncMessage("torconnect:stage-change", obj); break; - } - case TorConnectTopics.Error: { - self.state.ErrorCode = obj.code; - self.state.ErrorDetails = obj; - self.state.InternetStatus = TorConnect.internetStatus; - self.state.DetectedLocation = TorConnect.detectedLocation; - self.state.ShowViewLog = true; + case TorConnectTopics.BootstrapProgress: + self.sendAsyncMessage("torconnect:bootstrap-progress", obj); break; - } - case TorSettingsTopics.Ready: { - if ( - self.state.QuickStartEnabled !== TorSettings.quickstart.enabled - ) { - self.state.QuickStartEnabled = TorSettings.quickstart.enabled; - } else { - return; + case TorSettingsTopics.SettingsChanged: + if (!obj.changes.includes("quickstart.enabled")) { + break; } + // eslint-disable-next-lined no-fallthrough + case TorSettingsTopics.Ready: + self.sendAsyncMessage( + "torconnect:quickstart-changed", + TorSettings.quickstart.enabled + ); break; - } - case TorSettingsTopics.SettingsChanged: { - if ( - aSubject.wrappedJSObject.changes.includes("quickstart.enabled") - ) { - self.state.QuickStartEnabled = TorSettings.quickstart.enabled; - } else { - // this isn't a setting torconnect cares about - return; - } - break; - } - default: { - log.warn(`TorConnect: unhandled observe topic '${aTopic}'`); - } } - - self.sendAsyncMessage("torconnect:state-change", self.state); }, }; - // Observe all of the torconnect:.* topics. - for (const key in TorConnectTopics) { - const topic = TorConnectTopics[key]; - Services.obs.addObserver(this.torConnectObserver, topic); - } + Services.obs.addObserver( + this.torConnectObserver, + TorConnectTopics.StageChange + ); + Services.obs.addObserver( + this.torConnectObserver, + TorConnectTopics.BootstrapProgress + ); Services.obs.addObserver(this.torConnectObserver, TorSettingsTopics.Ready); Services.obs.addObserver( this.torConnectObserver, TorSettingsTopics.SettingsChanged ); - - this.userActionObserver = { - observe(aSubject) { - let obj = aSubject?.wrappedJSObject; - if (obj) { - obj.connState = self.state; - self.sendAsyncMessage("torconnect:user-action", obj); - } - }, - }; - Services.obs.addObserver(this.userActionObserver, BroadcastTopic); } willDestroy() { - // Stop observing all of our torconnect:.* topics. - for (const key in TorConnectTopics) { - const topic = TorConnectTopics[key]; - Services.obs.removeObserver(this.torConnectObserver, topic); - } + Services.obs.removeObserver( + this.torConnectObserver, + TorConnectTopics.StageChange + ); + Services.obs.removeObserver( + this.torConnectObserver, + TorConnectTopics.BootstrapProgress + ); Services.obs.removeObserver( this.torConnectObserver, TorSettingsTopics.Ready @@ -177,7 +94,6 @@ export class TorConnectParent extends JSWindowActorParent { this.torConnectObserver, TorSettingsTopics.SettingsChanged ); - Services.obs.removeObserver(this.userActionObserver, BroadcastTopic); } async receiveMessage(message) { @@ -192,48 +108,57 @@ export class TorConnectParent extends JSWindowActorParent { TorSettings.saveToPrefs().applySettings(); break; case "torconnect:open-tor-preferences": - TorConnect.openTorPreferences(); - break; - case "torconnect:cancel-bootstrap": - TorConnect.cancelBootstrap(); - break; - case "torconnect:begin-bootstrap": - TorConnect.beginBootstrap(); - break; - case "torconnect:begin-autobootstrap": - TorConnect.beginAutoBootstrap(message.data); + this.browsingContext.top.embedderElement.ownerGlobal.openPreferences( + "connection" + ); break; case "torconnect:view-tor-logs": - TorConnect.viewTorLogs(); + this.browsingContext.top.embedderElement.ownerGlobal.openPreferences( + "connection-viewlogs" + ); break; case "torconnect:restart": Services.startup.quit( Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit ); break; - case "torconnect:set-ui-state": - TorConnect.uiState = message.data; - this.state.UIState = TorConnect.uiState; + case "torconnect:start-again": + TorConnect.startAgain(); + break; + case "torconnect:choose-region": + TorConnect.chooseRegion(); + break; + case "torconnect:begin-bootstrapping": + TorConnect.beginBootstrapping(message.data.regionCode); break; - case "torconnect:broadcast-user-action": - Services.obs.notifyObservers(message.data, BroadcastTopic); + case "torconnect:cancel-bootstrapping": + TorConnect.cancelBootstrapping(); break; - case "torconnect:get-init-args": + case "torconnect:get-init-args": { // Called on AboutTorConnect.init(), pass down all state data it needs // to init. - // pretend this is a state transition on init - // so we always get fresh UI - this.state.StateChanged = true; - this.state.UIState = TorConnect.uiState; + let quickstartEnabled = false; + + // Workaround for a race condition, but we should fix it asap. + // about:torconnect is loaded before TorSettings is actually initialized. + // The getter might throw and the page not loaded correctly as a result. + // Silence any warning for now, but we should really fix it. + // See also tor-browser#41921. + try { + quickstartEnabled = TorSettings.quickstart.enabled; + } catch (e) { + // Do not throw. + } + return { TorStrings, - TorConnectState, - InternetStatus, Direction: Services.locale.isAppLocaleRTL ? "rtl" : "ltr", - State: this.state, CountryNames: TorConnect.countryNames, + stage: TorConnect.stage, + quickstartEnabled, }; + } case "torconnect:get-country-codes": return TorConnect.getCountryCodes(); } ===================================== toolkit/components/torconnect/content/aboutTorConnect.js ===================================== @@ -7,8 +7,6 @@ // populated in AboutTorConnect.init() let TorStrings = {}; -let TorConnectState = {}; -let InternetStatus = {}; const UIStates = Object.freeze({ ConnectToTor: "ConnectToTor", @@ -135,53 +133,23 @@ class AboutTorConnect { tryBridgeButton: document.querySelector(this.selectors.buttons.tryBridge), }); - uiState = { - currentState: UIStates.ConnectToTor, - allowAutomaticLocation: true, - selectedLocation: "automatic", - bootstrapCause: UIStates.ConnectToTor, - }; + selectedLocation; + shownStage = null; locations = {}; - constructor() { - this.uiStates = Object.freeze( - Object.fromEntries([ - [UIStates.ConnectToTor, this.showConnectToTor.bind(this)], - [UIStates.Offline, this.showOffline.bind(this)], - [UIStates.ConnectionAssist, this.showConnectionAssistant.bind(this)], - [UIStates.CouldNotLocate, this.showCouldNotLocate.bind(this)], - [UIStates.LocationConfirm, this.showLocationConfirmation.bind(this)], - [UIStates.FinalError, this.showFinalError.bind(this)], - ]) - ); - } - - beginBootstrap() { - RPMSendAsyncMessage("torconnect:begin-bootstrap"); - } - - beginAutoBootstrap(countryCode) { - if (countryCode === "automatic") { - countryCode = ""; - } - RPMSendAsyncMessage("torconnect:begin-autobootstrap", countryCode); + beginBootstrapping() { + RPMSendAsyncMessage("torconnect:begin-bootstrapping", {}); } - cancelBootstrap() { - RPMSendAsyncMessage("torconnect:cancel-bootstrap"); - } - - transitionUIState(nextState, connState) { - if (nextState !== this.uiState.currentState) { - this.uiState.currentState = nextState; - this.saveUIState(); - } - this.uiStates[nextState](connState); + beginAutoBootstrapping(regionCode) { + RPMSendAsyncMessage("torconnect:begin-bootstrapping", { + regionCode, + }); } - saveUIState() { - RPMSendAsyncMessage("torconnect:set-ui-state", this.uiState); + cancelBootstrapping() { + RPMSendAsyncMessage("torconnect:cancel-bootstrapping"); } /* @@ -305,19 +273,6 @@ class AboutTorConnect { this.elements.longContentText.append(...args); } - setProgress(description, visible, percent) { - this.elements.progressDescription.textContent = description; - if (visible) { - this.show(this.elements.progressMeter); - this.elements.progressMeter.style.setProperty( - "--progress-percent", - `${percent}%` - ); - } else { - this.hide(this.elements.progressMeter); - } - } - setBreadcrumbsStatus(connectToTor, connectionAssist, tryBridge) { this.elements.breadcrumbContainer.classList.remove("hidden"); const elems = [ @@ -362,22 +317,17 @@ class AboutTorConnect { return TorStrings.torConnect.bootstrapStatus[status] ?? status; } - getMaybeLocalizedError(state) { - if (!state?.ErrorCode) { - return ""; - } - switch (state.ErrorCode) { + getMaybeLocalizedError(error) { + switch (error.code) { case "Offline": return TorStrings.torConnect.offline; case "BootstrapError": { - const details = state.ErrorDetails?.cause; - if (!details?.phase || !details?.reason) { + if (!error.phase || !error.reason) { return TorStrings.torConnect.torBootstrapFailed; } - let status = this.getLocalizedStatus(details.phase); + let status = this.getLocalizedStatus(error.phase); const reason = - TorStrings.torConnect.bootstrapWarning[details.reason] ?? - details.reason; + TorStrings.torConnect.bootstrapWarning[error.reason] ?? error.reason; return TorStrings.torConnect.bootstrapFailedDetails .replace("%1$S", status) .replace("%2$S", reason); @@ -392,13 +342,10 @@ class AboutTorConnect { // A standard JS error, or something for which we do probably do not // have a translation. Returning the original message is the best we can // do. - return state.ErrorDetails.message; + return error.message; default: - console.warn( - `Unknown error code: ${state.ErrorCode}`, - state.ErrorDetails - ); - return state.ErrorDetails?.message ?? state.ErrorCode; + console.warn(`Unknown error code: ${error.code}`, error); + return error.message || error.code; } } @@ -406,109 +353,119 @@ class AboutTorConnect { These methods update the UI based on the current TorConnect state */ - updateUI(state) { - // calls update_$state() - this[`update_${state.State}`](state); - this.elements.quickstartToggle.pressed = state.QuickStartEnabled; - } + updateStage(stage) { + if (stage.name === this.shownStage) { + return; + } - /* Per-state updates */ + this.shownStage = stage.name; + this.selectedLocation = stage.defaultRegion; - update_Initial(state) { - this.showConnectToTor(state); - } + let showProgress = false; + let showLog = false; + switch (stage.name) { + case "Disabled": + console.error("Should not be open when TorConnect is disabled"); + break; + case "Loading": + case "Start": + // Loading is not currnetly handled, treat the same as "Start", but UI + // will be unresponsive. + this.showStart(stage.tryAgain, stage.potentiallyBlocked); + break; + case "Bootstrapping": + showProgress = true; + this.showBootstrapping(stage.bootstrapTrigger, stage.tryAgain); + break; + case "Offline": + showLog = true; + this.showOffline(); + break; + case "ChooseRegion": + showLog = true; + this.showChooseRegion(stage.error); + break; + case "RegionNotFound": + showLog = true; + this.showRegionNotFound(); + break; + case "ConfirmRegion": + showLog = true; + this.showConfirmRegion(stage.error); + break; + case "FinalError": + showLog = true; + this.showFinalError(stage.error); + break; + case "Bootstrapped": + showProgress = true; + this.showBootstrapped(); + break; + default: + console.error(`Unknown stage ${stage.name}`); + break; + } - update_Configuring(state) { - if ( - state.StateChanged && - (state.PreviousState === TorConnectState.Bootstrapping || - state.PreviousState === TorConnectState.AutoBootstrapping) - ) { - // The bootstrap has been cancelled - this.transitionUIState(this.uiState.bootstrapCause, state); + if (showProgress) { + this.show(this.elements.progressMeter); + } else { + this.hide(this.elements.progressMeter); } - } - update_AutoBootstrapping(state) { - this.showBootstrapping(state); - } + this.updateBootstrappingStatus(stage.bootstrappingStatus); - update_Bootstrapping(state) { - this.showBootstrapping(state); + if (showLog) { + this.show(this.elements.viewLogButton); + } else { + this.hide(this.elements.viewLogButton); + } } - update_Error(state) { - if (!state.StateChanged) { - return; - } - if (state.InternetStatus === InternetStatus.Offline) { - this.transitionUIState(UIStates.Offline, state); - } else if (state.PreviousState === TorConnectState.Bootstrapping) { - this.transitionUIState(UIStates.ConnectionAssist, state); - } else if (state.PreviousState === TorConnectState.AutoBootstrapping) { - if (this.uiState.bootstrapCause === UIStates.ConnectionAssist) { - if (this.getLocation() === "automatic") { - this.uiState.allowAutomaticLocation = false; - if (!state.DetectedLocation) { - this.transitionUIState(UIStates.CouldNotLocate, state); - return; - } - // Change the location only here, to avoid overriding any user change/ - // insisting with the detected location - this.setLocation(state.DetectedLocation); - } - this.transitionUIState(UIStates.LocationConfirm, state); - } else { - this.transitionUIState(UIStates.FinalError, state); - } - } else { - console.error( - "We received an error starting from an unexpected state", - state - ); + updateBootstrappingStatus(data) { + this.elements.progressMeter.style.setProperty( + "--progress-percent", + `${data.progress}%` + ); + if (this.shownStage === "Bootstrapping" && data.hasWarning) { + // When bootstrapping starts, we hide the log button, but we re-show it if + // we get a warning. + this.show(this.elements.viewLogButton); } } - update_Bootstrapped(_state) { - const showProgressbar = true; + updateQuickstart(enabled) { + this.elements.quickstartToggle.pressed = enabled; + } + showBootstrapped() { this.setTitle(TorStrings.torConnect.torConnected, ""); this.setLongText(TorStrings.settings.torPreferencesDescription); - this.setProgress("", showProgressbar, 100); + this.elements.progressDescription.textContent = ""; this.hideButtons(); } - update_Disabled(_state) { - // TODO: we should probably have some UX here if a user goes to about:torconnect when - // it isn't in use (eg using tor-launcher or system tor) - } - - showConnectToTor(state) { + showStart(tryAgain, potentiallyBlocked) { this.setTitle(TorStrings.torConnect.torConnect, ""); this.setLongText(TorStrings.settings.torPreferencesDescription); - this.setProgress("", false); - this.hide(this.elements.viewLogButton); + this.elements.progressDescription.textContent = ""; this.hideButtons(); this.show(this.elements.quickstartContainer); this.show(this.elements.configureButton); this.show(this.elements.connectButton, true); - if (state?.StateChanged) { - this.elements.connectButton.focus(); + this.elements.connectButton.focus(); + if (tryAgain) { + this.elements.connectButton.textContent = TorStrings.torConnect.tryAgain; } - if (state?.HasEverFailed) { + if (potentiallyBlocked) { this.setBreadcrumbsStatus( BreadcrumbStatus.Active, BreadcrumbStatus.Default, BreadcrumbStatus.Disabled ); - this.elements.connectButton.textContent = TorStrings.torConnect.tryAgain; } - this.uiState.bootstrapCause = UIStates.ConnectToTor; - this.saveUIState(); } - showBootstrapping(state) { - const showProgressbar = true; + showBootstrapping(trigger, tryAgain) { let title = ""; let description = ""; const breadcrumbs = [ @@ -516,128 +473,114 @@ class AboutTorConnect { BreadcrumbStatus.Disabled, BreadcrumbStatus.Disabled, ]; - switch (this.uiState.bootstrapCause) { - case UIStates.ConnectToTor: + switch (trigger) { + case "Start": + case "Offline": breadcrumbs[0] = BreadcrumbStatus.Active; - title = state.HasEverFailed + title = tryAgain ? TorStrings.torConnect.tryAgain : TorStrings.torConnect.torConnecting; description = TorStrings.settings.torPreferencesDescription; break; - case UIStates.ConnectionAssist: + case "ChooseRegion": breadcrumbs[2] = BreadcrumbStatus.Active; title = TorStrings.torConnect.tryingBridge; description = TorStrings.torConnect.assistDescription; break; - case UIStates.CouldNotLocate: + case "RegionNotFound": breadcrumbs[2] = BreadcrumbStatus.Active; title = TorStrings.torConnect.tryingBridgeAgain; description = TorStrings.torConnect.errorLocationDescription; break; - case UIStates.LocationConfirm: + case "ConfirmRegion": breadcrumbs[2] = BreadcrumbStatus.Active; title = TorStrings.torConnect.tryingBridgeAgain; description = TorStrings.torConnect.isLocationCorrectDescription; break; + default: + console.warn("Unrecognized bootstrap trigger", trigger); + break; } this.setTitle(title, ""); this.showConfigureConnectionLink(description); - this.setProgress("", showProgressbar, state.BootstrapProgress); - if (state.HasEverFailed) { + this.elements.progressDescription.textContent = ""; + if (tryAgain) { this.setBreadcrumbsStatus(...breadcrumbs); } else { this.hideBreadcrumbs(); } this.hideButtons(); - if (state.ShowViewLog) { - this.show(this.elements.viewLogButton); - } else { - this.hide(this.elements.viewLogButton); - } this.show(this.elements.cancelButton); - if (state.StateChanged) { - this.elements.cancelButton.focus(); - } + this.elements.cancelButton.focus(); } - showOffline(state) { + showOffline() { this.setTitle(TorStrings.torConnect.noInternet, "offline"); this.setLongText(TorStrings.torConnect.noInternetDescription); - this.setProgress(this.getMaybeLocalizedError(state), false); + this.elements.progressDescription.textContent = + TorStrings.torConnect.offline; this.setBreadcrumbsStatus( BreadcrumbStatus.Default, BreadcrumbStatus.Active, BreadcrumbStatus.Hidden ); - this.show(this.elements.viewLogButton); this.hideButtons(); this.show(this.elements.configureButton); this.show(this.elements.connectButton, true); this.elements.connectButton.textContent = TorStrings.torConnect.tryAgain; } - showConnectionAssistant(state) { + showChooseRegion(error) { this.setTitle(TorStrings.torConnect.couldNotConnect, "assist"); this.showConfigureConnectionLink(TorStrings.torConnect.assistDescription); - this.setProgress(this.getMaybeLocalizedError(state), false); + this.elements.progressDescription.textContent = + this.getMaybeLocalizedError(error); this.setBreadcrumbsStatus( BreadcrumbStatus.Default, BreadcrumbStatus.Active, BreadcrumbStatus.Disabled ); - this.showLocationForm(false, TorStrings.torConnect.tryBridge); - if (state?.StateChanged) { - this.elements.tryBridgeButton.focus(); - } - this.uiState.bootstrapCause = UIStates.ConnectionAssist; - this.saveUIState(); + this.showLocationForm(true, TorStrings.torConnect.tryBridge); + this.elements.tryBridgeButton.focus(); } - showCouldNotLocate(state) { - this.uiState.allowAutomaticLocation = false; + showRegionNotFound() { this.setTitle(TorStrings.torConnect.errorLocation, "location"); this.showConfigureConnectionLink( TorStrings.torConnect.errorLocationDescription ); - this.setProgress(TorStrings.torConnect.cannotDetermineCountry, false); + this.elements.progressDescription.textContent = + TorStrings.torConnect.cannotDetermineCountry; this.setBreadcrumbsStatus( BreadcrumbStatus.Default, BreadcrumbStatus.Active, BreadcrumbStatus.Disabled ); - this.show(this.elements.viewLogButton); - this.showLocationForm(true, TorStrings.torConnect.tryBridge); - if (state.StateChanged) { - this.elements.tryBridgeButton.focus(); - } - this.uiState.bootstrapCause = UIStates.CouldNotLocate; - this.saveUIState(); + this.showLocationForm(false, TorStrings.torConnect.tryBridge); + this.elements.tryBridgeButton.focus(); } - showLocationConfirmation(state) { + showConfirmRegion(error) { this.setTitle(TorStrings.torConnect.isLocationCorrect, "location"); this.showConfigureConnectionLink( TorStrings.torConnect.isLocationCorrectDescription ); - this.setProgress(this.getMaybeLocalizedError(state), false); + this.elements.progressDescription.textContent = + this.getMaybeLocalizedError(error); this.setBreadcrumbsStatus( BreadcrumbStatus.Default, BreadcrumbStatus.Default, BreadcrumbStatus.Active ); - this.show(this.elements.viewLogButton); - this.showLocationForm(true, TorStrings.torConnect.tryAgain); - if (state.StateChanged) { - this.elements.tryBridgeButton.focus(); - } - this.uiState.bootstrapCause = UIStates.LocationConfirm; - this.saveUIState(); + this.showLocationForm(false, TorStrings.torConnect.tryAgain); + this.elements.tryBridgeButton.focus(); } - showFinalError(state) { + showFinalError(error) { this.setTitle(TorStrings.torConnect.finalError, "final"); this.setLongText(TorStrings.torConnect.finalErrorDescription); - this.setProgress(this.getMaybeLocalizedError(state), false); + this.elements.progressDescription.textContent = + this.getMaybeLocalizedError(error); this.setBreadcrumbsStatus( BreadcrumbStatus.Default, BreadcrumbStatus.Default, @@ -665,7 +608,7 @@ class AboutTorConnect { } } - showLocationForm(isError, buttonLabel) { + showLocationForm(isChoose, buttonLabel) { this.hideButtons(); RPMSendQuery("torconnect:get-country-codes").then(codes => { if (codes && codes.length) { @@ -674,7 +617,7 @@ class AboutTorConnect { } }); let firstOpt = this.elements.locationDropdownSelect.options[0]; - if (this.uiState.allowAutomaticLocation) { + if (isChoose) { firstOpt.value = "automatic"; firstOpt.textContent = TorStrings.torConnect.automatic; } else { @@ -685,7 +628,7 @@ class AboutTorConnect { this.validateLocation(); this.show(this.elements.locationDropdownLabel); this.show(this.elements.locationDropdown); - this.elements.locationDropdownLabel.classList.toggle("error", isError); + this.elements.locationDropdownLabel.classList.toggle("error", !isChoose); this.show(this.elements.tryBridgeButton, true); if (buttonLabel !== undefined) { this.elements.tryBridgeButton.textContent = buttonLabel; @@ -697,12 +640,8 @@ class AboutTorConnect { return this.elements.locationDropdownSelect.options[selectedIndex].value; } - setLocation(code) { - if (!code) { - code = this.uiState.selectedLocation; - } else { - this.uiState.selectedLocation = code; - } + setLocation() { + const code = this.selectedLocation; if (this.getLocation() === code) { return; } @@ -726,13 +665,7 @@ class AboutTorConnect { document.documentElement.setAttribute("dir", direction); this.elements.connectToTorLink.addEventListener("click", () => { - if (this.uiState.currentState === UIStates.ConnectToTor) { - return; - } - this.transitionUIState(UIStates.ConnectToTor, null); - RPMSendAsyncMessage("torconnect:broadcast-user-action", { - uiState: UIStates.ConnectToTor, - }); + RPMSendAsyncMessage("torconnect:start-again"); }); this.elements.connectToTorLabel.textContent = TorStrings.torConnect.torConnect; @@ -747,10 +680,7 @@ class AboutTorConnect { ) { return; } - this.transitionUIState(UIStates.ConnectionAssist, null); - RPMSendAsyncMessage("torconnect:broadcast-user-action", { - uiState: UIStates.ConnectionAssist, - }); + RPMSendAsyncMessage("torconnect:choose-region"); }); this.elements.connectionAssistLabel.textContent = TorStrings.torConnect.breadcrumbAssist; @@ -786,23 +716,18 @@ class AboutTorConnect { this.elements.cancelButton.textContent = TorStrings.torConnect.cancel; this.elements.cancelButton.addEventListener("click", () => { - this.cancelBootstrap(); + this.cancelBootstrapping(); }); this.elements.connectButton.textContent = TorStrings.torConnect.torConnectButton; this.elements.connectButton.addEventListener("click", () => { - this.beginBootstrap(); + this.beginBootstrapping(); }); this.populateLocations(); this.elements.locationDropdownSelect.addEventListener("change", () => { - this.uiState.selectedLocation = this.getLocation(); - this.saveUIState(); this.validateLocation(); - RPMSendAsyncMessage("torconnect:broadcast-user-action", { - location: this.uiState.selectedLocation, - }); }); this.elements.locationDropdownLabel.textContent = @@ -811,10 +736,8 @@ class AboutTorConnect { this.elements.tryBridgeButton.textContent = TorStrings.torConnect.tryBridge; this.elements.tryBridgeButton.addEventListener("click", () => { const value = this.getLocation(); - if (value === "automatic") { - this.beginAutoBootstrap(); - } else { - this.beginAutoBootstrap(value); + if (value) { + this.beginAutoBootstrapping(value); } }); @@ -846,17 +769,14 @@ class AboutTorConnect { initObservers() { // TorConnectParent feeds us state blobs to we use to update our UI - RPMAddMessageListener("torconnect:state-change", ({ data }) => { - this.updateUI(data); + RPMAddMessageListener("torconnect:stage-change", ({ data }) => { + this.updateStage(data); }); - RPMAddMessageListener("torconnect:user-action", ({ data }) => { - if (data.location) { - this.uiState.selectedLocation = data.location; - this.setLocation(); - } - if (data.uiState !== undefined) { - this.transitionUIState(data.uiState, data.connState); - } + RPMAddMessageListener("torconnect:bootstrap-progress", ({ data }) => { + this.updateBootstrappingStatus(data); + }); + RPMAddMessageListener("torconnect:quickstart-change", ({ data }) => { + this.updateQuickstart(data); }); } @@ -866,7 +786,7 @@ class AboutTorConnect { // integers, so we must resort to a string compare here :( // see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code for relevant documentation if (evt.code === "Escape") { - this.cancelBootstrap(); + this.cancelBootstrapping(); } }; } @@ -876,23 +796,14 @@ class AboutTorConnect { // various constants TorStrings = Object.freeze(args.TorStrings); - TorConnectState = Object.freeze(args.TorConnectState); - InternetStatus = Object.freeze(args.InternetStatus); this.locations = args.CountryNames; this.initElements(args.Direction); this.initObservers(); this.initKeyboardShortcuts(); - if (Object.keys(args.State.UIState).length) { - this.uiState = args.State.UIState; - } else { - args.State.UIState = this.uiState; - this.saveUIState(); - } - this.uiStates[this.uiState.currentState](args.State); - // populate UI based on current state - this.updateUI(args.State); + this.updateStage(args.stage); + this.updateQuickstart(args.quickstartEnabled); } } ===================================== toolkit/components/torconnect/content/torConnectTitlebarStatus.js ===================================== @@ -38,7 +38,7 @@ var gTorConnectTitlebarStatus = { // The title also acts as an accessible name for the role="status". this.node.setAttribute("title", this._strings.titlebarStatusName); - this._observeTopic = TorConnectTopics.StateChange; + this._observeTopic = TorConnectTopics.StageChange; this._stateListener = { observe: (subject, topic) => { if (topic !== this._observeTopic) { @@ -66,17 +66,16 @@ var gTorConnectTitlebarStatus = { let textId; let connected = false; let potentiallyBlocked = false; - switch (TorConnect.state) { - case TorConnectState.Disabled: + switch (TorConnect.stageName) { + case TorConnectStage.Disabled: // Hide immediately. this.node.hidden = true; return; - case TorConnectState.Bootstrapped: + case TorConnectStage.Bootstrapped: textId = "titlebarStatusConnected"; connected = true; break; - case TorConnectState.Bootstrapping: - case TorConnectState.AutoBootstrapping: + case TorConnectStage.Bootstrapping: textId = "titlebarStatusConnecting"; break; default: ===================================== toolkit/components/torconnect/content/torConnectUrlbarButton.js ===================================== @@ -55,13 +55,13 @@ var gTorConnectUrlbarButton = { this.connect(); }); - this._observeTopic = TorConnectTopics.StateChange; + this._observeTopic = TorConnectTopics.StageChange; this._stateListener = { observe: (subject, topic) => { if (topic !== this._observeTopic) { return; } - this._torConnectStateChanged(); + this._torConnectStageChanged(); }, }; Services.obs.addObserver(this._stateListener, this._observeTopic); @@ -84,7 +84,7 @@ var gTorConnectUrlbarButton = { // switching selected browser. gBrowser.addProgressListener(this._locationListener); - this._torConnectStateChanged(); + this._torConnectStageChanged(); }, /** @@ -105,17 +105,17 @@ var gTorConnectUrlbarButton = { * Begin the tor connection bootstrapping process. */ connect() { - TorConnect.openTorConnect({ beginBootstrap: true }); + TorConnect.openTorConnect({ beginBootstrapping: "soft" }); }, /** - * Callback for when the TorConnect state changes. + * Callback for when the TorConnect stage changes. */ - _torConnectStateChanged() { - if (TorConnect.state === TorConnectState.Disabled) { + _torConnectStageChanged() { + if (TorConnect.stageName === TorConnectStage.Disabled) { // NOTE: We do not uninit early when we reach the - // TorConnectState.Bootstrapped state because we can still leave the - // Bootstrapped state if the tor process exists early and needs a restart. + // TorConnectStage.Bootstrapped stage because we can still leave the + // Bootstrapped stage if the tor process exists early and needs a restart. this.uninit(); return; } ===================================== toolkit/modules/RemotePageAccessManager.sys.mjs ===================================== @@ -239,19 +239,19 @@ export let RemotePageAccessManager = { }, "about:torconnect": { RPMAddMessageListener: [ - "torconnect:state-change", - "torconnect:user-action", + "torconnect:stage-change", + "torconnect:bootstrap-progress", + "torconnect:quickstart-change", ], RPMSendAsyncMessage: [ "torconnect:open-tor-preferences", - "torconnect:begin-bootstrap", - "torconnect:begin-autobootstrap", - "torconnect:cancel-bootstrap", + "torconnect:begin-bootstrapping", + "torconnect:cancel-bootstrapping", "torconnect:set-quickstart", "torconnect:view-tor-logs", "torconnect:restart", - "torconnect:set-ui-state", - "torconnect:broadcast-user-action", + "torconnect:start-again", + "torconnect:choose-region", ], RPMSendQuery: [ "torconnect:get-init-args", ===================================== toolkit/modules/TorAndroidIntegration.sys.mjs ===================================== @@ -83,6 +83,7 @@ class TorAndroidIntegrationImpl { observe(subj, topic) { switch (topic) { + // TODO: Replace with StageChange. case lazy.TorConnectTopics.StateChange: lazy.EventDispatcher.instance.sendRequest({ type: EmittedEvents.connectStateChanged, @@ -101,6 +102,7 @@ class TorAndroidIntegrationImpl { type: EmittedEvents.bootstrapComplete, }); break; + // TODO: Replace with StageChange stage.error. case lazy.TorConnectTopics.Error: lazy.EventDispatcher.instance.sendRequest({ type: EmittedEvents.connectError, @@ -159,17 +161,23 @@ class TorAndroidIntegrationImpl { await lazy.TorSettings.saveToPrefs(); break; case ListenedEvents.bootstrapBegin: - lazy.TorConnect.beginBootstrap(); + lazy.TorConnect.beginBootstrapping(); break; case ListenedEvents.bootstrapBeginAuto: - lazy.TorConnect.beginAutoBootstrap(data.countryCode); + // TODO: The countryCode should be set to "automatic" by the caller + // rather than `null`, so we can just pass in `data.countryCode` + // directly. + lazy.TorConnect.beginBootstrapping(data.countryCode || "automatic"); break; case ListenedEvents.bootstrapCancel: - lazy.TorConnect.cancelBootstrap(); + lazy.TorConnect.cancelBootstrapping(); break; + // TODO: Replace with TorConnect.stage. case ListenedEvents.bootstrapGetState: callback?.onSuccess(lazy.TorConnect.state); return; + // TODO: Expose TorConnect.startAgain() to allow users to begin + // from the start again. } callback?.onSuccess(); } catch (e) { ===================================== toolkit/modules/TorConnect.sys.mjs ===================================== @@ -8,7 +8,6 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs", - EventDispatcher: "resource://gre/modules/Messaging.sys.mjs", MoatRPC: "resource://gre/modules/Moat.sys.mjs", TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs", TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs", @@ -79,240 +78,181 @@ ChromeUtils.defineLazyGetter(lazy, "logger", () => }) ); -/* - TorConnect State Transitions - - ┌─────────┐ ┌────────┐ - │ ▼ ▼ │ - │ ┌──────────────────────────────────────────────────────────┐ │ - ┌─┼────── │ Error │ ◀───┐ │ - │ │ └──────────────────────────────────────────────────────────┘ │ │ - │ │ ▲ │ │ - │ │ │ │ │ - │ │ │ │ │ - │ │ ┌───────────────────────┐ ┌──────────┐ │ │ - │ │ ┌──── │ Initial │ ────────────────────▶ │ Disabled │ │ │ - │ │ │ └───────────────────────┘ └──────────┘ │ │ - │ │ │ │ │ │ - │ │ │ │ beginBootstrap() │ │ - │ │ │ ▼ │ │ - │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ - │ │ │ │ Bootstrapping │ ────┘ │ - │ │ │ └──────────────────────────────────────────────────────────┘ │ - │ │ │ │ ▲ │ │ - │ │ │ │ cancelBootstrap() │ beginBootstrap() └────┐ │ - │ │ │ ▼ │ │ │ - │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ - │ │ └───▶ │ │ ─┼────┘ - │ │ │ │ │ - │ │ │ │ │ - │ │ │ Configuring │ │ - │ │ │ │ │ - │ │ │ │ │ - └─┼─────▶ │ │ │ - │ └──────────────────────────────────────────────────────────┘ │ - │ │ ▲ ▲ │ - │ │ beginAutoBootstrap() │ cancelBootstrap() │ │ - │ ▼ │ │ │ - │ ┌───────────────────────┐ │ │ │ - └────── │ AutoBootstrapping │ ─┘ │ │ - └───────────────────────┘ │ │ - │ │ │ - │ ┌────────────────────────────────┘ │ - ▼ │ │ - ┌───────────────────────┐ │ - │ Bootstrapped │ ◀───────────────────────────────────┘ - └───────────────────────┘ -*/ - /* Topics Notified by the TorConnect module */ export const TorConnectTopics = Object.freeze({ + StageChange: "torconnect:stage-change", + // TODO: Remove torconnect:state-change when pages have switched to stage. StateChange: "torconnect:state-change", BootstrapProgress: "torconnect:bootstrap-progress", BootstrapComplete: "torconnect:bootstrap-complete", + // TODO: Remove torconnect:error when pages have switched to stage. Error: "torconnect:error", }); -// The StateCallback is the base class to implement the various states. -// All states should extend it and implement a `run` function, which can -// optionally be async, and define an array of valid transitions. -// The parent class will handle everything else, including the transition to -// other states when the run function is complete etc... -// A system is also provided to allow this function to early-out. The runner -// should check the transitioning getter when appropriate and return. -// In addition to that, a state can implement a transitionRequested callback, -// which can be used in conjunction with a mechanism like Promise.race. -// This allows to handle, for example, users' requests to cancel a bootstrap -// attempt. -// A state can optionally define a cleanup function, that will be run in all -// cases before transitioning to the next state. -class StateCallback { - #state; - #promise; - #transitioning = false; - - constructor(stateName) { - this.#state = stateName; - } - - async begin(...args) { - lazy.logger.trace(`Entering ${this.#state} state`); - // Make sure we always have an actual promise. - try { - this.#promise = Promise.resolve(this.run(...args)); - } catch (err) { - this.#promise = Promise.reject(err); - } - try { - // If the callback throws, transition to error as soon as possible. - await this.#promise; - lazy.logger.info(`${this.#state}'s run is done`); - } catch (err) { - if (this.transitioning) { - lazy.logger.error( - `A transition from ${ - this.#state - } is already happening, silencing this exception.`, - err - ); - return; - } - lazy.logger.error( - `${this.#state}'s run threw, transitioning to the Error state.`, - err - ); - this.changeState(TorConnectState.Error, err); - } - } - - async end(nextState) { - lazy.logger.trace( - `Ending state ${this.#state} (to transition to ${nextState})` - ); - - if (this.#transitioning) { - // Should we check turn this into an error? - // It will make dealing with the error state harder. - lazy.logger.warn("this.#transitioning is already true."); - } - - // Signal we should bail out ASAP. - this.#transitioning = true; - if (this.transitionRequested) { - this.transitionRequested(); - } - - lazy.logger.debug( - `Waiting for the ${ - this.#state - }'s callback to return before the transition.` - ); - try { - await this.#promise; - } finally { - lazy.logger.debug(`Calling ${this.#state}'s cleanup, if implemented.`); - if (this.cleanup) { - try { - await this.cleanup(nextState); - lazy.logger.debug(`${this.#state}'s cleanup function done.`); - } catch (e) { - lazy.logger.warn(`${this.#state}'s cleanup function threw.`, e); - } - } - } - } - - changeState(stateName, ...args) { - TorConnect._changeState(stateName, ...args); - } - - get transitioning() { - return this.#transitioning; - } - - get state() { - return this.#state; - } -} - -// async method to sleep for a given amount of time -const debugSleep = async ms => { - return new Promise(resolve => { - setTimeout(resolve, ms); - }); -}; - -class InitialState extends StateCallback { - allowedTransitions = Object.freeze([ - TorConnectState.Disabled, - TorConnectState.Bootstrapping, - TorConnectState.Configuring, - TorConnectState.Error, - ]); - - constructor() { - super(TorConnectState.Initial); - } - - run() { - // TODO: Block this transition until we successfully build a TorProvider. - } -} - -class ConfiguringState extends StateCallback { - allowedTransitions = Object.freeze([ - TorConnectState.AutoBootstrapping, - TorConnectState.Bootstrapping, - TorConnectState.Error, - ]); - - constructor() { - super(TorConnectState.Configuring); - } - - run() { - TorConnect._bootstrapProgress = 0; - } -} - -class BootstrappingState extends StateCallback { +/** + * @callback ProgressCallback + * + * @param {integer} progress - The progress percent. + */ +/** + * @typedef {object} BootstrapOptions + * + * Options for a bootstrap attempt. + * + * @property {boolean} [options.simulateCensorship] - Whether to simulate a + * failing bootstrap. + * @property {integer} [options.simulateDelay] - The delay in microseconds to + * apply to simulated bootstraps. + * @property {object} [options.simulateMoatResponse] - Simulate a Moat response + * for circumvention settings. Should include a "settings" property, and + * optionally a "country" property. You may add a "simulateCensorship" + * property to some of the settings to make only their bootstrap attempts + * fail. + * @property {boolean} [options.testInternet] - Whether to also test the + * internet connection. + * @property {boolean} [options.simulateOffline] - Whether to simulate an + * offline test result. This will not cause the bootstrap to fail. + * @property {string} [options.regionCode] - The region code to use to fetch + * auto-bootstrap settings, or "automatic" to automatically choose the region. + */ +/** + * @typedef {object} BootstrapResult + * + * The result of a bootstrap attempt. + * + * @property {string} [result] - The bootstrap result. + * @property {Error} [error] - An error from the attempt. + */ +/** + * @callback ResolveBootstrap + * + * Resolve a bootstrap attempt. + * + * @param {BootstrapResult} - The result, or error. + */ + +/** + * Each instance can be used to attempt one bootstrapping. + */ +class BootstrapAttempt { + /** + * The ongoing bootstrap request. + * + * @type {?TorBootstrapRequest} + */ #bootstrap = null; + /** + * The error returned by the bootstrap request, if any. + * + * @type {?Error} + */ #bootstrapError = null; + /** + * The ongoing internet test, if any. + * + * @type {?InternetTest} + */ #internetTest = null; + /** + * The method to call to complete the `run` promise. + * + * @type {?ResolveBootstrap} + */ + #resolveRun = null; + /** + * Whether the `run` promise has been, or is about to be, resolved. + * + * @type {boolean} + */ + #resolved = false; + /** + * Whether a cancel request has been started. + * + * @type {boolean} + */ #cancelled = false; - allowedTransitions = Object.freeze([ - TorConnectState.Configuring, - TorConnectState.Bootstrapped, - TorConnectState.Error, - ]); + /** + * Run a bootstrap attempt. + * + * @param {ProgressCallback} progressCallback - The callback to invoke with + * the bootstrap progress. + * @param {BootstrapOptions} options - Options to apply to the bootstrap. + * + * @return {Promise<string, Error>} - The result of the bootstrap. + */ + run(progressCallback, options) { + const { promise, resolve, reject } = Promise.withResolvers(); + this.#resolveRun = arg => { + if (this.#resolved) { + // Already been called once. + if (arg.error) { + lazy.logger.error("Delayed bootstrap error", arg.error); + } + return; + } + this.#resolved = true; + try { + // Should be ok to call this twice in the case where we "cancel" the + // bootstrap. + this.#internetTest?.cancel(); + } catch (error) { + lazy.logger.error("Unexpected error in bootstrap cleanup", error); + } + if (arg.error) { + reject(arg.error); + } else { + resolve(arg.result); + } + }; + try { + this.#runInternal(progressCallback, options); + } catch (error) { + this.#resolveRun({ error }); + } - constructor() { - super(TorConnectState.Bootstrapping); + return promise; } - async run() { - if (await this.#simulateCensorship()) { - return; + /** + * Run the attempt. + * + * @param {ProgressCallback} progressCallback - The callback to invoke with + * the bootstrap progress. + * @param {BootstrapOptions} options - Options to apply to the bootstrap. + */ + #runInternal(progressCallback, options) { + if (options.simulateCensorship) { + // Create a fake request. + this.#bootstrap = { + _timeout: 0, + bootstrap() { + this._timeout = setTimeout(() => { + const err = new Error("Censorship simulation"); + err.phase = "conn"; + err.reason = "noroute"; + this.onbootstraperror(err); + }, options.simulateDelay || 0); + }, + cancel() { + clearTimeout(this._timeout); + }, + }; + } else { + this.#bootstrap = new lazy.TorBootstrapRequest(); } - this.#bootstrap = new lazy.TorBootstrapRequest(); - this.#bootstrap.onbootstrapstatus = (progress, status) => { - TorConnect._updateBootstrapProgress(progress, status); + this.#bootstrap.onbootstrapstatus = (progress, _status) => { + if (!this.#resolved) { + progressCallback(progress); + } }; this.#bootstrap.onbootstrapcomplete = () => { - this.#internetTest.cancel(); - this.changeState(TorConnectState.Bootstrapped); + this.#resolveRun({ result: "complete" }); }; this.#bootstrap.onbootstraperror = error => { - if (this.#cancelled) { - // We ignore this error since it occurred after cancelling (by the - // user). We assume the error is just a side effect of the cancelling. - // E.g. If the cancelling is triggered late in the process, we get - // "Building circuits: Establishing a Tor circuit failed". - // TODO: Maybe move this logic deeper in the process to know when to - // filter out such errors triggered by cancelling. - lazy.logger.warn("Post-cancel error.", error); + if (this.#bootstrapError) { + lazy.logger.warn("Another bootstrap error", error); return; } // We have to wait for the Internet test to finish before sending the @@ -320,30 +260,40 @@ class BootstrappingState extends StateCallback { this.#bootstrapError = error; this.#maybeTransitionToError(); }; - - this.#internetTest = new InternetTest(); - this.#internetTest.onResult = status => { - TorConnect._internetStatus = status; - this.#maybeTransitionToError(); - }; - this.#internetTest.onError = () => { - this.#maybeTransitionToError(); - }; + if (options.testInternet) { + this.#internetTest = new InternetTest(options.simulateOffline); + this.#internetTest.onResult = () => { + this.#maybeTransitionToError(); + }; + this.#internetTest.onError = () => { + this.#maybeTransitionToError(); + }; + } this.#bootstrap.bootstrap(); } - async cleanup(nextState) { - if (nextState === TorConnectState.Configuring) { - // stop bootstrap process if user cancelled - this.#cancelled = true; - this.#internetTest?.cancel(); - await this.#bootstrap?.cancel(); + /** + * Callback for when we get a new bootstrap error or a change in the internet + * status. + */ + #maybeTransitionToError() { + if (this.#resolved || this.#cancelled) { + if (this.#bootstrapError) { + // We ignore this error since it occurred after cancelling (by the + // user), or we have already resolved. We assume the error is just a + // side effect of the cancelling. + // E.g. If the cancelling is triggered late in the process, we get + // "Building circuits: Establishing a Tor circuit failed". + // TODO: Maybe move this logic deeper in the process to know when to + // filter out such errors triggered by cancelling. + lazy.logger.warn("Post-complete error.", this.#bootstrapError); + } + return; } - } - #maybeTransitionToError() { if ( + this.#internetTest && this.#internetTest.status === InternetStatus.Unknown && this.#internetTest.error === null && this.#internetTest.enabled @@ -355,356 +305,394 @@ class BootstrappingState extends StateCallback { // us again. return; } - // Do not transition to the offline error until we are sure that also the - // bootstrap failed, in case Moat is down but the bootstrap can proceed - // anyway. + // Do not transition to "offline" until we are sure that also the bootstrap + // failed, in case Moat is down but the bootstrap can proceed anyway. if (!this.#bootstrapError) { return; } - if (this.#internetTest.status === InternetStatus.Offline) { - this.changeState( - TorConnectState.Error, - new TorConnectError(TorConnectError.Offline) - ); - } else { - // Give priority to the bootstrap error, in case the Internet test fails - TorConnect._hasBootstrapEverFailed = true; - this.changeState( - TorConnectState.Error, - new TorConnectError( - TorConnectError.BootstrapError, + if (this.#internetTest?.status === InternetStatus.Offline) { + if (this.#bootstrapError) { + lazy.logger.info( + "Ignoring bootstrap error since offline.", this.#bootstrapError - ) - ); - } - } - - async #simulateCensorship() { - // debug hook to simulate censorship preventing bootstrapping - const censorshipLevel = Services.prefs.getIntPref( - TorConnectPrefs.censorship_level, - 0 - ); - if (censorshipLevel <= 0) { - return false; - } - - await debugSleep(1500); - if (this.transitioning) { - // Already left this state. - return true; + ); + } + this.#resolveRun({ result: "offline" }); + return; } - TorConnect._hasBootstrapEverFailed = true; - if (censorshipLevel === 2) { - const codes = Object.keys(TorConnect._countryNames); - TorConnect._detectedLocation = - codes[Math.floor(Math.random() * codes.length)]; - } - const err = new Error("Censorship simulation"); - err.phase = "conn"; - err.reason = "noroute"; - this.changeState( - TorConnectState.Error, - new TorConnectError(TorConnectError.BootstrapError, err) - ); - return true; - } -} - -class AutoBootstrappingState extends StateCallback { - #moat; - #settings; - #changedSettings = false; - #transitionPromise; - #transitionResolve; - - allowedTransitions = Object.freeze([ - TorConnectState.Configuring, - TorConnectState.Bootstrapped, - TorConnectState.Error, - ]); - - constructor() { - super(TorConnectState.AutoBootstrapping); - this.#transitionPromise = new Promise(resolve => { - this.#transitionResolve = resolve; + this.#resolveRun({ + error: new TorConnectError( + TorConnectError.BootstrapError, + this.#bootstrapError + ), }); } - async run(countryCode) { - if (await this.#simulateCensorship(countryCode)) { - return; - } - await this.#initMoat(); - if (this.transitioning) { + /** + * Cancel the bootstrap attempt. + */ + async cancel() { + if (this.#cancelled) { + lazy.logger.warn( + "Cancelled bootstrap after it has already been cancelled" + ); return; } - await this.#fetchSettings(countryCode); - if (this.transitioning) { + this.#cancelled = true; + if (this.#resolved) { + lazy.logger.warn("Cancelled bootstrap after it has already resolved"); return; } - await this.#trySettings(); + // Wait until after bootstrap.cancel returns before we resolve with + // cancelled. In particular, there is a small chance that the bootstrap + // completes, in which case we want to be able to resolve with a success + // instead. + this.#internetTest?.cancel(); + await this.#bootstrap?.cancel(); + this.#resolveRun({ result: "cancelled" }); } +} +/** + * Each instance can be used to attempt one auto-bootstrapping sequence. + */ +class AutoBootstrapAttempt { /** - * Simulate a censorship event, if needed. + * The current bootstrap attempt, if any. * - * @param {string} countryCode The country code passed to the state - * @returns {Promise<boolean>} true if we are simulating the censorship and - * the bootstrap should stop immediately, or false if the bootstrap should - * continue normally. + * @type {?BootstrapAttempt} */ - async #simulateCensorship(countryCode) { - const censorshipLevel = Services.prefs.getIntPref( - TorConnectPrefs.censorship_level, - 0 - ); - if (censorshipLevel <= 0) { - return false; - } + #bootstrapAttempt = null; + /** + * The method to call to complete the `run` promise. + * + * @type {?ResolveBootstrap} + */ + #resolveRun = null; + /** + * Whether the `run` promise has been, or is about to be, resolved. + * + * @type {boolean} + */ + #resolved = false; + /** + * Whether a cancel request has been started. + * + * @type {boolean} + */ + #cancelled = false; + /** + * The method to call when the cancelled value is set to true. + * + * @type {?Function} + */ + #resolveCancelled = null; + /** + * A promise that resolves when the cancelled value is set to true. We can use + * this with Promise.race to end early when the user cancels. + * + * @type {?Promise} + */ + #cancelledPromise = null; + /** + * The found settings from Moat. + * + * @type {?object[]} + */ + #settings = null; + /** + * The last settings that have been applied to the TorProvider, if any. + * + * @type {?object} + */ + #changedSetting = null; + /** + * The detected region code returned by Moat, if any. + * + * @type {?string} + */ + detectedRegion = null; - // Very severe censorship: always fail even after manually selecting - // location specific settings. - if (censorshipLevel === 3) { - await debugSleep(2500); - if (!this.transitioning) { - this.changeState( - TorConnectState.Error, - new TorConnectError(TorConnectError.AllSettingsFailed) - ); + /** + * Run an auto-bootstrap attempt. + * + * @param {ProgressCallback} progressCallback - The callback to invoke with + * the bootstrap progress. + * @param {BootstrapOptions} options - Options to apply to the bootstrap. + * + * @return {Promise<string, Error>} - The result of the bootstrap. + */ + run(progressCallback, options) { + const { promise, resolve, reject } = Promise.withResolvers(); + + this.#resolveRun = async arg => { + if (this.#resolved) { + // Already been called once. + if (arg.error) { + lazy.logger.error("Delayed auto-bootstrap error", arg.error); + } + return; } - return true; - } - - // Severe censorship: only fail after auto selecting, but succeed after - // manually selecting a country. - if (censorshipLevel === 2 && !countryCode) { - await debugSleep(2500); - if (!this.transitioning) { - this.changeState( - TorConnectState.Error, - new TorConnectError(TorConnectError.CannotDetermineCountry) - ); + this.#resolved = true; + try { + // Run cleanup before we resolve the promise to ensure two instances + // of AutoBootstrapAttempt are not trying to change the settings at + // the same time. + if (this.#changedSetting) { + if (arg.result === "complete") { + // Persist the current settings to preferences. + lazy.TorSettings.setSettings(this.#changedSetting); + lazy.TorSettings.saveToPrefs(); + } // else, applySettings will restore the current settings. + await lazy.TorSettings.applySettings(); + } + } catch (error) { + lazy.logger.error("Unexpected error in auto-bootstrap cleanup", error); } - return true; - } + if (arg.error) { + reject(arg.error); + } else { + resolve(arg.result); + } + }; - return false; - } + ({ promise: this.#cancelledPromise, resolve: this.#resolveCancelled } = + Promise.withResolvers()); - /** - * Initialize the MoatRPC to communicate with the backend. - */ - async #initMoat() { - this.#moat = new lazy.MoatRPC(); - // We need to wait Moat's initialization even when we are requested to - // transition to another state to be sure its uninit will have its intended - // effect. So, do not use Promise.race here. - await this.#moat.init(); + this.#runInternal(progressCallback, options).catch(error => { + this.#resolveRun({ error }); + }); + + return promise; } /** - * Lookup user's potential censorship circumvention settings from Moat - * service. + * Run the attempt. + * + * Note, this is an async method, but should *not* be awaited by the `run` + * method. + * + * @param {ProgressCallback} progressCallback - The callback to invoke with + * the bootstrap progress. + * @param {BootstrapOptions} options - Options to apply to the bootstrap. */ - async #fetchSettings(countryCode) { - // For now, throw any errors we receive from the backend, except when it was - // unable to detect user's country/region. - // If we use specialized error objects, we could pass the original errors to - // them. - const maybeSettings = await Promise.race([ - this.#moat.circumvention_settings( - [...lazy.TorSettings.builtinBridgeTypes, "vanilla"], - countryCode - ), - // This might set maybeSettings to undefined. - this.#transitionPromise, - ]); - if (maybeSettings?.country) { - TorConnect._detectedLocation = maybeSettings.country; - } - - if (maybeSettings?.settings && maybeSettings.settings.length) { - this.#settings = maybeSettings.settings; - } else if (!this.transitioning) { - // Keep consistency with the other call. - this.#settings = await Promise.race([ - this.#moat.circumvention_defaults([ - ...lazy.TorSettings.builtinBridgeTypes, - "vanilla", - ]), - // This might set this.#settings to undefined. - this.#transitionPromise, - ]); + async #runInternal(progressCallback, options) { + await this.#fetchSettings(options); + if (this.#cancelled || this.#resolved) { + return; } - if (!this.#settings?.length && !this.transitioning) { - if (!TorConnect._detectedLocation) { - // unable to determine country - throw new TorConnectError(TorConnectError.CannotDetermineCountry); - } else { - // no settings available for country - throw new TorConnectError(TorConnectError.NoSettingsForCountry); - } + if (!this.#settings?.length) { + this.#resolveRun({ + error: new TorConnectError( + options.regionCode === "automatic" && !this.detectedRegion + ? TorConnectError.CannotDetermineCountry + : TorConnectError.NoSettingsForCountry + ), + }); } - } - /** - * Try to apply the settings we fetched. - */ - async #trySettings() { - // Otherwise, apply each of our settings and try to bootstrap with each. + // Apply each of our settings and try to bootstrap with each. for (const [index, currentSetting] of this.#settings.entries()) { - if (this.transitioning) { - break; - } - lazy.logger.info( `Attempting Bootstrap with configuration ${index + 1}/${ this.#settings.length }` ); - // Send the new settings directly to the provider. We will save them only - // if the bootstrap succeeds. - // FIXME: We should somehow signal TorSettings users that we have set - // custom settings, and they should not apply theirs until we are done - // with trying ours. - // Otherwise, the new settings provided by the user while we were - // bootstrapping could be the ones that cause the bootstrap to succeed, - // but we overwrite them (unless we backup the original settings, and then - // save our new settings only if they have not changed). - // Another idea (maybe easier to implement) is to disable the settings - // UI while *any* bootstrap is going on. - // This is also documented in tor-browser#41921. - const provider = await lazy.TorProviderBuilder.build(); - this.#changedSettings = true; - // We need to merge with old settings, in case the user is using a proxy - // or is behind a firewall. - await provider.writeSettings({ - ...lazy.TorSettings.getSettings(), - ...currentSetting, - }); - - // Build out our bootstrap request. - const bootstrap = new lazy.TorBootstrapRequest(); - bootstrap.onbootstrapstatus = (progress, status) => { - TorConnect._updateBootstrapProgress(progress, status); - }; - bootstrap.onbootstraperror = error => { - lazy.logger.error("Auto-Bootstrap error", error); - }; + await this.#trySetting(currentSetting, progressCallback, options); - // Begin the bootstrap. - const success = await Promise.race([ - bootstrap.bootstrap(), - this.#transitionPromise, - ]); - // Either the bootstrap request has finished, or a transition (caused by - // an error or by user's cancelation) started. - // However, we cannot be already transitioning in case of success, so if - // we are we should cancel the current bootstrap. - // With the current TorProvider, this will set DisableNetwork=1 again, - // which is what the user wanted if they canceled. - if (this.transitioning) { - if (success) { - lazy.logger.warn( - "We were already transitioning after a success, we were not expecting this." - ); - } - bootstrap.cancel(); - return; - } - if (success) { - // Persist the current settings to preferences. - lazy.TorSettings.setSettings(currentSetting); - lazy.TorSettings.saveToPrefs(); - // Do not await `applySettings`. Otherwise this opens up a window of - // time where the user can still "Cancel" the bootstrap. - // We are calling `applySettings` just to be on the safe side, but the - // settings we are passing now should be exactly the same we already - // passed earlier. - lazy.TorSettings.applySettings().catch(e => - lazy.logger.error("TorSettings.applySettings threw unexpectedly.", e) - ); - this.changeState(TorConnectState.Bootstrapped); + if (this.#cancelled || this.#resolved) { return; } } - // Only explicitly change state here if something else has not transitioned - // us. - if (!this.transitioning) { - throw new TorConnectError(TorConnectError.AllSettingsFailed); - } - } - - transitionRequested() { - this.#transitionResolve(); + this.#resolveRun({ + error: new TorConnectError(TorConnectError.AllSettingsFailed), + }); } - async cleanup(nextState) { - // No need to await. - this.#moat?.uninit(); - this.#moat = null; + /** + * Lookup user's potential censorship circumvention settings from Moat + * service. + * + * @param {BootstrapOptions} options - Options to apply to the bootstrap. + */ + async #fetchSettings(options) { + if (options.simulateMoatResponse) { + await Promise.race([ + new Promise(res => setTimeout(res, options.simulateDelay || 0)), + this.#cancelledPromise, + ]); - if (this.#changedSettings && nextState !== TorConnectState.Bootstrapped) { - try { - await lazy.TorSettings.applySettings(); - } catch (e) { - // We cannot do much if the original settings were bad or - // if the connection closed, so just report it in the - // console. - lazy.logger.warn("Failed to restore original settings.", e); + if (this.#cancelled || this.#resolved) { + return; } + + this.detectedRegion = options.simulateMoatResponse.country || null; + this.#settings = options.simulateMoatResponse.settings ?? null; + + return; } - } -} -class BootstrappedState extends StateCallback { - // We may need to leave the bootstrapped state if the tor daemon - // exits (if it is restarted, we will have to bootstrap again). - allowedTransitions = Object.freeze([TorConnectState.Configuring]); + const moat = new lazy.MoatRPC(); + try { + // We need to wait Moat's initialization even when we are requested to + // transition to another state to be sure its uninit will have its + // intended effect. So, do not use Promise.race here. + await moat.init(); - constructor() { - super(TorConnectState.Bootstrapped); - } + if (this.#cancelled || this.#resolved) { + return; + } - run() { - // Notify observers of bootstrap completion. - Services.obs.notifyObservers(null, TorConnectTopics.BootstrapComplete); + // For now, throw any errors we receive from the backend, except when it + // was unable to detect user's country/region. + // If we use specialized error objects, we could pass the original errors + // to them. + const maybeSettings = await Promise.race([ + moat.circumvention_settings( + [...lazy.TorSettings.builtinBridgeTypes, "vanilla"], + options.regionCode === "automatic" ? null : options.regionCode + ), + // This might set maybeSettings to undefined. + this.#cancelledPromise, + ]); + if (this.#cancelled || this.#resolved) { + return; + } + + this.detectedRegion = maybeSettings?.country || null; + + if (maybeSettings?.settings?.length) { + this.#settings = maybeSettings.settings; + } else { + // Keep consistency with the other call. + this.#settings = await Promise.race([ + moat.circumvention_defaults([ + ...lazy.TorSettings.builtinBridgeTypes, + "vanilla", + ]), + // This might set this.#settings to undefined. + this.#cancelledPromise, + ]); + } + } finally { + // Do not await the uninit. + moat.uninit(); + } } -} -class ErrorState extends StateCallback { - allowedTransitions = Object.freeze([TorConnectState.Configuring]); + /** + * Try to apply the settings we fetched. + * + * @param {object} setting - The setting to try. + * @param {ProgressCallback} progressCallback - The callback to invoke with + * the bootstrap progress. + * @param {BootstrapOptions} options - Options to apply to the bootstrap. + */ + async #trySetting(setting, progressCallback, options) { + if (this.#cancelled || this.#resolved) { + return; + } - static #hasEverHappened = false; + if (options.simulateMoatResponse && setting.simulateCensorship) { + // Move the simulateCensorship option to the options for the next + // BootstrapAttempt. + setting = structuredClone(setting); + delete setting.simulateCensorship; + options = { ...options, simulateCensorship: true }; + } - constructor() { - super(TorConnectState.Error); - ErrorState.#hasEverHappened = true; - } + // Send the new settings directly to the provider. We will save them only + // if the bootstrap succeeds. + // FIXME: We should somehow signal TorSettings users that we have set + // custom settings, and they should not apply theirs until we are done + // with trying ours. + // Otherwise, the new settings provided by the user while we were + // bootstrapping could be the ones that cause the bootstrap to succeed, + // but we overwrite them (unless we backup the original settings, and then + // save our new settings only if they have not changed). + // Another idea (maybe easier to implement) is to disable the settings + // UI while *any* bootstrap is going on. + // This is also documented in tor-browser#41921. + const provider = await lazy.TorProviderBuilder.build(); + this.#changedSetting = setting; + // We need to merge with old settings, in case the user is using a proxy + // or is behind a firewall. + await provider.writeSettings({ + ...lazy.TorSettings.getSettings(), + ...setting, + }); - run(_error) { - this.changeState(TorConnectState.Configuring); - } + if (this.#cancelled || this.#resolved) { + return; + } - static get hasEverHappened() { - return ErrorState.#hasEverHappened; - } -} + let result; + try { + this.#bootstrapAttempt = new BootstrapAttempt(); + // At this stage, cancelling AutoBootstrap will also cancel this + // bootstrapAttempt. + result = await this.#bootstrapAttempt.run(progressCallback, options); + } catch (error) { + // Only re-try with the next settings *if* we have a BootstrapError. + // Other errors will end this auto-bootstrap attempt entirely. + if ( + error instanceof TorConnectError && + error.code === TorConnectError.BootstrapError + ) { + lazy.logger.info("TorConnect setting failed", setting, error); + // Try with the next settings. + // NOTE: We do not restore the user settings in between these runs. + // Instead we wait for #resolveRun callback to do so. + // This means there is a window of time where the setting is applied, but + // no bootstrap is running. + return; + } + // Pass error up. + throw error; + } finally { + this.#bootstrapAttempt = null; + } -class DisabledState extends StateCallback { - // Trap state: no way to leave the Disabled state. - allowedTransitions = Object.freeze([]); + if (this.#cancelled || this.#resolved) { + return; + } - constructor() { - super(TorConnectState.Disabled); + // Pass the BootstrapAttempt result up. + this.#resolveRun({ result }); } - async run() { - lazy.logger.debug("Entered the disabled state."); + /** + * Cancel the bootstrap attempt. + */ + async cancel() { + if (this.#cancelled) { + lazy.logger.warn( + "Cancelled auto-bootstrap after it has already been cancelled" + ); + return; + } + this.#cancelled = true; + this.#resolveCancelled(); + if (this.#resolved) { + lazy.logger.warn( + "Cancelled auto-bootstrap after it has already resolved" + ); + return; + } + + // Wait until after bootstrap.cancel returns before we resolve with + // cancelled. In particular, there is a small chance that the bootstrap + // completes, in which case we want to be able to resolve with a success + // instead. + if (this.#bootstrapAttempt) { + this.#bootstrapAttempt.cancel(); + await this.#bootstrapAttempt; + } + // In case no bootstrap is running, we resolve with "cancelled". + this.#resolveRun({ result: "cancelled" }); } } @@ -721,8 +709,11 @@ class InternetTest { #pending = false; #canceled = false; #timeout = 0; + #simulateOffline = false; + + constructor(simulateOffline) { + this.#simulateOffline = simulateOffline; - constructor() { this.#enabled = Services.prefs.getBoolPref( TorConnectPrefs.allow_internet_test, true @@ -752,6 +743,19 @@ class InternetTest { this.#canceled = false; lazy.logger.info("Starting the Internet test"); + + if (this.#simulateOffline) { + await new Promise(res => setTimeout(res, 500)); + + this.#status = InternetStatus.Offline; + + if (this.#canceled) { + return; + } + this.onResult(this.#status); + return; + } + const mrpc = new lazy.MoatRPC(); try { await mrpc.init(); @@ -792,27 +796,173 @@ class InternetTest { return this.#status; } - get error() { - return this.#error; - } + get error() { + return this.#error; + } + + get enabled() { + return this.#enabled; + } + + // We randomize the Internet test timeout to make fingerprinting it harder, at + // least a little bit... + #timeoutRand() { + const offset = 30000; + const randRange = 5000; + return offset + randRange * (Math.random() * 2 - 1); + } +} + +export const TorConnectStage = Object.freeze({ + Disabled: "Disabled", + Loading: "Loading", + Start: "Start", + Bootstrapping: "Bootstrapping", + Offline: "Offline", + ChooseRegion: "ChooseRegion", + RegionNotFound: "RegionNotFound", + ConfirmRegion: "ConfirmRegion", + FinalError: "FinalError", + Bootstrapped: "Bootstrapped", +}); + +/** + * @typedef {object} ConnectStage + * + * A summary of the user stage. + * + * @property {string} name - The name of the stage. + * @property {string} defaultRegion - The default region to show in the UI. + * @property {?string} bootstrapTrigger - The TorConnectStage prior to this + * bootstrap attempt. Only set during the "Bootstrapping" stage. + * @property {?BootstrapError} error - The last bootstrapping error. + * @property {boolean} tryAgain - Whether a bootstrap attempt has failed, so + * that a normal bootstrap should be shown as "Try Again" instead of + * "Connect". NOTE: to be removed when about:torconnect no longer uses + * breadcrumbs. + * @property {boolean} potentiallyBlocked - Whether bootstrapping has ever + * failed, not including being cancelled or being offline. I.e. whether we + * have reached an error stage at some point before being bootstrapped. + * @property {BootstrappingStatus} bootstrappingStatus - The current + * bootstrapping status. + */ + +/** + * @typedef {object} BootstrappingStatus + * + * The status of a bootstrap. + * + * @property {number} progress - The percent progress. + * @property {boolean} hasWarning - Whether this bootstrap has a warning in the + * Tor log. + */ + +/** + * @typedef {object} BootstrapError + * + * Details about the error that caused bootstrapping to fail. + * + * @property {string} code - The error code type. + * @property {string} message - The error message. + * @property {?string} phase - The bootstrapping phase that failed. + * @property {?string} reason - The bootstrapping failure reason. + */ + +export const TorConnect = { + /** + * Default bootstrap options for simulation. + * + * @type {BootstrapOptions} + */ + simulateBootstrapOptions: {}, + + /** + * The name of the current stage the user is in. + * + * @type {string} + */ + _stageName: TorConnectStage.Loading, + + get stageName() { + return this._stageName; + }, + + /** + * The stage that triggered bootstrapping. + * + * @type {?string} + */ + _bootstrapTrigger: null, + + /** + * The alternative stage that we should move to after bootstrapping completes. + * + * @type {?string} + */ + _requestedStage: null, + + /** + * The default region to show in the UI for auto-bootstrapping. + * + * @type {string} + */ + _defaultRegion: "automatic", + + /** + * The current bootstrap attempt, if any. + * + * @type {?(BootstrapAttempt|AutoBootstrapAttempt)} + */ + _bootstrapAttempt: null, + + /** + * The bootstrap error that was last generated. + * + * @type {?TorConnectError} + */ + _errorDetails: null, + + /** + * Whether a bootstrap attempt has failed, so that a normal bootstrap should + * be shown as "Try Again" instead of "Connect". + * + * @type {boolean} + */ + // TODO: Drop tryAgain when we remove breadcrumbs and use "Start again" + // instead. + _tryAgain: false, - get enabled() { - return this.#enabled; - } + /** + * Whether bootstrapping has ever returned an error. + * + * @type {boolean} + */ + _potentiallyBlocked: false, - // We randomize the Internet test timeout to make fingerprinting it harder, at - // least a little bit... - #timeoutRand() { - const offset = 30000; - const randRange = 5000; - return offset + randRange * (Math.random() * 2 - 1); - } -} + /** + * Get a summary of the current user stage. + * + * @type {ConnectStage} + */ + get stage() { + return { + name: this._stageName, + defaultRegion: this._defaultRegion, + bootstrapTrigger: this._bootstrapTrigger, + error: this._errorDetails + ? { + code: this._errorDetails.code, + message: String(this._errorDetails.message ?? ""), + phase: this._errorDetails.cause?.phase ?? null, + reason: this._errorDetails.cause?.reason ?? null, + } + : null, + tryAgain: this._tryAgain, + potentiallyBlocked: this._potentiallyBlocked, + bootstrappingStatus: structuredClone(this._bootstrappingStatus), + }; + }, -export const TorConnect = { - _stateHandler: new InitialState(), - _bootstrapProgress: 0, - _internetStatus: InternetStatus.Unknown, // list of country codes Moat has settings for _countryCodes: [], _countryNames: Object.freeze( @@ -826,109 +976,28 @@ export const TorConnect = { return codesNames; })() ), - _detectedLocation: "", - _errorCode: null, - _errorDetails: null, - _logHasWarningOrError: false, - _hasBootstrapEverFailed: false, - _transitionPromise: null, // This is used as a helper to make the state of about:torconnect persistent // during a session, but TorConnect does not use this data at all. _uiState: {}, - _stateCallbacks: Object.freeze( - new Map([ - // Initial is never transitioned to - [TorConnectState.Initial, InitialState], - [TorConnectState.Configuring, ConfiguringState], - [TorConnectState.Bootstrapping, BootstrappingState], - [TorConnectState.AutoBootstrapping, AutoBootstrappingState], - [TorConnectState.Bootstrapped, BootstrappedState], - [TorConnectState.Error, ErrorState], - [TorConnectState.Disabled, DisabledState], - ]) - ), - - _makeState(state) { - const klass = this._stateCallbacks.get(state); - if (!klass) { - throw new Error(`${state} is not a valid state.`); - } - return new klass(); - }, - - async _changeState(newState, ...args) { - if (this._stateHandler.transitioning) { - // Avoid an exception to prevent it to be propagated to the original - // begin call. - lazy.logger.warn("Already transitioning"); - return; - } - const prevState = this._stateHandler; - - // ensure this is a valid state transition - if (!prevState.allowedTransitions.includes(newState)) { - throw Error( - `TorConnect: Attempted invalid state transition from ${prevState.state} to ${newState}` - ); - } - - lazy.logger.trace( - `Try transitioning from ${prevState.state} to ${newState}`, - args - ); - try { - await prevState.end(newState); - } catch (e) { - // We take for granted that the begin of this state will call us again, - // to request the transition to the error state. - if (newState !== TorConnectState.Error) { - lazy.logger.debug( - `Refusing the transition from ${prevState.state} to ${newState} because the previous state threw.` - ); - return; - } - } - - // Set our new state first so that state transitions can themselves - // trigger a state transition. - this._stateHandler = this._makeState(newState); - - // Error signal needs to be sent out before we enter the Error state. - // Expected on android `onBootstrapError` to set lastKnownError. - // Expected in about:torconnect to set the error codes and internet status - // *before* the StateChange signal. - if (newState === TorConnectState.Error) { - let error = args[0]; - if (!(error instanceof TorConnectError)) { - error = new TorConnectError(TorConnectError.ExternalError, error); - } - TorConnect._errorCode = error.code; - TorConnect._errorDetails = error; - lazy.logger.error(`Entering error state (${error.code})`, error); - - Services.obs.notifyObservers(error, TorConnectTopics.Error); - } - - Services.obs.notifyObservers( - { state: newState }, - TorConnectTopics.StateChange - ); - this._stateHandler.begin(...args); + /** + * The status of the most recent bootstrap attempt. + * + * @type {BootstrappingStatus} + */ + _bootstrappingStatus: { + progress: 0, + hasWarning: false, }, - _updateBootstrapProgress(progress, status) { - this._bootstrapProgress = progress; - - lazy.logger.info( - `Bootstrapping ${this._bootstrapProgress}% complete (${status})` - ); + /** + * Notify the bootstrap progress. + */ + _notifyBootstrapProgress() { + lazy.logger.debug("BootstrappingStatus", this._bootstrappingStatus); Services.obs.notifyObservers( - { - progress: TorConnect._bootstrapProgress, - hasWarnings: TorConnect._logHasWarningOrError, - }, + this._bootstrappingStatus, TorConnectTopics.BootstrapProgress ); }, @@ -936,62 +1005,54 @@ export const TorConnect = { // init should be called by TorStartupService init() { lazy.logger.debug("TorConnect.init()"); - this._stateHandler.begin(); if (!this.enabled) { // Disabled - this._changeState(TorConnectState.Disabled); - } else { - let observeTopic = addTopic => { - Services.obs.addObserver(this, addTopic); - lazy.logger.debug(`Observing topic '${addTopic}'`); - }; + this._setStage(TorConnectStage.Disabled); + return; + } - // Wait for TorSettings, as we will need it. - // We will wait for a TorProvider only after TorSettings is ready, - // because the TorProviderBuilder initialization might not have finished - // at this point, and TorSettings initialization is a prerequisite for - // having a provider. - // So, we prefer initializing TorConnect as soon as possible, so that - // the UI will be able to detect it is in the Initializing state and act - // consequently. - lazy.TorSettings.initializedPromise.then(() => - this._settingsInitialized() - ); + let observeTopic = addTopic => { + Services.obs.addObserver(this, addTopic); + lazy.logger.debug(`Observing topic '${addTopic}'`); + }; - // register the Tor topics we always care about - observeTopic(lazy.TorProviderTopics.ProcessExited); - observeTopic(lazy.TorProviderTopics.HasWarnOrErr); - } + // Wait for TorSettings, as we will need it. + // We will wait for a TorProvider only after TorSettings is ready, + // because the TorProviderBuilder initialization might not have finished + // at this point, and TorSettings initialization is a prerequisite for + // having a provider. + // So, we prefer initializing TorConnect as soon as possible, so that + // the UI will be able to detect it is in the Initializing state and act + // consequently. + lazy.TorSettings.initializedPromise.then(() => this._settingsInitialized()); + + // register the Tor topics we always care about + observeTopic(lazy.TorProviderTopics.ProcessExited); + observeTopic(lazy.TorProviderTopics.HasWarnOrErr); }, async observe(subject, topic) { lazy.logger.debug(`Observed ${topic}`); switch (topic) { - case lazy.TorProviderTopics.HasWarnOrErr: { - this._logHasWarningOrError = true; + case lazy.TorProviderTopics.HasWarnOrErr: + if (this._bootstrappingStatus.hasWarning) { + // No change. + return; + } + if (this._stageName === "Bootstrapping") { + this._bootstrappingStatus.hasWarning = true; + this._notifyBootstrapProgress(); + } break; - } - case lazy.TorProviderTopics.ProcessExited: { + case lazy.TorProviderTopics.ProcessExited: + 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. Services.prefs.setBoolPref(TorLauncherPrefs.prompt_at_startup, true); - switch (this.state) { - case TorConnectState.Bootstrapping: - case TorConnectState.AutoBootstrapping: - case TorConnectState.Bootstrapped: - // If we are in the bootstrap or auto bootstrap, we could go - // through the error phase (and eventually we might do it, if some - // transition calls fail). However, this would start the - // connection assist, so we go directly to configuring. - // FIXME: Find a better way to handle this. - this._changeState(TorConnectState.Configuring); - break; - // Other states naturally resolve in configuration. - } + this._makeStageRequest(TorConnectStage.Start, true); break; - } default: // ignore break; @@ -1003,29 +1064,47 @@ export const TorConnect = { // daemon when it exits (tor-browser#21053, tor-browser#41921). await lazy.TorProviderBuilder.build(); - // tor-browser#41907: This is only a workaround to avoid users being - // bounced back to the initial panel without any explanation. - // Longer term we should disable the clickable elements, or find a UX - // to prevent this from happening (e.g., allow buttons to be clicked, - // but show an intermediate starting state, or a message that tor is - // starting while the butons are disabled, etc...). - // Notice that currently the initial state does not do anything. - // Instead of just waiting, we could move this code in its callback. - // See also tor-browser#41921. - if (this.state !== TorConnectState.Initial) { - lazy.logger.warn( - "The TorProvider was built after the state had already changed." - ); - return; - } lazy.logger.debug("The TorProvider is ready, changing state."); + // NOTE: If the tor process exits before this point, then + // shouldQuickStart would be `false`. + // NOTE: At this point, _requestedStage should still be `null`. + this._setStage(TorConnectStage.Start); if (this.shouldQuickStart) { // Quickstart - this._changeState(TorConnectState.Bootstrapping); - } else { - // Configuring - this._changeState(TorConnectState.Configuring); + this.beginBootstrapping(); + } + }, + + /** + * Set the user stage. + * + * @param {string} name - The name of the stage to move to. + */ + _setStage(name) { + if (this._bootstrapAttempt) { + throw new Error(`Trying to set the stage to ${name} during a bootstrap`); + } + + lazy.logger.info(`Entering stage ${name}`); + const prevState = this.state; + this._stageName = name; + this._bootstrappingStatus.hasWarning = false; + this._bootstrappingStatus.progress = + name === TorConnectStage.Bootstrapped ? 100 : 0; + + Services.obs.notifyObservers(this.stage, TorConnectTopics.StageChange); + + // TODO: Remove when all pages have switched to stage. + const newState = this.state; + if (prevState !== newState) { + Services.obs.notifyObservers( + { state: newState }, + TorConnectTopics.StateChange + ); } + + // Update the progress after the stage has changed. + this._notifyBootstrapProgress(); }, /* @@ -1049,33 +1128,41 @@ export const TorConnect = { return ( this.enabled && // if we have succesfully bootstraped, then no need to show TorConnect - this.state !== TorConnectState.Bootstrapped + this._stageName !== TorConnectStage.Bootstrapped ); }, /** - * Whether bootstrapping can currently begin. + * Whether we are in a stage that can lead into the Bootstrapping stage. I.e. + * whether we can make a "normal" or "auto" bootstrapping request. * - * The value may change with TorConnectTopics.StateChanged. + * The value may change with TorConnectTopics.StageChanged. * * @param {boolean} */ get canBeginBootstrap() { - return this._stateHandler.allowedTransitions.includes( - TorConnectState.Bootstrapping + return ( + this._stageName === TorConnectStage.Start || + this._stageName === TorConnectStage.Offline || + this._stageName === TorConnectStage.ChooseRegion || + this._stageName === TorConnectStage.RegionNotFound || + this._stageName === TorConnectStage.ConfirmRegion ); }, /** - * Whether auto-bootstrapping can currently begin. + * Whether we are in an error stage that can lead into the Bootstrapping + * stage. I.e. whether we can make an "auto" bootstrapping request. * - * The value may change with TorConnectTopics.StateChanged. + * The value may change with TorConnectTopics.StageChanged. * * @param {boolean} */ get canBeginAutoBootstrap() { - return this._stateHandler.allowedTransitions.includes( - TorConnectState.AutoBootstrapping + return ( + this._stageName === TorConnectStage.ChooseRegion || + this._stageName === TorConnectStage.RegionNotFound || + this._stageName === TorConnectStage.ConfirmRegion ); }, @@ -1088,16 +1175,39 @@ export const TorConnect = { ); }, + // TODO: Remove when all pages have switched to "stage". get state() { - return this._stateHandler.state; - }, - - get bootstrapProgress() { - return this._bootstrapProgress; - }, - - get internetStatus() { - return this._internetStatus; + // There is no "Error" stage, but about:torconnect relies on receiving the + // Error state to update its display. So we temporarily set the stage for a + // StateChange signal. + if (this._isErrorState) { + return TorConnectState.Error; + } + switch (this._stageName) { + case TorConnectStage.Disabled: + return TorConnectState.Disabled; + case TorConnectStage.Loading: + return TorConnectState.Initial; + case TorConnectStage.Start: + case TorConnectStage.Offline: + case TorConnectStage.ChooseRegion: + case TorConnectStage.RegionNotFound: + case TorConnectStage.ConfirmRegion: + case TorConnectStage.FinalError: + return TorConnectState.Configuring; + case TorConnectStage.Bootstrapping: + if ( + this._bootstrapTrigger === TorConnectStage.Start || + this._bootstrapTrigger === TorConnectStage.Offline + ) { + return TorConnectState.Bootstrapping; + } + return TorConnectState.AutoBootstrapping; + case TorConnectStage.Bootstrapped: + return TorConnectState.Bootstrapped; + } + lazy.logger.error(`Unknown state at stage ${this._stageName}`); + return null; }, get countryCodes() { @@ -1108,92 +1218,414 @@ export const TorConnect = { return this._countryNames; }, - get detectedLocation() { - return this._detectedLocation; + /** + * Whether the Bootstrapping process has ever failed, not including being + * cancelled or being offline. + * + * The value may change with TorConnectTopics.StageChanged. + * + * @type {boolean} + */ + get potentiallyBlocked() { + return this._potentiallyBlocked; }, - get errorCode() { - return this._errorCode; + /** + * Ensure that we are not disabled. + */ + _ensureEnabled() { + if (!this.enabled || this._stageName === TorConnectStage.Disabled) { + throw new Error("Unexpected Disabled stage for user method"); + } }, - get errorDetails() { - return this._errorDetails; - }, + /** + * Signal an error to listeners. + * + * @param {Error} error - The error. + */ + _signalError(error) { + // TODO: Replace this method with _setError without any signalling when + // pages have switched to stage. + // Currently it simulates the old behaviour for about:torconnect. + lazy.logger.debug("Signalling error", error); + + if (!(error instanceof TorConnectError)) { + error = new TorConnectError(TorConnectError.ExternalError, error); + } + this._errorDetails = error; - get logHasWarningOrError() { - return this._logHasWarningOrError; + // Temporarily set an error state for listeners. + // We send the Error signal before the "StateChange" signal. + // Expected on android `onBootstrapError` to set lastKnownError. + // Expected in about:torconnect to set the error codes and internet status + // *before* the StateChange signal. + this._isErrorState = true; + Services.obs.notifyObservers(error, TorConnectTopics.Error); + Services.obs.notifyObservers( + { state: this.state }, + TorConnectTopics.StateChange + ); + this._isErrorState = false; }, /** - * Whether we have ever entered the Error state. + * Add simulation options to the bootstrap request. * - * @type {boolean} + * @param {BootstrapOptions} bootstrapOptions - The options to add to. + * @param {string} [regionCode] - The region code being used. */ - get hasEverFailed() { - return ErrorState.hasEverHappened; + _addSimulateOptions(bootstrapOptions, regionCode) { + if (this.simulateBootstrapOptions.simulateCensorship) { + bootstrapOptions.simulateCensorship = true; + } + if (this.simulateBootstrapOptions.simulateDelay) { + bootstrapOptions.simulateDelay = + this.simulateBootstrapOptions.simulateDelay; + } + if (this.simulateBootstrapOptions.simulateOffline) { + bootstrapOptions.simulateOffline = true; + } + if (this.simulateBootstrapOptions.simulateMoatResponse) { + bootstrapOptions.simulateMoatResponse = + this.simulateBootstrapOptions.simulateMoatResponse; + } + + const censorshipLevel = Services.prefs.getIntPref( + TorConnectPrefs.censorship_level, + 0 + ); + if (censorshipLevel > 0 && !bootstrapOptions.simulateDelay) { + bootstrapOptions.simulateDelay = 1500; + } + if (censorshipLevel === 1) { + // Bootstrap fails, but auto-bootstrap does not. + if (!regionCode) { + bootstrapOptions.simulateCensorship = true; + } + } else if (censorshipLevel === 2) { + // Bootstrap fails. Auto-bootstrap fails with ConfirmRegion when using + // auto-detect region, but succeeds otherwise. + if (!regionCode) { + bootstrapOptions.simulateCensorship = true; + } + if (regionCode === "automatic") { + bootstrapOptions.simulateCensorship = true; + bootstrapOptions.simulateMoatResponse = { + country: "fi", + settings: [{}, {}], + }; + } + } else if (censorshipLevel === 3) { + // Bootstrap and auto-bootstrap fail. + bootstrapOptions.simulateCensorship = true; + bootstrapOptions.simulateMoatResponse = { + country: null, + settings: [], + }; + } }, /** - * Whether the Bootstrapping process has ever failed, not including when it - * failed due to not being connected to the internet. + * Confirm that a bootstrapping can take place, and whether the given values + * are valid. * - * This does not include a failure in AutoBootstrapping. + * @param {string} [regionCode] - The region code passed in. * - * @type {boolean} + * @return {boolean} whether bootstrapping can proceed. */ - get potentiallyBlocked() { - return this._hasBootstrapEverFailed; - }, + _confirmBootstrapping(regionCode) { + this._ensureEnabled(); + + if (this._bootstrapAttempt) { + lazy.logger.warn( + "Already have an ongoing bootstrap attempt." + + ` Ignoring request with ${regionCode}.` + ); + return false; + } + + const currentStage = this._stageName; + + if (regionCode) { + if (!this.canBeginAutoBootstrap) { + lazy.logger.warn( + `Cannot begin auto bootstrap in stage ${currentStage}` + ); + return false; + } + if ( + regionCode === "automatic" && + currentStage !== TorConnectStage.ChooseRegion + ) { + lazy.logger.warn("Auto bootstrap is missing an explicit regionCode"); + return false; + } + return true; + } + + if (!this.canBeginBootstrap) { + lazy.logger.warn(`Cannot begin bootstrap in stage ${currentStage}`); + return false; + } + if (this.canBeginAutoBootstrap) { + // Only expect "auto" bootstraps to be triggered when in an error stage. + lazy.logger.warn( + `Expected a regionCode to bootstrap in stage ${currentStage}` + ); + return false; + } - get uiState() { - return this._uiState; + return true; }, - set uiState(newState) { - this._uiState = newState; + + /** + * Begin a bootstrap attempt. + * + * @param {string} [regionCode] - An optional region code string to use, or + * "automatic" to automatically determine the region. If given, will start + * an auto-bootstrap attempt. + */ + async beginBootstrapping(regionCode) { + lazy.logger.debug("TorConnect.beginBootstrapping()"); + + if (!this._confirmBootstrapping(regionCode)) { + return; + } + + const beginStage = this._stageName; + const bootstrapOptions = { regionCode }; + const bootstrapAttempt = regionCode + ? new AutoBootstrapAttempt() + : new BootstrapAttempt(); + + if (!regionCode) { + // Only test internet for the first bootstrap attempt. + // TODO: Remove this since we do not have user consent. tor-browser#42605. + bootstrapOptions.testInternet = true; + } + + this._addSimulateOptions(bootstrapOptions, regionCode); + + // NOTE: The only `await` in this method is for `bootstrapAttempt.run`. + // Moreover, we returned early if `_bootstrapAttempt` was non-`null`. + // Therefore, the method is effectively "locked" by `_bootstrapAttempt`, so + // there should only ever be one caller at a time. + + if (regionCode) { + // Set the default to what the user chose. + this._defaultRegion = regionCode; + } else { + // Reset the default region to show in the UI. + this._defaultRegion = "automatic"; + } + this._requestedStage = null; + this._bootstrapTrigger = beginStage; + this._setStage(TorConnectStage.Bootstrapping); + this._bootstrapAttempt = bootstrapAttempt; + + let error = null; + let result = null; + try { + result = await bootstrapAttempt.run(progress => { + this._bootstrappingStatus.progress = progress; + lazy.logger.info(`Bootstrapping ${progress}% complete`); + this._notifyBootstrapProgress(); + }, bootstrapOptions); + } catch (err) { + error = err; + } + + const requestedStage = this._requestedStage; + this._requestedStage = null; + this._bootstrapTrigger = null; + this._bootstrapAttempt = null; + + if (bootstrapAttempt.detectedRegion) { + this._defaultRegion = bootstrapAttempt.detectedRegion; + } + + if (result === "complete") { + // Reset tryAgain, potentiallyBlocked and errorDetails in case the tor + // process exists later on. + this._tryAgain = false; + this._potentiallyBlocked = false; + this._errorDetails = null; + + if (requestedStage) { + lazy.logger.warn( + `Ignoring ${requestedStage} request since we are bootstrapped` + ); + } + this._setStage(TorConnectStage.Bootstrapped); + Services.obs.notifyObservers(null, TorConnectTopics.BootstrapComplete); + return; + } + + if (requestedStage) { + lazy.logger.debug("Ignoring bootstrap result", result, error); + this._setStage(requestedStage); + return; + } + + if ( + result === "offline" && + (beginStage === TorConnectStage.Start || + beginStage === TorConnectStage.Offline) + ) { + this._tryAgain = true; + this._signalError(new TorConnectError(TorConnectError.Offline)); + + this._setStage(TorConnectStage.Offline); + return; + } + + if (error) { + lazy.logger.info("Bootstrap attempt error", error); + + this._tryAgain = true; + this._potentiallyBlocked = true; + + this._signalError(error); + + switch (beginStage) { + case TorConnectStage.Start: + case TorConnectStage.Offline: + this._setStage(TorConnectStage.ChooseRegion); + return; + case TorConnectStage.ChooseRegion: + // TODO: Uncomment for behaviour in tor-browser#42550. + /* + if (regionCode !== "automatic") { + // Not automatic. Go straight to the final error. + this._setStage(TorConnectStage.FinalError); + return; + } + */ + if (regionCode !== "automatic" || bootstrapAttempt.detectedRegion) { + this._setStage(TorConnectStage.ConfirmRegion); + return; + } + this._setStage(TorConnectStage.RegionNotFound); + return; + } + this._setStage(TorConnectStage.FinalError); + return; + } + + // Bootstrap was cancelled. + if (result !== "cancelled") { + lazy.logger.error(`Unexpected bootstrap result`, result); + } + + // TODO: Remove this Offline hack when pages use "stage". + if (beginStage === TorConnectStage.Offline) { + // Re-send the "Offline" error to push the pages back to "Offline". + this._signalError(new TorConnectError(TorConnectError.Offline)); + } + + // Return to the previous stage. + this._setStage(beginStage); }, - /* - These functions allow external consumers to tell TorConnect to transition states + /** + * Cancel an ongoing bootstrap attempt. */ + cancelBootstrapping() { + lazy.logger.debug("TorConnect.cancelBootstrapping()"); + + this._ensureEnabled(); + + if (!this._bootstrapAttempt) { + lazy.logger.warn("No bootstrap attempt to cancel"); + return; + } - beginBootstrap() { - lazy.logger.debug("TorConnect.beginBootstrap()"); - this._changeState(TorConnectState.Bootstrapping); + this._bootstrapAttempt.cancel(); }, - cancelBootstrap() { - lazy.logger.debug("TorConnect.cancelBootstrap()"); + /** + * Request the transition to the given stage. + * + * If we are bootstrapping, it will be cancelled and the stage will be + * transitioned to when it resolves. Otherwise, we will switch to the stage + * immediately. + * + * @param {string} stage - The stage to request. + * @param {boolean} [overideBootstrapped=false] - Whether the request can + * override the "Bootstrapped" stage. + */ + _makeStageRequest(stage, overrideBootstrapped = false) { + lazy.logger.debug(`Request for stage ${stage}`); + + this._ensureEnabled(); + + if (stage === this._stageName) { + lazy.logger.info(`Ignoring request for current stage ${stage}`); + return; + } if ( - this.state !== TorConnectState.AutoBootstrapping && - this.state !== TorConnectState.Bootstrapping + !overrideBootstrapped && + this._stageName === TorConnectStage.Bootstrapped ) { + lazy.logger.warn(`Cannot move to ${stage} when bootstrapped`); + return; + } + if (this._stageName === TorConnectStage.Loading) { + if (stage === TorConnectStage.Start) { + // Will transition to "Start" stage when loading completes. + lazy.logger.info("Still in the Loading stage"); + } else { + lazy.logger.warn(`Cannot move to ${stage} when Loading`); + } + return; + } + + if (!this._bootstrapAttempt) { + // Transition immediately. + this._setStage(stage); + return; + } + + if (this._requestedStage === stage) { + lazy.logger.info(`Already requesting stage ${stage}`); + return; + } + if (this._requestedStage) { lazy.logger.warn( - `Cannot cancel bootstrapping in the ${this.state} state` + `Overriding request for ${this._requestedStage} with ${stage}` ); - return; } - this._changeState(TorConnectState.Configuring); + // Move to stage *after* bootstrap completes. + this._requestedStage = stage; + this._bootstrapAttempt?.cancel(); }, - beginAutoBootstrap(countryCode) { - lazy.logger.debug("TorConnect.beginAutoBootstrap()"); - this._changeState(TorConnectState.AutoBootstrapping, countryCode); + /** + * Restart the TorConnect stage to the start. + */ + startAgain() { + this._makeStageRequest(TorConnectStage.Start); }, - /* - Further external commands and helper methods + /** + * Set the stage to be "ChooseRegion". */ - openTorPreferences() { - if (lazy.TorLauncherUtil.isAndroid) { - lazy.EventDispatcher.instance.sendRequest({ - type: "GeckoView:Tor:OpenSettings", - }); + chooseRegion() { + if (!this._potentiallyBlocked) { + lazy.logger.error("chooseRegion request before getting an error"); return; } - const win = lazy.BrowserWindowTracker.getTopWindow(); - win.switchToTabHavingURI("about:preferences#connection", true); + // NOTE: The ChooseRegion stage needs _errorDetails to be displayed in + // about:torconnect. The _potentiallyBlocked condition should be + // sufficient to ensure this. + this._makeStageRequest(TorConnectStage.ChooseRegion); }, + /* + Further external commands and helper methods + */ + /** * Open the "about:torconnect" tab. * @@ -1204,10 +1636,11 @@ export const TorConnect = { * potentially blocked. * * @param {object} [options] - extra options. - * @property {boolean} [options.beginBootstrap=false] - Whether to try and - * begin Bootstrapping. - * @property {string} [options.beginAutoBootstrap] - The location to use to - * begin AutoBootstrapping, if possible. + * @property {"soft"|"hard"} [options.beginBootstrapping] - Whether to try and + * begin bootstrapping. "soft" will only trigger the bootstrap if we are not + * `potentiallyBlocked`. "hard" will try begin the bootstrap regardless. + * @property {string} [options.regionCode] - A region to pass in for + * auto-bootstrapping. */ openTorConnect(options) { // FIXME: Should we move this to the about:torconnect actor? @@ -1215,25 +1648,23 @@ export const TorConnect = { win.switchToTabHavingURI("about:torconnect", true, { ignoreQueryString: true, }); - if ( - options?.beginBootstrap && - this.canBeginBootstrap && - !this.potentiallyBlocked - ) { - this.beginBootstrap(); + + if (!options?.beginBootstrapping || !this.canBeginBootstrap) { + return; } - // options.beginAutoBootstrap can be an empty string. - if ( - options?.beginAutoBootstrap !== undefined && - this.canBeginAutoBootstrap - ) { - this.beginAutoBootstrap(options.beginAutoBootstrap); + + if (options.beginBootstrapping === "hard") { + if (this.canBeginAutoBootstrap && !options.regionCode) { + // Treat as an addition startAgain request to first move back to the + // "Start" stage before bootstrapping. + this.startAgain(); + } + } else if (this.potentiallyBlocked) { + // Do not trigger the bootstrap if we have ever had an error. + return; } - }, - viewTorLogs() { - const win = lazy.BrowserWindowTracker.getTopWindow(); - win.switchToTabHavingURI("about:preferences#connection-viewlogs", true); + this.beginBootstrapping(options.regionCode); }, async getCountryCodes() { View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/bb3cd9… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/bb3cd9… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-128.4.0esr-14.0-1] 2 commits: Bug 373 - reenable webrtc builds
by Pier Angelo Vendrame (@pierov) 13 Nov '24

13 Nov '24
Pier Angelo Vendrame pushed to branch mullvad-browser-128.4.0esr-14.0-1 at The Tor Project / Applications / Mullvad Browser Commits: ce9c6700 by june wilde at 2024-10-31T20:58:36+00:00 Bug 373 - reenable webrtc builds - - - - - 385aa055 by june wilde at 2024-10-31T20:58:36+00:00 fixup! MB 320: Temporarily disable WebRTC and WDBA on Windows. - - - - - 4 changed files: - mozconfig-windows-x86_64 - third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.cc - third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc - third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h Changes: ===================================== mozconfig-windows-x86_64 ===================================== @@ -23,5 +23,4 @@ ac_add_options --disable-notification-server ac_add_options --disable-eme # tor-browser#320: Temporarily disable until we resolve the mingw problems. -ac_add_options --disable-webrtc ac_add_options --disable-default-browser-agent ===================================== third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.cc ===================================== @@ -10,9 +10,9 @@ #include "modules/desktop_capture/win/wgc_capture_session.h" -#include <DispatcherQueue.h> +#include <dispatcherqueue.h> #include <windows.graphics.capture.interop.h> -#include <windows.graphics.directX.direct3d11.interop.h> +#include <windows.graphics.directx.direct3d11.interop.h> #include <windows.graphics.h> #include <wrl/client.h> #include <wrl/event.h> @@ -181,9 +181,7 @@ HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) { if (!options.prefer_cursor_embedded()) { ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureSession2> session2; - if (SUCCEEDED(session_->QueryInterface( - ABI::Windows::Graphics::Capture::IID_IGraphicsCaptureSession2, - &session2))) { + if (SUCCEEDED(session_->QueryInterface(IID_PPV_ARGS(&session2)))) { session2->put_IsCursorCaptureEnabled(false); } } @@ -367,7 +365,7 @@ HRESULT WgcCaptureSession::ProcessFrame() { return hr; } - ComPtr<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> + ComPtr<ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> direct3DDxgiInterfaceAccess; hr = d3d_surface->QueryInterface(IID_PPV_ARGS(&direct3DDxgiInterfaceAccess)); if (FAILED(hr)) { ===================================== third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc ===================================== @@ -10,7 +10,7 @@ #include "modules/desktop_capture/win/wgc_capturer_win.h" -#include <DispatcherQueue.h> +#include <dispatcherqueue.h> #include <windows.foundation.metadata.h> #include <windows.graphics.capture.h> ===================================== third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h ===================================== @@ -11,7 +11,7 @@ #ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_ #define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_ -#include <DispatcherQueue.h> +#include <dispatcherqueue.h> #include <d3d11.h> #include <wrl/client.h> View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/57… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/57… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • ...
  • 2043
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.