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 -----
  • 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
  • 19931 discussions
[Git][tpo/applications/tor-browser-build][main] Bug 41566, 41567: Prepare Tor, Mullvad Browser 15.0a4
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch main at The Tor Project / Applications / tor-browser-build Commits: 3f55477b by Pier Angelo Vendrame at 2025-10-15T21:29:30+02:00 Bug 41566, 41567: Prepare Tor, Mullvad Browser 15.0a4 - - - - - 10 changed files: - projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt - projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt - projects/browser/config - projects/firefox/config - projects/geckoview/config - projects/go/config - projects/manual/config - projects/openssl/config - projects/translation/config - rbm.conf Changes: ===================================== projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt ===================================== @@ -1,3 +1,49 @@ +Mullvad Browser 15.0a4 - October 16 2025 + * All Platforms + * Updated Firefox to 140.4.0esr + * Updated NoScript to 13.2.1 + * Updated Mullvad Browser Extension to 0.9.5 + * Updated uBlock Origin to 1.66.4 + * Bug 477: Rebase Mullvad Browser Alpha onto 140.4.0esr [mullvad-browser] + * Bug 478: Update to the Mullvad Browser extension to 0.9.5 [mullvad-browser] + * Bug 19741: Opensearch (contextual search) does not obey FPI [tor-browser] + * Bug 43850: Modify the Contrast Control settings for RFP [tor-browser] + * Bug 43869: Hide pens with RFP [tor-browser] + * Bug 43900: Open newtab rather than firefoxview when unloading the last tab [tor-browser] + * Bug 44107: Switch tab search action is missing an icon [tor-browser] + * Bug 44108: Fix the new history sidebar [tor-browser] + * Bug 44123: Do not trim protocol off of URLs ever [tor-browser] + * Bug 44153: Test search engines [tor-browser] + * Bug 44159: Change or hide the sidebar settings description [tor-browser] + * Bug 44177: Remove more urlbar actions [tor-browser] + * Bug 44178: Search preservation does not work with duckduckgo in safest security level [tor-browser] + * Bug 44187: TLS session tickets leak Private Browsing mode [tor-browser] + * Bug 44213: Reduce linkability concerns of the "Search with" contextual search action [tor-browser] + * Bug 44214: Update letterboxing to reflect changes in ESR 140 [tor-browser] + * Bug 44215: Hide Firefox home settings in about:preferences [tor-browser] + * Bug 44221: Backport MozBug 1984333 Bump Spoofed Processor Count [tor-browser] + * Bug 44234: No images in PDF [tor-browser] + * Bug 44240: Typo on dom.security.https_first_add_exception_on_failure [tor-browser] + * Bug 44242: Hand over Security Level's WebAssembly controls to NoScript [tor-browser] + * Bug 44262: Disable adding search engines from HTML forms [tor-browser] + * Linux + * Bug 44227: Some CJK characters cannot be rendered by Tor which uses the Noto font family [tor-browser] + * Bug 41586: Replace Noto CJK with Jigmo on Linux [tor-browser-build] + * Build System + * All Platforms + * Bug 41373: Remove `_ALL` from mar filenames [tor-browser-build] + * Bug 41568: Update instructions for manually building 7zip [tor-browser-build] + * Bug 41579: Add zip to the list of Tor Browser Build dependencies [tor-browser-build] + * Bug 41588: Restore legacy channel support in projects/release/update_responses_config.yml [tor-browser-build] + * Bug 41589: Backport tor-browser-build-browser#41270: Add updater rewriterules to make 13.5.7 a watershed [tor-browser-build] + * Windows + * Bug 44167: Move the nsis-uninstall.patch to tor-browser repository [tor-browser] + * macOS + * Bug 41571: Work-around to prevent older 7z versions to break rcodesign. [tor-browser-build] + * Linux + * Bug 41558: Share descriptions between Linux packages and archives [tor-browser-build] + * Bug 41569: Use var/display_name in .desktop files [tor-browser-build] + Mullvad Browser 15.0a3 - September 18 2025 * All Platforms * Updated Firefox to 140.3.0esr ===================================== projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt ===================================== @@ -1,3 +1,73 @@ +Tor Browser 15.0a4 - October 16 2025 + * All Platforms + * Updated NoScript to 13.2.1 + * Updated OpenSSL to 3.5.4 + * Bug 19741: Opensearch (contextual search) does not obey FPI [tor-browser] + * Bug 43850: Modify the Contrast Control settings for RFP [tor-browser] + * Bug 43869: Hide pens with RFP [tor-browser] + * Bug 44068: Handle migration from meek-azure to meek built-in bridge type [tor-browser] + * Bug 44234: No images in PDF [tor-browser] + * Bug 44240: Typo on dom.security.https_first_add_exception_on_failure [tor-browser] + * Bug 44242: Hand over Security Level's WebAssembly controls to NoScript [tor-browser] + * Bug 44250: Rebase Tor Browser Alpha onto 140.4esr [tor-browser] + * Bug 41574: Update Snowflake builtin bridge lines [tor-browser-build] + * Windows + macOS + Linux + * Updated Firefox to 140.4.0esr + * Bug 43900: Open newtab rather than firefoxview when unloading the last tab [tor-browser] + * Bug 44101: Toolbar connection status is not visible when using vertical tabs [tor-browser] + * Bug 44107: Switch tab search action is missing an icon [tor-browser] + * Bug 44108: Fix the new history sidebar [tor-browser] + * Bug 44123: Do not trim protocol off of URLs ever [tor-browser] + * Bug 44153: Test search engines [tor-browser] + * Bug 44159: Change or hide the sidebar settings description [tor-browser] + * Bug 44177: Remove more urlbar actions [tor-browser] + * Bug 44178: Search preservation does not work with duckduckgo in safest security level [tor-browser] + * Bug 44184: Duckduckgo Onion Lite search does not work properly in safest when added as a search engine [tor-browser] + * Bug 44187: TLS session tickets leak Private Browsing mode [tor-browser] + * Bug 44192: Hovering unloaded tab causes console error [tor-browser] + * Bug 44213: Reduce linkability concerns of the "Search with" contextual search action [tor-browser] + * Bug 44214: Update letterboxing to reflect changes in ESR 140 [tor-browser] + * Bug 44215: Hide Firefox home settings in about:preferences [tor-browser] + * Bug 44221: Backport MozBug 1984333 Bump Spoofed Processor Count [tor-browser] + * Bug 44239: DDG HTML page and search results displayed incorrectly with Safest security setting [tor-browser] + * Bug 44262: Disable adding search engines from HTML forms [tor-browser] + * Linux + * Bug 44227: Some CJK characters cannot be rendered by Tor which uses the Noto font family [tor-browser] + * Bug 41586: Replace Noto CJK with Jigmo on Linux [tor-browser-build] + * Android + * Updated GeckoView to 140.4.0esr + * Bug 43401: Replace the constructor of Locale with a builder [tor-browser] + * Bug 43643: Clean out unused tor connect strings [tor-browser] + * Bug 43650: Survey banner behaves like a dialog on Android, rather than a card [tor-browser] + * Bug 43676: Preemptively disable unified trust panel by default so we are tracking for next ESR [tor-browser] + * Bug 44031: Implement YEC 2025 Takeover for Android Stable [tor-browser] + * Bug 44218: Tor Browser Alpha for Android (15.0a2) doesn't work on Huawei devices P20 and P30 [tor-browser] + * Bug 44237: Revoke access to all advertising ids available in Android [tor-browser] + * Build System + * All Platforms + * Bug 41568: Update instructions for manually building 7zip [tor-browser-build] + * Bug 41576: Build expert bundles outside containers [tor-browser-build] + * Bug 41579: Add zip to the list of Tor Browser Build dependencies [tor-browser-build] + * Bug 41588: Restore legacy channel support in projects/release/update_responses_config.yml [tor-browser-build] + * Bug 41589: Backport tor-browser-build-browser#41270: Add updater rewriterules to make 13.5.7 a watershed [tor-browser-build] + * Windows + macOS + Linux + * Bug 41373: Remove `_ALL` from mar filenames [tor-browser-build] + * Bug 44131: Generate torrc-defaults and put it in objdir post-build [tor-browser] + * Windows + Linux + Android + * Updated Go to 1.24.9 + * Windows + * Bug 44167: Move the nsis-uninstall.patch to tor-browser repository [tor-browser] + * macOS + * Bug 41571: Work-around to prevent older 7z versions to break rcodesign. [tor-browser-build] + * Linux + * Bug 41558: Share descriptions between Linux packages and archives [tor-browser-build] + * Bug 41569: Use var/display_name in .desktop files [tor-browser-build] + * Android + * Bug 44220: Disable the JS minifier as it produces invalid JS [tor-browser] + * Bug 41577: Minify JS with UglifyJS on Android x86 [tor-browser-build] + * Bug 41582: Drop --pack-dyn-relocs=relr [tor-browser-build] + * Bug 41583: Align tor and PTs to 16kB on Android [tor-browser-build] + Tor Browser 15.0a3 - September 18 2025 * All Platforms * Updated Tor to 0.4.9.3-alpha ===================================== projects/browser/config ===================================== @@ -113,16 +113,16 @@ input_files: enable: '[% ! c("var/android") %]' - filename: dmg-root enable: '[% ! c("var/android") %]' - - URL: https://addons.mozilla.org/firefox/downloads/file/4551629/noscript-13.0.9.x… + - URL: https://addons.mozilla.org/firefox/downloads/file/4593796/noscript-13.2.1.x… name: noscript - sha256sum: 525ab61aff21f67d10c3e20965a86ed479631549908a658f54692adc47d771f5 - - URL: https://addons.mozilla.org/firefox/downloads/file/4573289/ublock_origin-1.6… + sha256sum: 190297f3d1e55db0c65f9bc00460bea9b753939d428ea593d6cef27fde1ce69a + - URL: https://addons.mozilla.org/firefox/downloads/file/4578681/ublock_origin-1.6… name: ublock-origin - sha256sum: 155820bace5bf7c8ba547e27678a4a25251131c54904f02f5bf340996f6fdbdc + sha256sum: bc62cd930601212f1568964389352bbd4b1808466f2c9ac1198c754338077fb0 enable: '[% c("var/mullvad-browser") %]' - - URL: https://cdn.mullvad.net/browser-extension/0.9.4/mullvad-browser-extension-0… + - URL: https://cdn.mullvad.net/browser-extension/0.9.5/mullvad-browser-extension-0… name: mullvad-extension - sha256sum: c91fcf30fe9a998f45ddc92e083bca4cd7b6dbb24fc545391e155ed1ec13c8cb + sha256sum: c36f8d5447300621579f7f1408a25f25dac3929c867e7ae5db9fc8b82e80a4e9 enable: '[% c("var/mullvad-browser") %]' - filename: 'gtk3-settings.ini' enable: '[% c("var/linux") %]' ===================================== projects/firefox/config ===================================== @@ -21,7 +21,7 @@ var: browser_series: '15.0' browser_rebase: 1 browser_branch: '[% c("var/browser_series") %]-[% c("var/browser_rebase") %]' - browser_build: 1 + browser_build: 2 copyright_year: '[% exec("git show -s --format=%ci " _ c("git_hash") _ "^{commit}", { exec_noco => 1 }).remove("-.*") %]' nightly_updates_publish_dir: '[% c("var/nightly_updates_publish_dir_prefix") %]nightly-[% c("var/osname") %]' gitlab_project: https://gitlab.torproject.org/tpo/applications/tor-browser ===================================== projects/geckoview/config ===================================== @@ -23,7 +23,7 @@ var: browser_series: '15.0' browser_rebase: 1 browser_branch: '[% c("var/browser_series") %]-[% c("var/browser_rebase") %]' - browser_build: 1 + browser_build: 2 gitlab_project: https://gitlab.torproject.org/tpo/applications/tor-browser git_commit: '[% exec("git rev-parse " _ c("git_hash") _ "^{commit}", { exec_noco => 1 }) %]' deps: ===================================== projects/go/config ===================================== @@ -1,11 +1,11 @@ # vim: filetype=yaml sw=2 -version: '1.24.7' +version: '1.24.9' filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]' container: use_container: 1 var: - source_sha256: 2a8f50db0f88803607c50d7ea8834dcb7bd483c6b428a91e360fdf8624b46464 + source_sha256: c72f81ba54fe00efe7f3e7499d400979246881b13b775e9a9bb85541c11be695 no_crosscompile: 1 setup: | mkdir -p /var/tmp/dist ===================================== projects/manual/config ===================================== @@ -1,7 +1,7 @@ # vim: filetype=yaml sw=2 # To update, see doc/how-to-update-the-manual.txt # Remember to update also the package's hash, with the version! -version: 311002 +version: 321660 filename: 'manual-[% c("version") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]' container: use_container: 1 ===================================== projects/openssl/config ===================================== @@ -1,5 +1,5 @@ # vim: filetype=yaml sw=2 -version: 3.5.3 +version: 3.5.4 filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]' container: use_container: 1 @@ -33,5 +33,5 @@ input_files: - name: '[% c("var/compiler") %]' project: '[% c("var/compiler") %]' - URL: 'https://github.com/openssl/openssl/releases/download/openssl-[% c("version") %]/openssl-[% c("version") %].tar.gz' - sha256sum: c9489d2abcf943cdc8329a57092331c598a402938054dc3a22218aea8a8ec3bf + sha256sum: 967311f84955316969bdb1d8d4b983718ef42338639c621ec4c34fddef355e99 name: openssl ===================================== projects/translation/config ===================================== @@ -12,13 +12,13 @@ compress_tar: 'gz' steps: base-browser: base-browser: '[% INCLUDE build %]' - git_hash: 7a4673ab420d7c52cd3fa7366d0d9cdaa5a37937 + git_hash: dff70d135408cfc24931c170efa91fbaded19914 targets: nightly: git_hash: 'base-browser' tor-browser: tor-browser: '[% INCLUDE build %]' - git_hash: db5e90248e74338ce72ff118bdb749fea6ea2606 + git_hash: ca310e42296a7085ea59fc323592f3dc702123ac targets: nightly: git_hash: 'tor-browser' @@ -32,7 +32,7 @@ steps: fenix: '[% INCLUDE build %]' # We need to bump the commit before releasing but just pointing to a branch # might cause too much rebuidling of the Firefox part. - git_hash: 092a1e145fc7d558afbb0f6490f21aef4f6f5b2e + git_hash: 0efa38c746df88feeb4daa7150e394e87e6d4d18 compress_tar: 'zst' targets: nightly: ===================================== rbm.conf ===================================== @@ -74,11 +74,11 @@ buildconf: git_signtag_opt: '-s' var: - torbrowser_version: '15.0a3' + torbrowser_version: '15.0a4' 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: '2025/09/17 18:30:00' + browser_release_date: '2025/10/15 18:00:00' browser_release_date_timestamp: '[% USE date; date.format(c("var/browser_release_date"), "%s") %]' browser_default_channel: alpha browser_platforms: @@ -128,9 +128,9 @@ var: updater_enabled: 1 build_mar: 1 torbrowser_incremental_from: + - 15.0a3 - 15.0a2 - 15.0a1 - - 14.5a6 mar_channel_id: '[% c("var/projectname") %]-torproject-[% c("var/channel") %]' # torbrowser_legacy_version: 13.5.22 View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/3… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/3… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser-build][main] 2 commits: Bug 44131: Skip copying Bundle-Data when building for macos
by boklm (@boklm) 15 Oct '25

15 Oct '25
boklm pushed to branch main at The Tor Project / Applications / tor-browser-build Commits: b0e80195 by Beatriz Rizental at 2025-10-15T09:51:52+02:00 Bug 44131: Skip copying Bundle-Data when building for macos That folder doesn't exist anymore for macos. - - - - - a2e5d129 by Beatriz Rizental at 2025-10-15T09:53:45+02:00 Bug 44131: Escape backlashes when building tor-expert-bundle This is currently breaking the Windows builds. - - - - - 2 changed files: - projects/browser/build - projects/tor-expert-bundle/config Changes: ===================================== projects/browser/build ===================================== @@ -219,8 +219,10 @@ do cp -a Bundle-Data/Docs/* "$tbdir/$DOCSPATH" [% IF c("var/tor-browser") -%] cp -a Bundle-Data/Docs-TBB/* "$tbdir/$DOCSPATH" - tar -C Bundle-Data/[% bundledata_osname %] \ - -c . | tar -C "$tbdir[% IF ! c("var/macos") %]/[% c('var/ProjectName') %][% END %]" -x + [% IF ! c("var/macos") %] + tar -C Bundle-Data/[% bundledata_osname %] \ + -c . | tar -C "$tbdir/[% c('var/ProjectName') %]" -x + [% END %] [% ELSIF c("var/mullvad-browser") -%] cp -a Bundle-Data/Docs-MB/* "$tbdir/$DOCSPATH" [% END -%] ===================================== projects/tor-expert-bundle/config ===================================== @@ -11,7 +11,8 @@ targets: pt_path: PluggableTransports/ windows: var: - pt_path: TorBrowser\Tor\PluggableTransports\ + # Backlashes need to be escaped here. + pt_path: TorBrowser\\Tor\\PluggableTransports\\ pt_extension: .exe input_files: View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/compare/… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/compare/… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser] Pushed new tag mullvad-browser-140.4.0esr-15.0-1-build2
by Pier Angelo Vendrame (@pierov) 15 Oct '25

15 Oct '25
Pier Angelo Vendrame pushed new tag mullvad-browser-140.4.0esr-15.0-1-build2 at The Tor Project / Applications / Mullvad Browser -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/tree/mullv… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-140.4.0esr-15.0-1-build2
by Pier Angelo Vendrame (@pierov) 15 Oct '25

15 Oct '25
Pier Angelo Vendrame pushed new tag tor-browser-140.4.0esr-15.0-1-build2 at The Tor Project / Applications / Tor Browser -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.4.0esr-15.0-1] fixup! Firefox preference overrides.
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch mullvad-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: e64d0d0c by hackademix at 2025-10-15T18:07:33+00:00 fixup! Firefox preference overrides. BB 44262: Disable adding search engines from HTML forms - - - - - 1 changed file: - browser/app/profile/001-base-profile.js Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -216,6 +216,9 @@ pref("browser.safebrowsing.provider.mozilla.gethashURL", ""); pref("browser.urlbar.recentsearches.featureGate", false); pref("browser.urlbar.suggest.recentsearches", false); +// tor-browser#44262: Disable adding search engines from HTML forms +pref("browser.urlbar.update2.engineAliasRefresh", false); + // Disable the UITour API // See tor-browser#41457 and // https://bugzilla.mozilla.org/show_bug.cgi?id=1915280 View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/e64… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/e64… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-140.4.0esr-15.0-1] fixup! Firefox preference overrides.
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch base-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 85dc5eb3 by hackademix at 2025-10-15T18:07:11+00:00 fixup! Firefox preference overrides. BB 44262: Disable adding search engines from HTML forms - - - - - 1 changed file: - browser/app/profile/001-base-profile.js Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -216,6 +216,9 @@ pref("browser.safebrowsing.provider.mozilla.gethashURL", ""); pref("browser.urlbar.recentsearches.featureGate", false); pref("browser.urlbar.suggest.recentsearches", false); +// tor-browser#44262: Disable adding search engines from HTML forms +pref("browser.urlbar.update2.engineAliasRefresh", false); + // Disable the UITour API // See tor-browser#41457 and // https://bugzilla.mozilla.org/show_bug.cgi?id=1915280 View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/85dc5eb… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/85dc5eb… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] fixup! Firefox preference overrides.
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 2d9ad814 by hackademix at 2025-10-15T18:02:31+00:00 fixup! Firefox preference overrides. BB 44262: Disable adding search engines from HTML forms - - - - - 1 changed file: - browser/app/profile/001-base-profile.js Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -216,6 +216,9 @@ pref("browser.safebrowsing.provider.mozilla.gethashURL", ""); pref("browser.urlbar.recentsearches.featureGate", false); pref("browser.urlbar.suggest.recentsearches", false); +// tor-browser#44262: Disable adding search engines from HTML forms +pref("browser.urlbar.update2.engineAliasRefresh", false); + // Disable the UITour API // See tor-browser#41457 and // https://bugzilla.mozilla.org/show_bug.cgi?id=1915280 View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/2d9ad81… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/2d9ad81… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 23c8a66b by Henry Wilkes at 2025-10-15T17:43:21+00:00 fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser TB 44101: Show tor connection status in the nav bar when the tab bar is hidden. - - - - - 4 changed files: - browser/base/content/navigator-toolbox.inc.xhtml - toolkit/components/torconnect/content/torConnectTitlebarStatus.css - + toolkit/components/torconnect/content/torConnectTitlebarStatus.inc.xhtml - toolkit/components/torconnect/content/torConnectTitlebarStatus.js Changes: ===================================== browser/base/content/navigator-toolbox.inc.xhtml ===================================== @@ -100,12 +100,7 @@ #include private-browsing-indicator.inc.xhtml <toolbarbutton class="content-analysis-indicator toolbarbutton-1 content-analysis-indicator-icon"/> - <html:div id="tor-connect-titlebar-status" role="status"> - <html:img alt="" - src="chrome://global/content/torconnect/tor-not-connected-to-connected-animated.svg" /> - <html:span id="tor-connect-titlebar-status-label"></html:span> - </html:div> - +#include ../../../toolkit/components/torconnect/content/torConnectTitlebarStatus.inc.xhtml #include titlebar-items.inc.xhtml </toolbar> @@ -526,6 +521,7 @@ <hbox class="titlebar-spacer" type="post-tabs"/> #include private-browsing-indicator.inc.xhtml <toolbarbutton class="content-analysis-indicator toolbarbutton-1 content-analysis-indicator-icon"/> +#include ../../../toolkit/components/torconnect/content/torConnectTitlebarStatus.inc.xhtml #include titlebar-items.inc.xhtml </toolbar> ===================================== toolkit/components/torconnect/content/torConnectTitlebarStatus.css ===================================== @@ -1,16 +1,28 @@ -#tor-connect-titlebar-status:not([hidden]) { +.tor-connect-titlebar-status:not([hidden]) { display: flex; align-items: center; - /* Want same as #private-browsing-indicator-with-label */ + /* Want same as .private-browsing-indicator-with-label */ margin-inline: 7px; + + #navigator-toolbox[tabs-hidden] #TabsToolbar > & { + /* Hide in the tabs bar when the tabs bar is hidden. E.g. when using + * vertical tabs. Should be shown in the #nav-bar instead. + * See tor-browser#44101. */ + display: none; + } + + #navigator-toolbox:not([tabs-hidden]) #nav-bar > & { + /* Hide in the nav bar when the (horizontal) tabs bar is visible. */ + display: none; + } } -#tor-connect-titlebar-status-label { +.tor-connect-titlebar-status-label { margin-inline: 6px; white-space: nowrap; } -#tor-connect-titlebar-status img { +.tor-connect-titlebar-status img { -moz-context-properties: fill, stroke; fill: var(--icon-color); stroke: var(--icon-color); @@ -25,7 +37,7 @@ object-position: var(--tor-not-connected-offset); } -#tor-connect-titlebar-status.tor-connect-status-potentially-blocked img { +.tor-connect-titlebar-status.tor-connect-status-potentially-blocked img { /* NOTE: context-stroke is only used for the first "frame" for the slash. When * we assign the potentially-blocked class, we do *not* expect to be connected * at the same time, so we only expect this first frame to be visible in this @@ -33,17 +45,17 @@ stroke: var(--icon-color-critical); } -#tor-connect-titlebar-status.tor-connect-status-connected img { +.tor-connect-titlebar-status.tor-connect-status-connected img { object-position: var(--tor-connected-offset); } @media not ((prefers-contrast) or (forced-colors)) { /* Make the connected text and icon purple. */ - #tor-connect-titlebar-status.tor-connect-status-connected { + .tor-connect-titlebar-status.tor-connect-status-connected { color: var(--tor-text-color); } - #tor-connect-titlebar-status.tor-connect-status-connected img { + .tor-connect-titlebar-status.tor-connect-status-connected img { fill: var(--tor-text-color); stroke: var(--tor-text-color); } @@ -60,11 +72,11 @@ } @media (prefers-reduced-motion: no-preference) { - #tor-connect-titlebar-status.tor-connect-status-connected.tor-connect-status-animate-transition { + .tor-connect-titlebar-status.tor-connect-status-connected.tor-connect-status-animate-transition { transition: color 1000ms; } - #tor-connect-titlebar-status.tor-connect-status-connected.tor-connect-status-animate-transition img { + .tor-connect-titlebar-status.tor-connect-status-connected.tor-connect-status-animate-transition img { transition: fill 1000ms, stroke 1000ms; animation-name: onion-not-connected-to-connected; animation-delay: 200ms; ===================================== toolkit/components/torconnect/content/torConnectTitlebarStatus.inc.xhtml ===================================== @@ -0,0 +1,7 @@ +<html:div class="tor-connect-titlebar-status" role="status"> + <html:img + alt="" + src="chrome://global/content/torconnect/tor-not-connected-to-connected-animated.svg" + /> + <html:span class="tor-connect-titlebar-status-label"></html:span> +</html:div> ===================================== toolkit/components/torconnect/content/torConnectTitlebarStatus.js ===================================== @@ -3,21 +3,15 @@ */ var gTorConnectTitlebarStatus = { /** - * The status element in the title bar. + * The status elements and their labels. * - * @type {Element} + * @type {{status: Element, label: Element}[]} */ - node: null, - /** - * The status label. - * - * @type {Element} - */ - label: null, + _elements: [], /** * Whether we are connected, or null if the connection state is not yet known. * - * @type {bool?} + * @type {boolean?} */ connected: null, @@ -31,21 +25,21 @@ var gTorConnectTitlebarStatus = { this._strings = TorStrings.torConnect; - this.node = document.getElementById("tor-connect-titlebar-status"); - this.label = document.getElementById("tor-connect-titlebar-status-label"); + this._elements = Array.from( + document.querySelectorAll(".tor-connect-titlebar-status"), + element => { + return { + status: element, + label: element.querySelector(".tor-connect-titlebar-status-label"), + }; + } + ); // The title also acts as an accessible name for the role="status". - this.node.setAttribute("title", this._strings.titlebarStatusName); + for (const { status } of this._elements) { + status.setAttribute("title", this._strings.titlebarStatusName); + } - this._observeTopic = TorConnectTopics.StageChange; - this._stateListener = { - observe: (subject, topic) => { - if (topic !== this._observeTopic) { - return; - } - this._torConnectStateChanged(); - }, - }; - Services.obs.addObserver(this._stateListener, this._observeTopic); + Services.obs.addObserver(this, TorConnectTopics.StageChange); this._torConnectStateChanged(); }, @@ -54,7 +48,15 @@ var gTorConnectTitlebarStatus = { * De-initialize the component. */ uninit() { - Services.obs.removeObserver(this._stateListener, this._observeTopic); + Services.obs.removeObserver(this, TorConnectTopics.StageChange); + }, + + observe(subject, topic) { + switch (topic) { + case TorConnectTopics.StageChange: + this._torConnectStateChanged(); + break; + } }, /** @@ -67,7 +69,7 @@ var gTorConnectTitlebarStatus = { switch (TorConnect.stageName) { case TorConnectStage.Disabled: // Hide immediately. - this.node.hidden = true; + this._setHidden(true); return; case TorConnectStage.Bootstrapped: textId = "titlebarStatusConnected"; @@ -85,7 +87,9 @@ var gTorConnectTitlebarStatus = { } break; } - this.label.textContent = this._strings[textId]; + for (const { label } of this._elements) { + label.textContent = this._strings[textId]; + } if (this.connected !== connected) { // When we are transitioning from // this.connected = false @@ -104,11 +108,13 @@ var gTorConnectTitlebarStatus = { // // We only expect this latter case when opening a new window after // bootstrapping has already completed. See tor-browser#41850. - this.node.classList.toggle( - "tor-connect-status-animate-transition", - connected && this.connected !== null - ); - this.node.classList.toggle("tor-connect-status-connected", connected); + for (const { status } of this._elements) { + status.classList.toggle( + "tor-connect-status-animate-transition", + connected && this.connected !== null + ); + status.classList.toggle("tor-connect-status-connected", connected); + } this.connected = connected; if (connected) { this._startHiding(); @@ -119,10 +125,23 @@ var gTorConnectTitlebarStatus = { this._stopHiding(); } } - this.node.classList.toggle( - "tor-connect-status-potentially-blocked", - potentiallyBlocked - ); + for (const { status } of this._elements) { + status.classList.toggle( + "tor-connect-status-potentially-blocked", + potentiallyBlocked + ); + } + }, + + /** + * Hide or show the status. + * + * @param {boolean} hide - Whether to hide the status. + */ + _setHidden(hide) { + for (const { status } of this._elements) { + status.hidden = hide; + } }, /** @@ -134,7 +153,7 @@ var gTorConnectTitlebarStatus = { return; } this._hidingTimeout = setTimeout(() => { - this.node.hidden = true; + this._setHidden(true); }, 5000); }, @@ -146,6 +165,6 @@ var gTorConnectTitlebarStatus = { clearTimeout(this._hidingTimeout); this._hidingTimeout = 0; } - this.node.hidden = false; + this._setHidden(false); }, }; View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/23c8a66… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/23c8a66… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.4.0esr-15.0-1] Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch mullvad-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: 9795642d by Pier Angelo Vendrame at 2025-10-15T17:37:56+00:00 Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers Differential Revision: https://phabricator.services.mozilla.com/D268183 - - - - - 5 changed files: - browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs - toolkit/components/search/OpenSearchEngine.sys.mjs - toolkit/components/search/SearchEngine.sys.mjs - toolkit/components/search/SearchService.sys.mjs - toolkit/components/search/SearchUtils.sys.mjs Changes: ===================================== browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs ===================================== @@ -286,10 +286,22 @@ class ProviderContextualSearch extends ActionsProvider { let { type, engine } = this.#resultEngine; if (type == OPEN_SEARCH_ENGINE) { + let originAttributes; + try { + let currentURI = Services.io.newURI(queryContext.currentPage); + originAttributes = { + firstPartyDomain: Services.eTLD.getSchemelessSite(currentURI), + }; + } catch {} let openSearchEngineData = await lazy.loadAndParseOpenSearchEngine( - Services.io.newURI(engine.uri) + Services.io.newURI(engine.uri), + null, + originAttributes ); - engine = new lazy.OpenSearchEngine({ engineData: openSearchEngineData }); + engine = new lazy.OpenSearchEngine({ + engineData: openSearchEngineData, + originAttributes, + }); } this.#performSearch( ===================================== toolkit/components/search/OpenSearchEngine.sys.mjs ===================================== @@ -56,6 +56,8 @@ export class OpenSearchEngine extends SearchEngine { * @param {string} [options.faviconURL] * The website favicon, to be used if the engine data hasn't specified an * icon. + * @param {object} [options.originAttributes] + * The origin attributes to use to download additional resources. */ constructor(options = {}) { super({ @@ -68,7 +70,10 @@ export class OpenSearchEngine extends SearchEngine { }); if (options.faviconURL) { - this._setIcon(options.faviconURL, undefined, false).catch(e => + this._setIcon(options.faviconURL, { + override: false, + originAttributes: options.originAttributes, + }).catch(e => lazy.logConsole.error( `Error while setting icon for search engine ${options.engineData.name}:`, e.message @@ -77,7 +82,7 @@ export class OpenSearchEngine extends SearchEngine { } if (options.engineData) { - this.#setEngineData(options.engineData); + this.#setEngineData(options.engineData, options.originAttributes); // As this is a new engine, we must set the verification hash for the load // path set in the constructor. @@ -189,8 +194,10 @@ export class OpenSearchEngine extends SearchEngine { * * @param {OpenSearchProperties} data * The OpenSearch data. + * @param {object} originAttributes + * The origin attributes for any additional downloads */ - #setEngineData(data) { + #setEngineData(data, originAttributes) { let name = data.name.trim(); if (Services.search.getEngineByName(name)) { throw Components.Exception( @@ -258,11 +265,12 @@ export class OpenSearchEngine extends SearchEngine { } for (let image of data.images) { - this._setIcon(image.url, image.size).catch(e => - lazy.logConsole.error( - `Error while setting icon for search engine ${data.name}:`, - e.message - ) + this._setIcon(image.url, { size: image.size, originAttributes }).catch( + e => + lazy.logConsole.error( + `Error while setting icon for search engine ${data.name}:`, + e.message + ) ); } } ===================================== toolkit/components/search/SearchEngine.sys.mjs ===================================== @@ -585,15 +585,19 @@ export class SearchEngine { * @param {string} iconURL * A URI string pointing to the engine's icon. * Must have http[s], data, or moz-extension protocol. - * @param {number} [size] + * @param {object} options + * The options object + * @param {number} [options.size] * Width and height of the icon (determined automatically if not provided). - * @param {boolean} [override] + * @param {boolean} [options.override] * Whether the new URI should override an existing one. + * @param {object} [options.originAttributes] + * The origin attributes to use to load the icon. * @returns {Promise<void>} * Resolves when the icon was set. * Rejects with an Error if there was an error. */ - async _setIcon(iconURL, size, override = true) { + async _setIcon(iconURL, options = { override: true }) { lazy.logConsole.debug( "_setIcon: Setting icon url for", this.name, @@ -601,8 +605,12 @@ export class SearchEngine { limitURILength(iconURL) ); - [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, size); - this._addIconToMap(iconURL, size, override); + let size; + [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, { + size: options.size, + originAttributes: options.originAttributes, + }); + this._addIconToMap(iconURL, size, options.override); if (this._engineAddedToStore) { lazy.SearchUtils.notifyAction( @@ -620,18 +628,24 @@ export class SearchEngine { * @param {string} iconURL * A URI string pointing to the engine's icon. * Must have http[s], data, or moz-extension protocol. - * @param {number} [size] + * @param {object} options + * The options object + * @param {number} [options.size] * Width and height of the icon (determined automatically if not provided). + * @param {object} [options.originAttributes] + * The origin attributes to use to load the icon. * @returns {Promise<[string, number]>} * Resolves to [dataURL, size] if successful and rejects if there was an error. */ - async _downloadAndRescaleIcon(iconURL, size) { + async _downloadAndRescaleIcon(iconURL, options = {}) { let uri = lazy.SearchUtils.makeURI(iconURL); if (!uri) { throw new Error(`Invalid URI`); } + let size = options.size; + switch (uri.scheme) { case "moz-extension": { if (!size) { @@ -644,7 +658,10 @@ export class SearchEngine { case "data": case "http": case "https": { - let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon(uri); + let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon( + uri, + options.originAttributes + ); if (byteArray.length > lazy.SearchUtils.MAX_ICON_SIZE) { lazy.logConsole.debug( `Rescaling icon for search engine ${this.name}.` ===================================== toolkit/components/search/SearchService.sys.mjs ===================================== @@ -772,7 +772,11 @@ export class SearchService { null, originAttributes ); - engine = new lazy.OpenSearchEngine({ engineData, faviconURL: iconURL }); + engine = new lazy.OpenSearchEngine({ + engineData, + faviconURL: iconURL, + originAttributes, + }); } catch (ex) { throw Components.Exception( "addEngine: Error adding engine:\n" + ex, ===================================== toolkit/components/search/SearchUtils.sys.mjs ===================================== @@ -511,13 +511,19 @@ export var SearchUtils = { * * @param {string|nsIURI} uri * The URI to the icon. + * @param {object} [originAttributes] + * The origin attributes to download the icon. * @returns {Promise<[Uint8Array, string]>} * Resolves to an array containing the data and the mime type. * Rejects if the icon cannot be fetched. */ - async fetchIcon(uri) { + async fetchIcon(uri, originAttributes = null) { return new Promise((resolve, reject) => { - let chan = SearchUtils.makeChannel(uri, Ci.nsIContentPolicy.TYPE_IMAGE); + let chan = SearchUtils.makeChannel( + uri, + Ci.nsIContentPolicy.TYPE_IMAGE, + originAttributes + ); let listener = new SearchUtils.LoadListener( chan, /^image\//, View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/979… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/979… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-140.4.0esr-15.0-1] Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch base-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 803d875f by Pier Angelo Vendrame at 2025-10-15T17:37:02+00:00 Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers Differential Revision: https://phabricator.services.mozilla.com/D268183 - - - - - 5 changed files: - browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs - toolkit/components/search/OpenSearchEngine.sys.mjs - toolkit/components/search/SearchEngine.sys.mjs - toolkit/components/search/SearchService.sys.mjs - toolkit/components/search/SearchUtils.sys.mjs Changes: ===================================== browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs ===================================== @@ -286,10 +286,22 @@ class ProviderContextualSearch extends ActionsProvider { let { type, engine } = this.#resultEngine; if (type == OPEN_SEARCH_ENGINE) { + let originAttributes; + try { + let currentURI = Services.io.newURI(queryContext.currentPage); + originAttributes = { + firstPartyDomain: Services.eTLD.getSchemelessSite(currentURI), + }; + } catch {} let openSearchEngineData = await lazy.loadAndParseOpenSearchEngine( - Services.io.newURI(engine.uri) + Services.io.newURI(engine.uri), + null, + originAttributes ); - engine = new lazy.OpenSearchEngine({ engineData: openSearchEngineData }); + engine = new lazy.OpenSearchEngine({ + engineData: openSearchEngineData, + originAttributes, + }); } this.#performSearch( ===================================== toolkit/components/search/OpenSearchEngine.sys.mjs ===================================== @@ -56,6 +56,8 @@ export class OpenSearchEngine extends SearchEngine { * @param {string} [options.faviconURL] * The website favicon, to be used if the engine data hasn't specified an * icon. + * @param {object} [options.originAttributes] + * The origin attributes to use to download additional resources. */ constructor(options = {}) { super({ @@ -68,7 +70,10 @@ export class OpenSearchEngine extends SearchEngine { }); if (options.faviconURL) { - this._setIcon(options.faviconURL, undefined, false).catch(e => + this._setIcon(options.faviconURL, { + override: false, + originAttributes: options.originAttributes, + }).catch(e => lazy.logConsole.error( `Error while setting icon for search engine ${options.engineData.name}:`, e.message @@ -77,7 +82,7 @@ export class OpenSearchEngine extends SearchEngine { } if (options.engineData) { - this.#setEngineData(options.engineData); + this.#setEngineData(options.engineData, options.originAttributes); // As this is a new engine, we must set the verification hash for the load // path set in the constructor. @@ -189,8 +194,10 @@ export class OpenSearchEngine extends SearchEngine { * * @param {OpenSearchProperties} data * The OpenSearch data. + * @param {object} originAttributes + * The origin attributes for any additional downloads */ - #setEngineData(data) { + #setEngineData(data, originAttributes) { let name = data.name.trim(); if (Services.search.getEngineByName(name)) { throw Components.Exception( @@ -258,11 +265,12 @@ export class OpenSearchEngine extends SearchEngine { } for (let image of data.images) { - this._setIcon(image.url, image.size).catch(e => - lazy.logConsole.error( - `Error while setting icon for search engine ${data.name}:`, - e.message - ) + this._setIcon(image.url, { size: image.size, originAttributes }).catch( + e => + lazy.logConsole.error( + `Error while setting icon for search engine ${data.name}:`, + e.message + ) ); } } ===================================== toolkit/components/search/SearchEngine.sys.mjs ===================================== @@ -585,15 +585,19 @@ export class SearchEngine { * @param {string} iconURL * A URI string pointing to the engine's icon. * Must have http[s], data, or moz-extension protocol. - * @param {number} [size] + * @param {object} options + * The options object + * @param {number} [options.size] * Width and height of the icon (determined automatically if not provided). - * @param {boolean} [override] + * @param {boolean} [options.override] * Whether the new URI should override an existing one. + * @param {object} [options.originAttributes] + * The origin attributes to use to load the icon. * @returns {Promise<void>} * Resolves when the icon was set. * Rejects with an Error if there was an error. */ - async _setIcon(iconURL, size, override = true) { + async _setIcon(iconURL, options = { override: true }) { lazy.logConsole.debug( "_setIcon: Setting icon url for", this.name, @@ -601,8 +605,12 @@ export class SearchEngine { limitURILength(iconURL) ); - [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, size); - this._addIconToMap(iconURL, size, override); + let size; + [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, { + size: options.size, + originAttributes: options.originAttributes, + }); + this._addIconToMap(iconURL, size, options.override); if (this._engineAddedToStore) { lazy.SearchUtils.notifyAction( @@ -620,18 +628,24 @@ export class SearchEngine { * @param {string} iconURL * A URI string pointing to the engine's icon. * Must have http[s], data, or moz-extension protocol. - * @param {number} [size] + * @param {object} options + * The options object + * @param {number} [options.size] * Width and height of the icon (determined automatically if not provided). + * @param {object} [options.originAttributes] + * The origin attributes to use to load the icon. * @returns {Promise<[string, number]>} * Resolves to [dataURL, size] if successful and rejects if there was an error. */ - async _downloadAndRescaleIcon(iconURL, size) { + async _downloadAndRescaleIcon(iconURL, options = {}) { let uri = lazy.SearchUtils.makeURI(iconURL); if (!uri) { throw new Error(`Invalid URI`); } + let size = options.size; + switch (uri.scheme) { case "moz-extension": { if (!size) { @@ -644,7 +658,10 @@ export class SearchEngine { case "data": case "http": case "https": { - let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon(uri); + let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon( + uri, + options.originAttributes + ); if (byteArray.length > lazy.SearchUtils.MAX_ICON_SIZE) { lazy.logConsole.debug( `Rescaling icon for search engine ${this.name}.` ===================================== toolkit/components/search/SearchService.sys.mjs ===================================== @@ -772,7 +772,11 @@ export class SearchService { null, originAttributes ); - engine = new lazy.OpenSearchEngine({ engineData, faviconURL: iconURL }); + engine = new lazy.OpenSearchEngine({ + engineData, + faviconURL: iconURL, + originAttributes, + }); } catch (ex) { throw Components.Exception( "addEngine: Error adding engine:\n" + ex, ===================================== toolkit/components/search/SearchUtils.sys.mjs ===================================== @@ -511,13 +511,19 @@ export var SearchUtils = { * * @param {string|nsIURI} uri * The URI to the icon. + * @param {object} [originAttributes] + * The origin attributes to download the icon. * @returns {Promise<[Uint8Array, string]>} * Resolves to an array containing the data and the mime type. * Rejects if the icon cannot be fetched. */ - async fetchIcon(uri) { + async fetchIcon(uri, originAttributes = null) { return new Promise((resolve, reject) => { - let chan = SearchUtils.makeChannel(uri, Ci.nsIContentPolicy.TYPE_IMAGE); + let chan = SearchUtils.makeChannel( + uri, + Ci.nsIContentPolicy.TYPE_IMAGE, + originAttributes + ); let listener = new SearchUtils.LoadListener( chan, /^image\//, View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/803d875… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/803d875… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 39767109 by Pier Angelo Vendrame at 2025-10-15T17:28:31+00:00 Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers Differential Revision: https://phabricator.services.mozilla.com/D268183 - - - - - 5 changed files: - browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs - toolkit/components/search/OpenSearchEngine.sys.mjs - toolkit/components/search/SearchEngine.sys.mjs - toolkit/components/search/SearchService.sys.mjs - toolkit/components/search/SearchUtils.sys.mjs Changes: ===================================== browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs ===================================== @@ -286,10 +286,22 @@ class ProviderContextualSearch extends ActionsProvider { let { type, engine } = this.#resultEngine; if (type == OPEN_SEARCH_ENGINE) { + let originAttributes; + try { + let currentURI = Services.io.newURI(queryContext.currentPage); + originAttributes = { + firstPartyDomain: Services.eTLD.getSchemelessSite(currentURI), + }; + } catch {} let openSearchEngineData = await lazy.loadAndParseOpenSearchEngine( - Services.io.newURI(engine.uri) + Services.io.newURI(engine.uri), + null, + originAttributes ); - engine = new lazy.OpenSearchEngine({ engineData: openSearchEngineData }); + engine = new lazy.OpenSearchEngine({ + engineData: openSearchEngineData, + originAttributes, + }); } this.#performSearch( ===================================== toolkit/components/search/OpenSearchEngine.sys.mjs ===================================== @@ -56,6 +56,8 @@ export class OpenSearchEngine extends SearchEngine { * @param {string} [options.faviconURL] * The website favicon, to be used if the engine data hasn't specified an * icon. + * @param {object} [options.originAttributes] + * The origin attributes to use to download additional resources. */ constructor(options = {}) { super({ @@ -68,7 +70,10 @@ export class OpenSearchEngine extends SearchEngine { }); if (options.faviconURL) { - this._setIcon(options.faviconURL, undefined, false).catch(e => + this._setIcon(options.faviconURL, { + override: false, + originAttributes: options.originAttributes, + }).catch(e => lazy.logConsole.error( `Error while setting icon for search engine ${options.engineData.name}:`, e.message @@ -77,7 +82,7 @@ export class OpenSearchEngine extends SearchEngine { } if (options.engineData) { - this.#setEngineData(options.engineData); + this.#setEngineData(options.engineData, options.originAttributes); // As this is a new engine, we must set the verification hash for the load // path set in the constructor. @@ -189,8 +194,10 @@ export class OpenSearchEngine extends SearchEngine { * * @param {OpenSearchProperties} data * The OpenSearch data. + * @param {object} originAttributes + * The origin attributes for any additional downloads */ - #setEngineData(data) { + #setEngineData(data, originAttributes) { let name = data.name.trim(); if (Services.search.getEngineByName(name)) { throw Components.Exception( @@ -258,11 +265,12 @@ export class OpenSearchEngine extends SearchEngine { } for (let image of data.images) { - this._setIcon(image.url, image.size).catch(e => - lazy.logConsole.error( - `Error while setting icon for search engine ${data.name}:`, - e.message - ) + this._setIcon(image.url, { size: image.size, originAttributes }).catch( + e => + lazy.logConsole.error( + `Error while setting icon for search engine ${data.name}:`, + e.message + ) ); } } ===================================== toolkit/components/search/SearchEngine.sys.mjs ===================================== @@ -585,15 +585,19 @@ export class SearchEngine { * @param {string} iconURL * A URI string pointing to the engine's icon. * Must have http[s], data, or moz-extension protocol. - * @param {number} [size] + * @param {object} options + * The options object + * @param {number} [options.size] * Width and height of the icon (determined automatically if not provided). - * @param {boolean} [override] + * @param {boolean} [options.override] * Whether the new URI should override an existing one. + * @param {object} [options.originAttributes] + * The origin attributes to use to load the icon. * @returns {Promise<void>} * Resolves when the icon was set. * Rejects with an Error if there was an error. */ - async _setIcon(iconURL, size, override = true) { + async _setIcon(iconURL, options = { override: true }) { lazy.logConsole.debug( "_setIcon: Setting icon url for", this.name, @@ -601,8 +605,12 @@ export class SearchEngine { limitURILength(iconURL) ); - [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, size); - this._addIconToMap(iconURL, size, override); + let size; + [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, { + size: options.size, + originAttributes: options.originAttributes, + }); + this._addIconToMap(iconURL, size, options.override); if (this._engineAddedToStore) { lazy.SearchUtils.notifyAction( @@ -620,18 +628,24 @@ export class SearchEngine { * @param {string} iconURL * A URI string pointing to the engine's icon. * Must have http[s], data, or moz-extension protocol. - * @param {number} [size] + * @param {object} options + * The options object + * @param {number} [options.size] * Width and height of the icon (determined automatically if not provided). + * @param {object} [options.originAttributes] + * The origin attributes to use to load the icon. * @returns {Promise<[string, number]>} * Resolves to [dataURL, size] if successful and rejects if there was an error. */ - async _downloadAndRescaleIcon(iconURL, size) { + async _downloadAndRescaleIcon(iconURL, options = {}) { let uri = lazy.SearchUtils.makeURI(iconURL); if (!uri) { throw new Error(`Invalid URI`); } + let size = options.size; + switch (uri.scheme) { case "moz-extension": { if (!size) { @@ -644,7 +658,10 @@ export class SearchEngine { case "data": case "http": case "https": { - let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon(uri); + let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon( + uri, + options.originAttributes + ); if (byteArray.length > lazy.SearchUtils.MAX_ICON_SIZE) { lazy.logConsole.debug( `Rescaling icon for search engine ${this.name}.` ===================================== toolkit/components/search/SearchService.sys.mjs ===================================== @@ -772,7 +772,11 @@ export class SearchService { null, originAttributes ); - engine = new lazy.OpenSearchEngine({ engineData, faviconURL: iconURL }); + engine = new lazy.OpenSearchEngine({ + engineData, + faviconURL: iconURL, + originAttributes, + }); } catch (ex) { throw Components.Exception( "addEngine: Error adding engine:\n" + ex, ===================================== toolkit/components/search/SearchUtils.sys.mjs ===================================== @@ -511,13 +511,19 @@ export var SearchUtils = { * * @param {string|nsIURI} uri * The URI to the icon. + * @param {object} [originAttributes] + * The origin attributes to download the icon. * @returns {Promise<[Uint8Array, string]>} * Resolves to an array containing the data and the mime type. * Rejects if the icon cannot be fetched. */ - async fetchIcon(uri) { + async fetchIcon(uri, originAttributes = null) { return new Promise((resolve, reject) => { - let chan = SearchUtils.makeChannel(uri, Ci.nsIContentPolicy.TYPE_IMAGE); + let chan = SearchUtils.makeChannel( + uri, + Ci.nsIContentPolicy.TYPE_IMAGE, + originAttributes + ); let listener = new SearchUtils.LoadListener( chan, /^image\//, View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/3976710… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/3976710… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.4.0esr-15.0-1] 12 commits: fixup! BB 41919: Letterboxing, add temporarily visible web content-size...
by henry (@henry) 15 Oct '25

15 Oct '25
henry pushed to branch mullvad-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: 81be023b by Henry Wilkes at 2025-10-15T18:07:04+01:00 fixup! BB 41919: Letterboxing, add temporarily visible web content-size indicator on window resizing. TB 44214: Fix letterboxing status indicator for RTL. - - - - - deed64ab by Henry Wilkes at 2025-10-15T18:07:05+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop unnecessary CSS rules. - - - - - f18fc8f5 by Henry Wilkes at 2025-10-15T18:07:06+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS logical positions for the status panel, rather than "left" and "right". - - - - - 77ea2cff by Henry Wilkes at 2025-10-15T18:07:06+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop upstream's rules for placing content. - - - - - 725f9b44 by Henry Wilkes at 2025-10-15T18:07:07+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop letterboxing gradient. - - - - - c5acc4f7 by Henry Wilkes at 2025-10-15T18:07:08+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Separate out the essential --letterboxing-width and --letterboxing-height rules into their own .letterboxing block, to have the property values set on. - - - - - d3f14e86 by Henry Wilkes at 2025-10-15T18:07:09+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 1. - - - - - f622ced5 by Henry Wilkes at 2025-10-15T18:07:09+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 2. - - - - - 9bd8b0b8 by Henry Wilkes at 2025-10-15T18:07:10+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Move the letterboxing classes one element up from tabpanels to tabbox. This is because we need to restyle the tabbox. - - - - - f6b94a03 by Henry Wilkes at 2025-10-15T18:07:11+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use a CSS class to show/hide the letterboxing border, rather than setting the border-radius in javascript. - - - - - 9f0d03ed by Henry Wilkes at 2025-10-15T18:07:12+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Rename CSS variable from top-radius to radius-top. This is closer to what upstream has done recently for tokens, where higher specificity is appended. - - - - - a11336fe by Henry Wilkes at 2025-10-15T18:07:12+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Update letterboxing styling for ESR 140. - - - - - 4 changed files: - browser/base/content/browser-fullScreenAndPointerLock.js - browser/themes/shared/tabbrowser/content-area.css - toolkit/components/resistfingerprinting/RFPHelper.sys.mjs - toolkit/components/resistfingerprinting/content/letterboxing.css Changes: ===================================== browser/base/content/browser-fullScreenAndPointerLock.js ===================================== @@ -879,7 +879,13 @@ var FullScreen = { } this._isChromeCollapsed = false; - Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "shown"); + // Need a subject to know which window this applies to. + // Base browser patch can be dropped after bugzilla bug 1992036. + Services.obs.notifyObservers( + gNavToolbox, + "fullscreen-nav-toolbox", + "shown" + ); }, hideNavToolbox(aAnimate = false) { @@ -943,7 +949,13 @@ var FullScreen = { gNavToolbox.style.marginTop = -gNavToolbox.getBoundingClientRect().height + "px"; this._isChromeCollapsed = true; - Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "hidden"); + // Need a subject to know which window this applies to. + // Base browser patch can be dropped after bugzilla bug 1880918. + Services.obs.notifyObservers( + gNavToolbox, + "fullscreen-nav-toolbox", + "hidden" + ); MousePosTracker.removeListener(this); }, ===================================== browser/themes/shared/tabbrowser/content-area.css ===================================== @@ -242,13 +242,17 @@ } } -#statuspanel[type=letterboxingStatus] > #statuspanel-label, -#statuspanel[previoustype=letterboxingStatus][inactive] > #statuspanel-label { +#statuspanel:is([type=letterboxingStatus], [previoustype=letterboxingStatus][inactive]) > #statuspanel-label { background-image: url("chrome://browser/skin/window.svg"); background-size: 1em; background-repeat: no-repeat; - background-position-x: .5em; background-position-y: center; + background-position-x: left .5em; + + &:-moz-locale-dir(rtl) { + background-position-x: right .5em; + } + padding-inline-start: 2em; -moz-context-properties: fill; fill: var(--color-accent-primary); ===================================== toolkit/components/resistfingerprinting/RFPHelper.sys.mjs ===================================== @@ -18,8 +18,6 @@ const kPrefLetterboxingTesting = "privacy.resistFingerprinting.letterboxing.testing"; const kPrefLetterboxingVcenter = "privacy.resistFingerprinting.letterboxing.vcenter"; -const kPrefLetterboxingGradient = - "privacy.resistFingerprinting.letterboxing.gradient"; const kPrefLetterboxingDidForceSize = "privacy.resistFingerprinting.letterboxing.didForceSize"; const kPrefLetterboxingRememberSize = @@ -28,10 +26,17 @@ const kPrefLetterboxingRememberSize = const kTopicDOMWindowOpened = "domwindowopened"; const kTopicDOMWindowClosed = "domwindowclosed"; +const kTopicFullscreenNavToolbox = "fullscreen-nav-toolbox"; + const kPrefResizeWarnings = "privacy.resistFingerprinting.resizeWarnings"; +const kPrefVerticalTabs = "sidebar.verticalTabs"; + const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + Color: "resource://gre/modules/Color.sys.mjs", +}); ChromeUtils.defineLazyGetter(lazy, "logConsole", () => console.createInstance({ prefix: "RFPHelper", @@ -155,7 +160,8 @@ class _RFPHelper { Services.prefs.addObserver(kPrefResistFingerprinting, this); Services.prefs.addObserver(kPrefLetterboxing, this); Services.prefs.addObserver(kPrefLetterboxingVcenter, this); - Services.prefs.addObserver(kPrefLetterboxingGradient, this); + Services.prefs.addObserver(kPrefVerticalTabs, this); + Services.obs.addObserver(this, kTopicFullscreenNavToolbox); XPCOMUtils.defineLazyPreferenceGetter( this, @@ -188,9 +194,10 @@ class _RFPHelper { // Remove unconditional observers Services.prefs.removeObserver(kPrefResistFingerprinting, this); - Services.prefs.removeObserver(kPrefLetterboxingGradient, this); Services.prefs.removeObserver(kPrefLetterboxingVcenter, this); Services.prefs.removeObserver(kPrefLetterboxing, this); + Services.prefs.removeObserver(kPrefVerticalTabs, this); + Services.obs.removeObserver(this, kTopicFullscreenNavToolbox); // Remove the RFP observers, swallowing exceptions if they weren't present this._removeLanguagePrefObservers(); } @@ -212,6 +219,15 @@ class _RFPHelper { case kTopicDOMWindowClosed: this._handleDOMWindowClosed(subject); break; + case kTopicFullscreenNavToolbox: + // The `subject` is the gNavToolbox. + // Record whether the toobox has been hidden when the browser (not + // content) is in fullscreen. + subject.ownerGlobal.gBrowser.tabbox.classList.toggle( + "letterboxing-nav-toolbox-hidden", + data === "hidden" + ); + break; default: break; } @@ -226,6 +242,13 @@ class _RFPHelper { resizeObserver.observe(browser.parentElement); break; } + case "nativethemechange": + // NOTE: "nativethemechange" seems to always be sent after + // "windowlwthemeupdate". So all the lwtheme CSS properties should be + // set to the new theme's values already, so we don't need to wait for + // windowlwthemeupdate. + this._updateLetterboxingColors(aMessage.currentTarget, true); + break; default: break; } @@ -245,9 +268,13 @@ class _RFPHelper { Services.prefs.clearUserPref(kPrefLetterboxingDidForceSize); // fall-through case kPrefLetterboxingVcenter: - case kPrefLetterboxingGradient: this._handleLetterboxingPrefChanged(); break; + case kPrefVerticalTabs: + if (this.letterboxingEnabled) { + forEachWindow(win => this._updateLetterboxingColors(win)); + } + break; default: break; } @@ -452,7 +479,7 @@ class _RFPHelper { // If not already cached on the document object, traverse the CSSOM and // find the rule applying the default letterboxing styles to browsers // preemptively in order to beat race conditions on tab/window creation - return (document._letterboxingMarginsRule ||= (() => { + return (document._letterboxingDefaultRule ||= (() => { const LETTERBOX_CSS_SELECTOR = ".letterboxing"; const LETTERBOX_CSS_URL = "chrome://global/content/resistfingerprinting/letterboxing.css"; @@ -688,26 +715,22 @@ class _RFPHelper { if (lastRoundedSize) { // Check whether the letterboxing margin is less than the border radius, - // and if so flatten the borders. - let borderRadius = parseInt( - win - .getComputedStyle(browserContainer) - .getPropertyValue("--letterboxing-border-radius") + // and if so do not show an outline. + const gapVertical = parentHeight - lastRoundedSize.height; + const gapHorizontal = parentWidth - lastRoundedSize.width; + browserParent.classList.toggle( + "letterboxing-show-outline", + gapVertical >= this._letterboxingBorderRadius || + gapHorizontal >= this._letterboxingBorderRadius + ); + // When the Letterboxing area is top-aligned, only show the sidebar corner + // if there is enough horizontal space. + // The factor of 4 is from the horizontal centre-alignment and wanting + // enough space for twice the corner radius. + browserParent.classList.toggle( + "letterboxing-show-sidebar-corner", + gapHorizontal >= 4 * this._letterboxingBorderRadius ); - if ( - borderRadius && - parentWidth - lastRoundedSize.width < borderRadius && - parentHeight - lastRoundedSize.height < borderRadius - ) { - borderRadius = 0; - } else { - borderRadius = ""; - } - styleChanges.queueIfNeeded(browserParent, { - "--letterboxing-decorator-visibility": - borderRadius === 0 ? "hidden" : "", - "--letterboxing-border-radius": borderRadius, - }); if (win.gBrowser.selectedBrowser == aBrowser) { const updateStatus = async args => { win.XULBrowserWindow.letterboxingStatus = args @@ -769,20 +792,31 @@ class _RFPHelper { _resetContentSize(aBrowser) { aBrowser.parentElement.classList.add("exclude-letterboxing"); + aBrowser.parentElement.classList.remove( + "letterboxing-show-outline", + "letterboxing-show-sidebar-corner" + ); } _updateSizeForTabsInWindow(aWindow) { let tabBrowser = aWindow.gBrowser; - tabBrowser.tabpanels?.classList.add("letterboxing"); - tabBrowser.tabpanels?.classList.toggle( + tabBrowser.tabbox.classList.add("letterboxing"); + tabBrowser.tabbox.classList.toggle( "letterboxing-vcenter", Services.prefs.getBoolPref(kPrefLetterboxingVcenter, false) ); - tabBrowser.tabpanels?.classList.toggle( - "letterboxing-gradient", - Services.prefs.getBoolPref(kPrefLetterboxingGradient, false) - ); + if (this._letterboxingBorderRadius === undefined && tabBrowser.tabbox) { + // Cache the value since it is not expected to change in a session for any + // window. + this._letterboxingBorderRadius = Math.ceil( + parseFloat( + aWindow + .getComputedStyle(tabBrowser.tabbox) + .getPropertyValue("--letterboxing-border-radius") + ) + ); + } for (let tab of tabBrowser.tabs) { let browser = tab.linkedBrowser; @@ -791,7 +825,7 @@ class _RFPHelper { // We need to add this class late because otherwise new windows get // maximized. aWindow.setTimeout(() => { - tabBrowser.tabpanels?.classList.add("letterboxing-ready"); + tabBrowser.tabbox.classList.add("letterboxing-ready"); if (!aWindow._rfpOriginalSize) { this._recordWindowSize(aWindow); } @@ -869,6 +903,247 @@ class _RFPHelper { this._resizeObservers.set(aWindow, resizeObserver); // Rounding the content viewport. this._updateSizeForTabsInWindow(aWindow); + + this._updateLetterboxingColors(aWindow, true); + aWindow.addEventListener("nativethemechange", this); + } + + /** + * Convert a CSS property to its RGBA value. + * + * @param {Window} win - The window for the element. + * @param {CSSStyleDeclaration} style - The computed style for the element we + * want to grab the color from. + * @param {string} property - The name of the property we want. + * + * @returns {InspectorRGBATuple} - The RGBA color. The "r", "g", "b" fields + * are relative to the 0-255 color range. The "a" field is in the 0-1 range. + */ + _convertToRGBA(win, style, property) { + let cssColor = style.getPropertyValue(property); + if (!cssColor) { + lazy.logConsole.error(`Missing color "${property}"`); + return { r: 0, g: 0, b: 0, a: 0 }; + } + const currentColorRegex = + /(^|[^a-zA-Z0-9_-])currentColor($|[^a-zA-Z0-9_-])/g; + if (currentColorRegex.test(cssColor)) { + const currentColor = style.color; + cssColor = cssColor.replace(currentColorRegex, (_, pre, post) => { + return pre + currentColor + post; + }); + lazy.logConsole.debug( + "Replaced currentColor.", + property, + currentColor, + cssColor + ); + } + /* Can drop the document argument after bugzilla bug 1973684 (142). */ + const colorRGBA = win.InspectorUtils.colorToRGBA(cssColor, win.document); + if (!colorRGBA) { + lazy.logConsole.error( + `Failed to convert "${property}" color (${cssColor}) to RGBA` + ); + return { r: 0, g: 0, b: 0, a: 0 }; + } + return colorRGBA; + } + + /** + * Compose two colors with alpha values on top of each other. + * + * @param {InspectorRGBATuple} topRGBA - The color to place on the top. + * @param {InspectorRGBATuple} bottomRGBA - The color to place on the bottom. + * + * @returns {InspectorRGBATuple} - The composed color. + */ + _composeRGBA(topRGBA, bottomRGBA) { + const topA = Math.max(0, Math.min(1, topRGBA.a)); + const bottomA = Math.max(0, Math.min(1, bottomRGBA.a)); + const a = topA + bottomA - topA * bottomA; // Should be 1 if either is 1. + if (a === 0) { + return { r: 0, g: 0, b: 0, a }; + } + const ret = { a }; + for (const field of ["r", "g", "b"]) { + ret[field] = + (topRGBA[field] * topA + bottomRGBA[field] * bottomA * (1 - topA)) / a; + } + return ret; + } + + /** + * Calculate the urlbar's container opaque background color, removing any + * transparency. + * + * @param {Window} win - The window to calculate the color for. + * @param {CSSStyleDeclaration} style - The computed style for the #nav-bar + * element. + * + * @returns {InspectorRGBATuple} - The calculated color, which will be opaque. + */ + _calculateUrlbarContainerColor(win, style) { + let colorRGBA; + if (!Services.prefs.getBoolPref(kPrefVerticalTabs)) { + lazy.logConsole.debug("Toolbar background used."); + colorRGBA = this._convertToRGBA(win, style, "--toolbar-bgcolor"); + if (colorRGBA.a === 1) { + return colorRGBA; + } + } else { + // The urlbar only has the toolbox colour. + colorRGBA = { r: 0, g: 0, b: 0, a: 0 }; + } + let toolboxHasBackgroundImage = false; + const isLwTheme = win.document.documentElement.hasAttribute("lwtheme"); + if (isLwTheme) { + for (const prop of ["--lwt-header-image", "--lwt-additional-images"]) { + const headerImage = style.getPropertyValue(prop); + if (headerImage && headerImage !== "none") { + // The theme sets a background image behind the urlbar. No easy way to + // derive a single colour from this. + toolboxHasBackgroundImage = true; + lazy.logConsole.debug( + "Toolbox has background image.", + prop, + headerImage + ); + break; + } + } + } + if (!toolboxHasBackgroundImage) { + lazy.logConsole.debug("Toolbox background used."); + colorRGBA = this._composeRGBA( + colorRGBA, + this._convertToRGBA(win, style, "--toolbox-bgcolor") + ); + if (colorRGBA.a === 1) { + return colorRGBA; + } + } + + // Determine whether the urlbar is dark. + // At this point, the urlbar background has some transparency, likely on top + // of an image. + // We use the theme's text colour to figure out whether the urlbar + // background is overall meant to be light or dark. Unlike the urlbar, we + // expect this colour to be (almost) opaque. + const textRGBA = this._convertToRGBA(win, style, "--toolbar-field-color"); + const textColor = new lazy.Color(textRGBA.r, textRGBA.g, textRGBA.b); + if (textColor.relativeLuminance >= 0.5) { + // Light text, so assume it has a dark background. + // Combine with a generic opaque dark colour. Copied from "frame" for the + // built-in dark theme. + lazy.logConsole.debug("Generic dark background used."); + const darkFrameRGBA = { r: 28, g: 27, b: 34, a: 1 }; + return this._composeRGBA(colorRGBA, darkFrameRGBA); + } + // Combine with an opaque light colour. Copied from "frame" for the built-in + // light theme. + lazy.logConsole.debug("Generic light background used."); + const lightFrameRGBA = { r: 234, g: 234, b: 237, a: 1 }; + return this._composeRGBA(colorRGBA, lightFrameRGBA); + } + + /** + * Update the Letterboxing colors and related classes, or clear them if + * Letterboxing is not enabled. + * + * @param {Window} win - The window to update the colors for. + * @param {boolean} letterboxingEnabled - Whether Letterboxing is enabled. + */ + _updateLetterboxingColors(win, letterboxingEnabled) { + let urlbarBackgroundRGBA; + let urlbarTextRGBA; + let contentSeparatorRGBA; + let urlbarBackgroundDark = false; + let lowBackgroundOutlineContrast = false; + + if (letterboxingEnabled) { + // Want the effective colour of various elements without any alpha values + // so they can be used consistently. + const navbarStyle = win.getComputedStyle( + win.document.getElementById("nav-bar") + ); + const containerRGBA = this._calculateUrlbarContainerColor( + win, + navbarStyle + ); + urlbarBackgroundRGBA = this._composeRGBA( + this._convertToRGBA( + win, + navbarStyle, + "--toolbar-field-background-color" + ), + containerRGBA + ); + urlbarTextRGBA = this._composeRGBA( + this._convertToRGBA(win, navbarStyle, "--toolbar-field-color"), + urlbarBackgroundRGBA + ); + /* Separator between the urlbar container #nav-bar and the tabbox. */ + const tabboxStyle = win.getComputedStyle(win.gBrowser.tabbox); + contentSeparatorRGBA = this._composeRGBA( + this._convertToRGBA( + win, + tabboxStyle, + "--chrome-content-separator-color" + ), + containerRGBA + ); + const bgColor = new lazy.Color( + urlbarBackgroundRGBA.r, + urlbarBackgroundRGBA.g, + urlbarBackgroundRGBA.b + ); + const outlineColor = new lazy.Color( + contentSeparatorRGBA.r, + contentSeparatorRGBA.g, + contentSeparatorRGBA.b + ); + const contrastRatio = bgColor.contrastRatio(outlineColor); + lazy.logConsole.debug( + "Outline-background contrast ratio.", + contrastRatio + ); + urlbarBackgroundDark = bgColor.relativeLuminance < 0.5; + /* Very low contrast ratio. For reference the default light theme has + * a contrast ratio of ~1.1. */ + lowBackgroundOutlineContrast = contrastRatio < 1.05; + } + for (const { name, colorRGBA } of [ + { + name: "--letterboxing-urlbar-text-color", + colorRGBA: urlbarTextRGBA, + }, + { + name: "--letterboxing-urlbar-background-color", + colorRGBA: urlbarBackgroundRGBA, + }, + { + name: "--letterboxing-content-separator-color", + colorRGBA: contentSeparatorRGBA, + }, + ]) { + if (letterboxingEnabled) { + win.gBrowser.tabbox.style.setProperty( + name, + `rgb(${colorRGBA.r}, ${colorRGBA.g}, ${colorRGBA.b})` + ); + } else { + win.gBrowser.tabbox.style.removeProperty(name); + } + } + win.gBrowser.tabbox.classList.toggle( + "letterboxing-urlbar-background-dark", + urlbarBackgroundDark + ); + win.gBrowser.tabbox.classList.toggle( + "letterboxing-low-background-outline-contrast", + lowBackgroundOutlineContrast + ); } _detachWindow(aWindow) { @@ -886,7 +1161,7 @@ class _RFPHelper { aWindow.removeEventListener("TabOpen", this); // revert tabpanel's style to default - tabBrowser.tabpanels?.classList.remove("letterboxing"); + tabBrowser.tabbox.classList.remove("letterboxing"); // and restore default size on each browser element for (let tab of tabBrowser.tabs) { @@ -896,6 +1171,9 @@ class _RFPHelper { aWindow.removeEventListener("dblclick", this._onWindowDoubleClick); delete aWindow.shrinkToLetterbox; aWindow.removeEventListener("sizemodechange", windowResizeHandler); + + aWindow.removeEventListener("nativethemechange", this); + this._updateLetterboxingColors(aWindow, false); } _handleDOMWindowOpened(win) { ===================================== toolkit/components/resistfingerprinting/content/letterboxing.css ===================================== @@ -7,17 +7,106 @@ * RFPHelper.sys.mjs (LETTERBOX_CSS_SELECTOR and LETTERBOX_CSS_URL, * respectively), where --letterboxing-width & --letterboxing-height are * actually set. + * Keep this block first and separate to the rules that do not necessarily + * require --letterboxing-width or --letterboxing-height. */ .letterboxing { - --letterboxing-bgcolor: var(--tabpanel-background-color); - --letterboxing-border-radius: 8px; - --letterboxing-border-top-radius: 0; + .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser { + width: var(--letterboxing-width) !important; + height: var(--letterboxing-height) !important; + } +} + +#tabbrowser-tabbox.letterboxing { + --letterboxing-bgcolor: var(--background-color-canvas); + /* Match the border radius used for the sidebar. */ + --letterboxing-border-radius: var(--border-radius-medium); + --letterboxing-border-radius-top: 0; --letterboxing-vertical-alignment: start; - --letterboxing-shadow-color: rgba(12, 12, 13, 0.10); - --letterboxing-gradient-color1: var(--letterboxing-bgcolor); - --letterboxing-gradient-color2: color-mix(in srgb, var(--chrome-content-separator-color) 50%, var(--letterboxing-bgcolor)); - --letterboxing-border-color: var(--letterboxing-bgcolor); - --letterboxing-decorator-visibility: visible; + --letterboxing-shadow: none; + --letterboxing-outline-color: var(--border-color); + --letterboxing-outline-width: 1px; + + @media not ((prefers-contrast) or (forced-colors)) { + /* Match the #sidebar outline width. */ + --letterboxing-outline-width: 0.5px; + --letterboxing-shadow-color: rgba(58, 57, 68, 0.20); + --letterboxing-shadow: 0 2px 14px 0 var(--letterboxing-shadow-color); + + /* Match the effective urlbar background colour. */ + --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color); + /* Match the effective colour of the separator between the urlbar container + * and the content. */ + --letterboxing-outline-color: var(--letterboxing-content-separator-color); + + &.letterboxing-urlbar-background-dark { + --letterboxing-shadow-color: #15141a; + } + + &.letterboxing-low-background-outline-contrast { + /* The default content separator colour has insufficient contrast. */ + --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, black); + + &.letterboxing-urlbar-background-dark { + /* Lighten the colour. */ + --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, white); + } + } + } + + @media (prefers-contrast) and (not (forced-colors)) { + :root[lwtheme] & { + /* User with prefers-contrast coming from the system settings, but also an + * installed theme. */ + --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color); + /* Presumably a user with prefers-contrast and a custom theme has chosen + * a theme where the contrast between the urlbar and the text is + * sufficiently high or low. */ + --letterboxing-outline-color: var(--letterboxing-urlbar-text-color); + } + } + + background: var(--letterboxing-bgcolor); + + &:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline) { + /* Letterboxing outline is visible for the current tab. Replace the usual + * outline to match the Letterboxing outline. For most scenarios, this + * should be mostly the same colour as when Letterboxing is not visible. But + * it may make a difference for some theme combinations. */ + outline-color: var(--letterboxing-outline-color); + outline-width: var(--letterboxing-outline-width); + } + + #tabbrowser-tabpanels { + /* Override the --tabpanel-background-color. + * Also, make sure this remains transparent, otherwise it will overlap the + * parent's corner's border-radius due to it's "position: relative" rule. */ + /* TODO: FIX this for newtab pages. tor-browser#44085 */ + background: transparent; + } + + /* stylelint-disable-next-line media-query-no-invalid */ + @media -moz-pref("sidebar.revamp") { + :root:not([inDOMFullscreen]) &[sidebar-shown]:not(.letterboxing-nav-toolbox-hidden):is( + /* When the Letterboxing area is aligned to the top, show the rounded + * corner if there is enough vertical space between the sidebar and the + * browser element, which is not rounded at the top. */ + :not(.letterboxing-vcenter):has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-sidebar-corner), + /* When the Letterboxing area is aligned to the centre, show the rounded + * corner if the Letterboxing border is shown. */ + .letterboxing-vcenter:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline) + ) { + /* stylelint-disable-next-line media-query-no-invalid */ + @media -moz-pref("sidebar.position_start") { + border-start-start-radius: var(--letterboxing-border-radius); + } + + /* stylelint-disable-next-line media-query-no-invalid */ + @media not -moz-pref("sidebar.position_start") { + border-start-end-radius: var(--letterboxing-border-radius); + } + } + } .browserContainer { /* @@ -26,101 +115,71 @@ * doesn't get notified on horizontal shrinking. */ overflow: hidden; - background: var(--letterboxing-bgcolor); } - .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser { - box-shadow: 0 4px 8px 0 var(--letterboxing-shadow-color); - border-radius: var(--letterboxing-border-radius); - border-top-left-radius: var(--letterboxing-border-top-radius); - border-top-right-radius: var(--letterboxing-border-top-radius); - width: var(--letterboxing-width) !important; - height: var(--letterboxing-height) !important; - background: var(--letterboxing-gradient-color2); + &.letterboxing-vcenter { + --letterboxing-border-radius-top: var(--letterboxing-border-radius); + --letterboxing-vertical-alignment: center; } } -:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) { - place-content: start center; -} - -.browserDecorator { - display: none; - pointer-events: none; - background: transparent; - position: relative; - z-index: 1; -} +.browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) { + :root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready & { + place-content: var(--letterboxing-vertical-alignment) center; + } -.letterboxing.letterboxing-vcenter .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) { - --letterboxing-border-top-radius: var(--letterboxing-border-radius); - --letterboxing-vertical-alignment: center; -} + :root:not([inDOMFullscreen]) .letterboxing &.letterboxing-show-outline { + browser { + /* We use clip-path rather than border-radius because border-radius on its + * own leads to rendering artefacts in the corners (tested with GNOME). + * See tor-browser#44214 (comment 3262962). */ + /* TODO: Use border-radius once bugzilla bug 1991874 is resolved. */ + clip-path: rect(auto auto auto auto round var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius)); + } -.letterboxing.letterboxing-gradient .browserContainer { - background: linear-gradient(283deg, var(--letterboxing-gradient-color1) 0%, var(--letterboxing-gradient-color2) 100%), var(--letterboxing-bgcolor); -} + .browserDecorator { + /* Need a separate browserDecorator element because the clip-path on the + * browser would exclude the outline and box-shadow. */ + /* TODO: Move these rules to the browser element once bugzilla bug 1991874 + * is resolved, and drop browserDecorator. */ + display: block; + border-radius: var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius); + /* NOTE: The top outline will not be visible when this is aligned to the + * top. */ + outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color); + box-shadow: var(--letterboxing-shadow); + } -:root:not([inDOMFullscreen]) .letterboxing .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) - > .browserDecorator { - display: initial; - visibility: var(--letterboxing-decorator-visibility); - border-radius: var(--letterboxing-border-radius); - border-top-left-radius: var(--letterboxing-border-top-radius); - border-top-right-radius: var(--letterboxing-border-top-radius); - box-shadow: var(--letterboxing-border-color) 0 0 .1px inset, var(--letterboxing-border-color) 0 0 .1px; - border: .1px solid var(--letterboxing-border-color); - outline: .1px solid var(--letterboxing-bgcolor); - height: calc(var(--letterboxing-height) + 1px); - top: -1px; -} + #statuspanel:not([mirror]) #statuspanel-label { + border-end-start-radius: var(--letterboxing-border-radius); + } -.letterboxing-vcenter .browserDecorator { - height: auto !important; - top: 0 !important; -} + #statuspanel[mirror] #statuspanel-label { + border-end-end-radius: var(--letterboxing-border-radius); + } + } -/* - Align status bar with content. - TODO: switch to nested CSS selectors for conciseness when available (Firefox >= 117) -*/ -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel:not([hidden]) { - position: relative; - place-self: end left; - left: 0; - right: 0; - z-index: 2; - --letterboxing-status-left-radius: var(--letterboxing-border-radius); - --letterboxing-status-right-radius: 0; -} -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel:not([mirror]):-moz-locale-dir(rtl), -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel[mirror]:-moz-locale-dir(ltr) { - left: 0; - right: 0; - --letterboxing-status-right-radius: var(--letterboxing-border-radius); - --letterboxing-status-left-radius: 0; - justify-self: right; -} + #statuspanel { + position: relative; + place-self: end start; + z-index: 2; -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) -#statuspanel-label { - border-radius: 0 0 var(--letterboxing-status-right-radius) var(--letterboxing-status-left-radius); - margin: 0; - border: 1px solid var(--letterboxing-border-color); - max-width: calc(var(--letterboxing-width) * .5); -} + &[mirror] { + justify-self: end; + } + } -browser:fullscreen { - --letterboxing-border-top-radius: 0; - --letterboxing-border-radius: 0; + #statuspanel-label { + margin: 0; + outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color); + max-width: calc(var(--letterboxing-width) * .5); + } } -:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) { - place-content: var(--letterboxing-vertical-alignment) center; +.browserDecorator { + display: none; + pointer-events: none; + background: transparent; + position: relative; + z-index: 1; } View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/42… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/42… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-140.4.0esr-15.0-1] 12 commits: fixup! BB 41919: Letterboxing, add temporarily visible web content-size...
by henry (@henry) 15 Oct '25

15 Oct '25
henry pushed to branch base-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: a8d9aeef by Henry Wilkes at 2025-10-15T18:01:07+01:00 fixup! BB 41919: Letterboxing, add temporarily visible web content-size indicator on window resizing. TB 44214: Fix letterboxing status indicator for RTL. - - - - - 73862bbd by Henry Wilkes at 2025-10-15T18:01:08+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop unnecessary CSS rules. - - - - - 09abf933 by Henry Wilkes at 2025-10-15T18:01:09+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS logical positions for the status panel, rather than "left" and "right". - - - - - 943bae35 by Henry Wilkes at 2025-10-15T18:01:09+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop upstream's rules for placing content. - - - - - 9b4b135b by Henry Wilkes at 2025-10-15T18:01:10+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop letterboxing gradient. - - - - - 4d9307d1 by Henry Wilkes at 2025-10-15T18:01:11+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Separate out the essential --letterboxing-width and --letterboxing-height rules into their own .letterboxing block, to have the property values set on. - - - - - e4d77eb2 by Henry Wilkes at 2025-10-15T18:01:12+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 1. - - - - - 261e8522 by Henry Wilkes at 2025-10-15T18:01:12+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 2. - - - - - 2282a161 by Henry Wilkes at 2025-10-15T18:01:13+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Move the letterboxing classes one element up from tabpanels to tabbox. This is because we need to restyle the tabbox. - - - - - f48a8536 by Henry Wilkes at 2025-10-15T18:01:14+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use a CSS class to show/hide the letterboxing border, rather than setting the border-radius in javascript. - - - - - 26d1b64a by Henry Wilkes at 2025-10-15T18:01:15+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Rename CSS variable from top-radius to radius-top. This is closer to what upstream has done recently for tokens, where higher specificity is appended. - - - - - 940d5e2c by Henry Wilkes at 2025-10-15T18:01:15+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Update letterboxing styling for ESR 140. - - - - - 4 changed files: - browser/base/content/browser-fullScreenAndPointerLock.js - browser/themes/shared/tabbrowser/content-area.css - toolkit/components/resistfingerprinting/RFPHelper.sys.mjs - toolkit/components/resistfingerprinting/content/letterboxing.css Changes: ===================================== browser/base/content/browser-fullScreenAndPointerLock.js ===================================== @@ -879,7 +879,13 @@ var FullScreen = { } this._isChromeCollapsed = false; - Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "shown"); + // Need a subject to know which window this applies to. + // Base browser patch can be dropped after bugzilla bug 1992036. + Services.obs.notifyObservers( + gNavToolbox, + "fullscreen-nav-toolbox", + "shown" + ); }, hideNavToolbox(aAnimate = false) { @@ -943,7 +949,13 @@ var FullScreen = { gNavToolbox.style.marginTop = -gNavToolbox.getBoundingClientRect().height + "px"; this._isChromeCollapsed = true; - Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "hidden"); + // Need a subject to know which window this applies to. + // Base browser patch can be dropped after bugzilla bug 1880918. + Services.obs.notifyObservers( + gNavToolbox, + "fullscreen-nav-toolbox", + "hidden" + ); MousePosTracker.removeListener(this); }, ===================================== browser/themes/shared/tabbrowser/content-area.css ===================================== @@ -242,13 +242,17 @@ } } -#statuspanel[type=letterboxingStatus] > #statuspanel-label, -#statuspanel[previoustype=letterboxingStatus][inactive] > #statuspanel-label { +#statuspanel:is([type=letterboxingStatus], [previoustype=letterboxingStatus][inactive]) > #statuspanel-label { background-image: url("chrome://browser/skin/window.svg"); background-size: 1em; background-repeat: no-repeat; - background-position-x: .5em; background-position-y: center; + background-position-x: left .5em; + + &:-moz-locale-dir(rtl) { + background-position-x: right .5em; + } + padding-inline-start: 2em; -moz-context-properties: fill; fill: var(--color-accent-primary); ===================================== toolkit/components/resistfingerprinting/RFPHelper.sys.mjs ===================================== @@ -18,8 +18,6 @@ const kPrefLetterboxingTesting = "privacy.resistFingerprinting.letterboxing.testing"; const kPrefLetterboxingVcenter = "privacy.resistFingerprinting.letterboxing.vcenter"; -const kPrefLetterboxingGradient = - "privacy.resistFingerprinting.letterboxing.gradient"; const kPrefLetterboxingDidForceSize = "privacy.resistFingerprinting.letterboxing.didForceSize"; const kPrefLetterboxingRememberSize = @@ -28,10 +26,17 @@ const kPrefLetterboxingRememberSize = const kTopicDOMWindowOpened = "domwindowopened"; const kTopicDOMWindowClosed = "domwindowclosed"; +const kTopicFullscreenNavToolbox = "fullscreen-nav-toolbox"; + const kPrefResizeWarnings = "privacy.resistFingerprinting.resizeWarnings"; +const kPrefVerticalTabs = "sidebar.verticalTabs"; + const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + Color: "resource://gre/modules/Color.sys.mjs", +}); ChromeUtils.defineLazyGetter(lazy, "logConsole", () => console.createInstance({ prefix: "RFPHelper", @@ -155,7 +160,8 @@ class _RFPHelper { Services.prefs.addObserver(kPrefResistFingerprinting, this); Services.prefs.addObserver(kPrefLetterboxing, this); Services.prefs.addObserver(kPrefLetterboxingVcenter, this); - Services.prefs.addObserver(kPrefLetterboxingGradient, this); + Services.prefs.addObserver(kPrefVerticalTabs, this); + Services.obs.addObserver(this, kTopicFullscreenNavToolbox); XPCOMUtils.defineLazyPreferenceGetter( this, @@ -188,9 +194,10 @@ class _RFPHelper { // Remove unconditional observers Services.prefs.removeObserver(kPrefResistFingerprinting, this); - Services.prefs.removeObserver(kPrefLetterboxingGradient, this); Services.prefs.removeObserver(kPrefLetterboxingVcenter, this); Services.prefs.removeObserver(kPrefLetterboxing, this); + Services.prefs.removeObserver(kPrefVerticalTabs, this); + Services.obs.removeObserver(this, kTopicFullscreenNavToolbox); // Remove the RFP observers, swallowing exceptions if they weren't present this._removeLanguagePrefObservers(); } @@ -212,6 +219,15 @@ class _RFPHelper { case kTopicDOMWindowClosed: this._handleDOMWindowClosed(subject); break; + case kTopicFullscreenNavToolbox: + // The `subject` is the gNavToolbox. + // Record whether the toobox has been hidden when the browser (not + // content) is in fullscreen. + subject.ownerGlobal.gBrowser.tabbox.classList.toggle( + "letterboxing-nav-toolbox-hidden", + data === "hidden" + ); + break; default: break; } @@ -226,6 +242,13 @@ class _RFPHelper { resizeObserver.observe(browser.parentElement); break; } + case "nativethemechange": + // NOTE: "nativethemechange" seems to always be sent after + // "windowlwthemeupdate". So all the lwtheme CSS properties should be + // set to the new theme's values already, so we don't need to wait for + // windowlwthemeupdate. + this._updateLetterboxingColors(aMessage.currentTarget, true); + break; default: break; } @@ -245,9 +268,13 @@ class _RFPHelper { Services.prefs.clearUserPref(kPrefLetterboxingDidForceSize); // fall-through case kPrefLetterboxingVcenter: - case kPrefLetterboxingGradient: this._handleLetterboxingPrefChanged(); break; + case kPrefVerticalTabs: + if (this.letterboxingEnabled) { + forEachWindow(win => this._updateLetterboxingColors(win)); + } + break; default: break; } @@ -452,7 +479,7 @@ class _RFPHelper { // If not already cached on the document object, traverse the CSSOM and // find the rule applying the default letterboxing styles to browsers // preemptively in order to beat race conditions on tab/window creation - return (document._letterboxingMarginsRule ||= (() => { + return (document._letterboxingDefaultRule ||= (() => { const LETTERBOX_CSS_SELECTOR = ".letterboxing"; const LETTERBOX_CSS_URL = "chrome://global/content/resistfingerprinting/letterboxing.css"; @@ -688,26 +715,22 @@ class _RFPHelper { if (lastRoundedSize) { // Check whether the letterboxing margin is less than the border radius, - // and if so flatten the borders. - let borderRadius = parseInt( - win - .getComputedStyle(browserContainer) - .getPropertyValue("--letterboxing-border-radius") + // and if so do not show an outline. + const gapVertical = parentHeight - lastRoundedSize.height; + const gapHorizontal = parentWidth - lastRoundedSize.width; + browserParent.classList.toggle( + "letterboxing-show-outline", + gapVertical >= this._letterboxingBorderRadius || + gapHorizontal >= this._letterboxingBorderRadius + ); + // When the Letterboxing area is top-aligned, only show the sidebar corner + // if there is enough horizontal space. + // The factor of 4 is from the horizontal centre-alignment and wanting + // enough space for twice the corner radius. + browserParent.classList.toggle( + "letterboxing-show-sidebar-corner", + gapHorizontal >= 4 * this._letterboxingBorderRadius ); - if ( - borderRadius && - parentWidth - lastRoundedSize.width < borderRadius && - parentHeight - lastRoundedSize.height < borderRadius - ) { - borderRadius = 0; - } else { - borderRadius = ""; - } - styleChanges.queueIfNeeded(browserParent, { - "--letterboxing-decorator-visibility": - borderRadius === 0 ? "hidden" : "", - "--letterboxing-border-radius": borderRadius, - }); if (win.gBrowser.selectedBrowser == aBrowser) { const updateStatus = async args => { win.XULBrowserWindow.letterboxingStatus = args @@ -769,20 +792,31 @@ class _RFPHelper { _resetContentSize(aBrowser) { aBrowser.parentElement.classList.add("exclude-letterboxing"); + aBrowser.parentElement.classList.remove( + "letterboxing-show-outline", + "letterboxing-show-sidebar-corner" + ); } _updateSizeForTabsInWindow(aWindow) { let tabBrowser = aWindow.gBrowser; - tabBrowser.tabpanels?.classList.add("letterboxing"); - tabBrowser.tabpanels?.classList.toggle( + tabBrowser.tabbox.classList.add("letterboxing"); + tabBrowser.tabbox.classList.toggle( "letterboxing-vcenter", Services.prefs.getBoolPref(kPrefLetterboxingVcenter, false) ); - tabBrowser.tabpanels?.classList.toggle( - "letterboxing-gradient", - Services.prefs.getBoolPref(kPrefLetterboxingGradient, false) - ); + if (this._letterboxingBorderRadius === undefined && tabBrowser.tabbox) { + // Cache the value since it is not expected to change in a session for any + // window. + this._letterboxingBorderRadius = Math.ceil( + parseFloat( + aWindow + .getComputedStyle(tabBrowser.tabbox) + .getPropertyValue("--letterboxing-border-radius") + ) + ); + } for (let tab of tabBrowser.tabs) { let browser = tab.linkedBrowser; @@ -791,7 +825,7 @@ class _RFPHelper { // We need to add this class late because otherwise new windows get // maximized. aWindow.setTimeout(() => { - tabBrowser.tabpanels?.classList.add("letterboxing-ready"); + tabBrowser.tabbox.classList.add("letterboxing-ready"); if (!aWindow._rfpOriginalSize) { this._recordWindowSize(aWindow); } @@ -869,6 +903,247 @@ class _RFPHelper { this._resizeObservers.set(aWindow, resizeObserver); // Rounding the content viewport. this._updateSizeForTabsInWindow(aWindow); + + this._updateLetterboxingColors(aWindow, true); + aWindow.addEventListener("nativethemechange", this); + } + + /** + * Convert a CSS property to its RGBA value. + * + * @param {Window} win - The window for the element. + * @param {CSSStyleDeclaration} style - The computed style for the element we + * want to grab the color from. + * @param {string} property - The name of the property we want. + * + * @returns {InspectorRGBATuple} - The RGBA color. The "r", "g", "b" fields + * are relative to the 0-255 color range. The "a" field is in the 0-1 range. + */ + _convertToRGBA(win, style, property) { + let cssColor = style.getPropertyValue(property); + if (!cssColor) { + lazy.logConsole.error(`Missing color "${property}"`); + return { r: 0, g: 0, b: 0, a: 0 }; + } + const currentColorRegex = + /(^|[^a-zA-Z0-9_-])currentColor($|[^a-zA-Z0-9_-])/g; + if (currentColorRegex.test(cssColor)) { + const currentColor = style.color; + cssColor = cssColor.replace(currentColorRegex, (_, pre, post) => { + return pre + currentColor + post; + }); + lazy.logConsole.debug( + "Replaced currentColor.", + property, + currentColor, + cssColor + ); + } + /* Can drop the document argument after bugzilla bug 1973684 (142). */ + const colorRGBA = win.InspectorUtils.colorToRGBA(cssColor, win.document); + if (!colorRGBA) { + lazy.logConsole.error( + `Failed to convert "${property}" color (${cssColor}) to RGBA` + ); + return { r: 0, g: 0, b: 0, a: 0 }; + } + return colorRGBA; + } + + /** + * Compose two colors with alpha values on top of each other. + * + * @param {InspectorRGBATuple} topRGBA - The color to place on the top. + * @param {InspectorRGBATuple} bottomRGBA - The color to place on the bottom. + * + * @returns {InspectorRGBATuple} - The composed color. + */ + _composeRGBA(topRGBA, bottomRGBA) { + const topA = Math.max(0, Math.min(1, topRGBA.a)); + const bottomA = Math.max(0, Math.min(1, bottomRGBA.a)); + const a = topA + bottomA - topA * bottomA; // Should be 1 if either is 1. + if (a === 0) { + return { r: 0, g: 0, b: 0, a }; + } + const ret = { a }; + for (const field of ["r", "g", "b"]) { + ret[field] = + (topRGBA[field] * topA + bottomRGBA[field] * bottomA * (1 - topA)) / a; + } + return ret; + } + + /** + * Calculate the urlbar's container opaque background color, removing any + * transparency. + * + * @param {Window} win - The window to calculate the color for. + * @param {CSSStyleDeclaration} style - The computed style for the #nav-bar + * element. + * + * @returns {InspectorRGBATuple} - The calculated color, which will be opaque. + */ + _calculateUrlbarContainerColor(win, style) { + let colorRGBA; + if (!Services.prefs.getBoolPref(kPrefVerticalTabs)) { + lazy.logConsole.debug("Toolbar background used."); + colorRGBA = this._convertToRGBA(win, style, "--toolbar-bgcolor"); + if (colorRGBA.a === 1) { + return colorRGBA; + } + } else { + // The urlbar only has the toolbox colour. + colorRGBA = { r: 0, g: 0, b: 0, a: 0 }; + } + let toolboxHasBackgroundImage = false; + const isLwTheme = win.document.documentElement.hasAttribute("lwtheme"); + if (isLwTheme) { + for (const prop of ["--lwt-header-image", "--lwt-additional-images"]) { + const headerImage = style.getPropertyValue(prop); + if (headerImage && headerImage !== "none") { + // The theme sets a background image behind the urlbar. No easy way to + // derive a single colour from this. + toolboxHasBackgroundImage = true; + lazy.logConsole.debug( + "Toolbox has background image.", + prop, + headerImage + ); + break; + } + } + } + if (!toolboxHasBackgroundImage) { + lazy.logConsole.debug("Toolbox background used."); + colorRGBA = this._composeRGBA( + colorRGBA, + this._convertToRGBA(win, style, "--toolbox-bgcolor") + ); + if (colorRGBA.a === 1) { + return colorRGBA; + } + } + + // Determine whether the urlbar is dark. + // At this point, the urlbar background has some transparency, likely on top + // of an image. + // We use the theme's text colour to figure out whether the urlbar + // background is overall meant to be light or dark. Unlike the urlbar, we + // expect this colour to be (almost) opaque. + const textRGBA = this._convertToRGBA(win, style, "--toolbar-field-color"); + const textColor = new lazy.Color(textRGBA.r, textRGBA.g, textRGBA.b); + if (textColor.relativeLuminance >= 0.5) { + // Light text, so assume it has a dark background. + // Combine with a generic opaque dark colour. Copied from "frame" for the + // built-in dark theme. + lazy.logConsole.debug("Generic dark background used."); + const darkFrameRGBA = { r: 28, g: 27, b: 34, a: 1 }; + return this._composeRGBA(colorRGBA, darkFrameRGBA); + } + // Combine with an opaque light colour. Copied from "frame" for the built-in + // light theme. + lazy.logConsole.debug("Generic light background used."); + const lightFrameRGBA = { r: 234, g: 234, b: 237, a: 1 }; + return this._composeRGBA(colorRGBA, lightFrameRGBA); + } + + /** + * Update the Letterboxing colors and related classes, or clear them if + * Letterboxing is not enabled. + * + * @param {Window} win - The window to update the colors for. + * @param {boolean} letterboxingEnabled - Whether Letterboxing is enabled. + */ + _updateLetterboxingColors(win, letterboxingEnabled) { + let urlbarBackgroundRGBA; + let urlbarTextRGBA; + let contentSeparatorRGBA; + let urlbarBackgroundDark = false; + let lowBackgroundOutlineContrast = false; + + if (letterboxingEnabled) { + // Want the effective colour of various elements without any alpha values + // so they can be used consistently. + const navbarStyle = win.getComputedStyle( + win.document.getElementById("nav-bar") + ); + const containerRGBA = this._calculateUrlbarContainerColor( + win, + navbarStyle + ); + urlbarBackgroundRGBA = this._composeRGBA( + this._convertToRGBA( + win, + navbarStyle, + "--toolbar-field-background-color" + ), + containerRGBA + ); + urlbarTextRGBA = this._composeRGBA( + this._convertToRGBA(win, navbarStyle, "--toolbar-field-color"), + urlbarBackgroundRGBA + ); + /* Separator between the urlbar container #nav-bar and the tabbox. */ + const tabboxStyle = win.getComputedStyle(win.gBrowser.tabbox); + contentSeparatorRGBA = this._composeRGBA( + this._convertToRGBA( + win, + tabboxStyle, + "--chrome-content-separator-color" + ), + containerRGBA + ); + const bgColor = new lazy.Color( + urlbarBackgroundRGBA.r, + urlbarBackgroundRGBA.g, + urlbarBackgroundRGBA.b + ); + const outlineColor = new lazy.Color( + contentSeparatorRGBA.r, + contentSeparatorRGBA.g, + contentSeparatorRGBA.b + ); + const contrastRatio = bgColor.contrastRatio(outlineColor); + lazy.logConsole.debug( + "Outline-background contrast ratio.", + contrastRatio + ); + urlbarBackgroundDark = bgColor.relativeLuminance < 0.5; + /* Very low contrast ratio. For reference the default light theme has + * a contrast ratio of ~1.1. */ + lowBackgroundOutlineContrast = contrastRatio < 1.05; + } + for (const { name, colorRGBA } of [ + { + name: "--letterboxing-urlbar-text-color", + colorRGBA: urlbarTextRGBA, + }, + { + name: "--letterboxing-urlbar-background-color", + colorRGBA: urlbarBackgroundRGBA, + }, + { + name: "--letterboxing-content-separator-color", + colorRGBA: contentSeparatorRGBA, + }, + ]) { + if (letterboxingEnabled) { + win.gBrowser.tabbox.style.setProperty( + name, + `rgb(${colorRGBA.r}, ${colorRGBA.g}, ${colorRGBA.b})` + ); + } else { + win.gBrowser.tabbox.style.removeProperty(name); + } + } + win.gBrowser.tabbox.classList.toggle( + "letterboxing-urlbar-background-dark", + urlbarBackgroundDark + ); + win.gBrowser.tabbox.classList.toggle( + "letterboxing-low-background-outline-contrast", + lowBackgroundOutlineContrast + ); } _detachWindow(aWindow) { @@ -886,7 +1161,7 @@ class _RFPHelper { aWindow.removeEventListener("TabOpen", this); // revert tabpanel's style to default - tabBrowser.tabpanels?.classList.remove("letterboxing"); + tabBrowser.tabbox.classList.remove("letterboxing"); // and restore default size on each browser element for (let tab of tabBrowser.tabs) { @@ -896,6 +1171,9 @@ class _RFPHelper { aWindow.removeEventListener("dblclick", this._onWindowDoubleClick); delete aWindow.shrinkToLetterbox; aWindow.removeEventListener("sizemodechange", windowResizeHandler); + + aWindow.removeEventListener("nativethemechange", this); + this._updateLetterboxingColors(aWindow, false); } _handleDOMWindowOpened(win) { ===================================== toolkit/components/resistfingerprinting/content/letterboxing.css ===================================== @@ -7,17 +7,106 @@ * RFPHelper.sys.mjs (LETTERBOX_CSS_SELECTOR and LETTERBOX_CSS_URL, * respectively), where --letterboxing-width & --letterboxing-height are * actually set. + * Keep this block first and separate to the rules that do not necessarily + * require --letterboxing-width or --letterboxing-height. */ .letterboxing { - --letterboxing-bgcolor: var(--tabpanel-background-color); - --letterboxing-border-radius: 8px; - --letterboxing-border-top-radius: 0; + .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser { + width: var(--letterboxing-width) !important; + height: var(--letterboxing-height) !important; + } +} + +#tabbrowser-tabbox.letterboxing { + --letterboxing-bgcolor: var(--background-color-canvas); + /* Match the border radius used for the sidebar. */ + --letterboxing-border-radius: var(--border-radius-medium); + --letterboxing-border-radius-top: 0; --letterboxing-vertical-alignment: start; - --letterboxing-shadow-color: rgba(12, 12, 13, 0.10); - --letterboxing-gradient-color1: var(--letterboxing-bgcolor); - --letterboxing-gradient-color2: color-mix(in srgb, var(--chrome-content-separator-color) 50%, var(--letterboxing-bgcolor)); - --letterboxing-border-color: var(--letterboxing-bgcolor); - --letterboxing-decorator-visibility: visible; + --letterboxing-shadow: none; + --letterboxing-outline-color: var(--border-color); + --letterboxing-outline-width: 1px; + + @media not ((prefers-contrast) or (forced-colors)) { + /* Match the #sidebar outline width. */ + --letterboxing-outline-width: 0.5px; + --letterboxing-shadow-color: rgba(58, 57, 68, 0.20); + --letterboxing-shadow: 0 2px 14px 0 var(--letterboxing-shadow-color); + + /* Match the effective urlbar background colour. */ + --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color); + /* Match the effective colour of the separator between the urlbar container + * and the content. */ + --letterboxing-outline-color: var(--letterboxing-content-separator-color); + + &.letterboxing-urlbar-background-dark { + --letterboxing-shadow-color: #15141a; + } + + &.letterboxing-low-background-outline-contrast { + /* The default content separator colour has insufficient contrast. */ + --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, black); + + &.letterboxing-urlbar-background-dark { + /* Lighten the colour. */ + --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, white); + } + } + } + + @media (prefers-contrast) and (not (forced-colors)) { + :root[lwtheme] & { + /* User with prefers-contrast coming from the system settings, but also an + * installed theme. */ + --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color); + /* Presumably a user with prefers-contrast and a custom theme has chosen + * a theme where the contrast between the urlbar and the text is + * sufficiently high or low. */ + --letterboxing-outline-color: var(--letterboxing-urlbar-text-color); + } + } + + background: var(--letterboxing-bgcolor); + + &:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline) { + /* Letterboxing outline is visible for the current tab. Replace the usual + * outline to match the Letterboxing outline. For most scenarios, this + * should be mostly the same colour as when Letterboxing is not visible. But + * it may make a difference for some theme combinations. */ + outline-color: var(--letterboxing-outline-color); + outline-width: var(--letterboxing-outline-width); + } + + #tabbrowser-tabpanels { + /* Override the --tabpanel-background-color. + * Also, make sure this remains transparent, otherwise it will overlap the + * parent's corner's border-radius due to it's "position: relative" rule. */ + /* TODO: FIX this for newtab pages. tor-browser#44085 */ + background: transparent; + } + + /* stylelint-disable-next-line media-query-no-invalid */ + @media -moz-pref("sidebar.revamp") { + :root:not([inDOMFullscreen]) &[sidebar-shown]:not(.letterboxing-nav-toolbox-hidden):is( + /* When the Letterboxing area is aligned to the top, show the rounded + * corner if there is enough vertical space between the sidebar and the + * browser element, which is not rounded at the top. */ + :not(.letterboxing-vcenter):has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-sidebar-corner), + /* When the Letterboxing area is aligned to the centre, show the rounded + * corner if the Letterboxing border is shown. */ + .letterboxing-vcenter:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline) + ) { + /* stylelint-disable-next-line media-query-no-invalid */ + @media -moz-pref("sidebar.position_start") { + border-start-start-radius: var(--letterboxing-border-radius); + } + + /* stylelint-disable-next-line media-query-no-invalid */ + @media not -moz-pref("sidebar.position_start") { + border-start-end-radius: var(--letterboxing-border-radius); + } + } + } .browserContainer { /* @@ -26,101 +115,71 @@ * doesn't get notified on horizontal shrinking. */ overflow: hidden; - background: var(--letterboxing-bgcolor); } - .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser { - box-shadow: 0 4px 8px 0 var(--letterboxing-shadow-color); - border-radius: var(--letterboxing-border-radius); - border-top-left-radius: var(--letterboxing-border-top-radius); - border-top-right-radius: var(--letterboxing-border-top-radius); - width: var(--letterboxing-width) !important; - height: var(--letterboxing-height) !important; - background: var(--letterboxing-gradient-color2); + &.letterboxing-vcenter { + --letterboxing-border-radius-top: var(--letterboxing-border-radius); + --letterboxing-vertical-alignment: center; } } -:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) { - place-content: start center; -} - -.browserDecorator { - display: none; - pointer-events: none; - background: transparent; - position: relative; - z-index: 1; -} +.browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) { + :root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready & { + place-content: var(--letterboxing-vertical-alignment) center; + } -.letterboxing.letterboxing-vcenter .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) { - --letterboxing-border-top-radius: var(--letterboxing-border-radius); - --letterboxing-vertical-alignment: center; -} + :root:not([inDOMFullscreen]) .letterboxing &.letterboxing-show-outline { + browser { + /* We use clip-path rather than border-radius because border-radius on its + * own leads to rendering artefacts in the corners (tested with GNOME). + * See tor-browser#44214 (comment 3262962). */ + /* TODO: Use border-radius once bugzilla bug 1991874 is resolved. */ + clip-path: rect(auto auto auto auto round var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius)); + } -.letterboxing.letterboxing-gradient .browserContainer { - background: linear-gradient(283deg, var(--letterboxing-gradient-color1) 0%, var(--letterboxing-gradient-color2) 100%), var(--letterboxing-bgcolor); -} + .browserDecorator { + /* Need a separate browserDecorator element because the clip-path on the + * browser would exclude the outline and box-shadow. */ + /* TODO: Move these rules to the browser element once bugzilla bug 1991874 + * is resolved, and drop browserDecorator. */ + display: block; + border-radius: var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius); + /* NOTE: The top outline will not be visible when this is aligned to the + * top. */ + outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color); + box-shadow: var(--letterboxing-shadow); + } -:root:not([inDOMFullscreen]) .letterboxing .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) - > .browserDecorator { - display: initial; - visibility: var(--letterboxing-decorator-visibility); - border-radius: var(--letterboxing-border-radius); - border-top-left-radius: var(--letterboxing-border-top-radius); - border-top-right-radius: var(--letterboxing-border-top-radius); - box-shadow: var(--letterboxing-border-color) 0 0 .1px inset, var(--letterboxing-border-color) 0 0 .1px; - border: .1px solid var(--letterboxing-border-color); - outline: .1px solid var(--letterboxing-bgcolor); - height: calc(var(--letterboxing-height) + 1px); - top: -1px; -} + #statuspanel:not([mirror]) #statuspanel-label { + border-end-start-radius: var(--letterboxing-border-radius); + } -.letterboxing-vcenter .browserDecorator { - height: auto !important; - top: 0 !important; -} + #statuspanel[mirror] #statuspanel-label { + border-end-end-radius: var(--letterboxing-border-radius); + } + } -/* - Align status bar with content. - TODO: switch to nested CSS selectors for conciseness when available (Firefox >= 117) -*/ -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel:not([hidden]) { - position: relative; - place-self: end left; - left: 0; - right: 0; - z-index: 2; - --letterboxing-status-left-radius: var(--letterboxing-border-radius); - --letterboxing-status-right-radius: 0; -} -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel:not([mirror]):-moz-locale-dir(rtl), -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel[mirror]:-moz-locale-dir(ltr) { - left: 0; - right: 0; - --letterboxing-status-right-radius: var(--letterboxing-border-radius); - --letterboxing-status-left-radius: 0; - justify-self: right; -} + #statuspanel { + position: relative; + place-self: end start; + z-index: 2; -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) -#statuspanel-label { - border-radius: 0 0 var(--letterboxing-status-right-radius) var(--letterboxing-status-left-radius); - margin: 0; - border: 1px solid var(--letterboxing-border-color); - max-width: calc(var(--letterboxing-width) * .5); -} + &[mirror] { + justify-self: end; + } + } -browser:fullscreen { - --letterboxing-border-top-radius: 0; - --letterboxing-border-radius: 0; + #statuspanel-label { + margin: 0; + outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color); + max-width: calc(var(--letterboxing-width) * .5); + } } -:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) { - place-content: var(--letterboxing-vertical-alignment) center; +.browserDecorator { + display: none; + pointer-events: none; + background: transparent; + position: relative; + z-index: 1; } View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/29c65a… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/29c65a… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] 13 commits: dropme! fixup! TB 41917: Change letterboxing styling for Tor Browser.
by henry (@henry) 15 Oct '25

15 Oct '25
henry pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 0dddcb33 by Henry Wilkes at 2025-10-15T15:59:48+01:00 dropme! fixup! TB 41917: Change letterboxing styling for Tor Browser. TB 44214: Drop letterboxing changes for Tor Browser only. The commit "TB 41917: Change letterboxing styling for Tor Browser." should be entirely dropped. - - - - - fdc52857 by Henry Wilkes at 2025-10-15T15:59:49+01:00 fixup! BB 41919: Letterboxing, add temporarily visible web content-size indicator on window resizing. TB 44214: Fix letterboxing status indicator for RTL. - - - - - f00744fe by Henry Wilkes at 2025-10-15T15:59:50+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop unnecessary CSS rules. - - - - - 805650c4 by Henry Wilkes at 2025-10-15T15:59:51+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS logical positions for the status panel, rather than "left" and "right". - - - - - bfe8d56b by Henry Wilkes at 2025-10-15T15:59:52+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop upstream's rules for placing content. - - - - - b8a35ecc by Henry Wilkes at 2025-10-15T15:59:53+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop letterboxing gradient. - - - - - 999bb9f2 by Henry Wilkes at 2025-10-15T15:59:53+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Separate out the essential --letterboxing-width and --letterboxing-height rules into their own .letterboxing block, to have the property values set on. - - - - - 31b90c2e by Henry Wilkes at 2025-10-15T15:59:54+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 1. - - - - - 58d4acdc by Henry Wilkes at 2025-10-15T15:59:55+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 2. - - - - - 317b197e by Henry Wilkes at 2025-10-15T15:59:56+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Move the letterboxing classes one element up from tabpanels to tabbox. This is because we need to restyle the tabbox. - - - - - cb287829 by Henry Wilkes at 2025-10-15T15:59:57+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use a CSS class to show/hide the letterboxing border, rather than setting the border-radius in javascript. - - - - - ff6fdbef by Henry Wilkes at 2025-10-15T15:59:58+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Rename CSS variable from top-radius to radius-top. This is closer to what upstream has done recently for tokens, where higher specificity is appended. - - - - - 196110ba by Henry Wilkes at 2025-10-15T17:19:07+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Update letterboxing styling for ESR 140. - - - - - 4 changed files: - browser/base/content/browser-fullScreenAndPointerLock.js - browser/themes/shared/tabbrowser/content-area.css - toolkit/components/resistfingerprinting/RFPHelper.sys.mjs - toolkit/components/resistfingerprinting/content/letterboxing.css Changes: ===================================== browser/base/content/browser-fullScreenAndPointerLock.js ===================================== @@ -879,7 +879,13 @@ var FullScreen = { } this._isChromeCollapsed = false; - Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "shown"); + // Need a subject to know which window this applies to. + // Base browser patch can be dropped after bugzilla bug 1992036. + Services.obs.notifyObservers( + gNavToolbox, + "fullscreen-nav-toolbox", + "shown" + ); }, hideNavToolbox(aAnimate = false) { @@ -943,7 +949,13 @@ var FullScreen = { gNavToolbox.style.marginTop = -gNavToolbox.getBoundingClientRect().height + "px"; this._isChromeCollapsed = true; - Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "hidden"); + // Need a subject to know which window this applies to. + // Base browser patch can be dropped after bugzilla bug 1880918. + Services.obs.notifyObservers( + gNavToolbox, + "fullscreen-nav-toolbox", + "hidden" + ); MousePosTracker.removeListener(this); }, ===================================== browser/themes/shared/tabbrowser/content-area.css ===================================== @@ -242,13 +242,17 @@ } } -#statuspanel[type=letterboxingStatus] > #statuspanel-label, -#statuspanel[previoustype=letterboxingStatus][inactive] > #statuspanel-label { +#statuspanel:is([type=letterboxingStatus], [previoustype=letterboxingStatus][inactive]) > #statuspanel-label { background-image: url("chrome://browser/skin/window.svg"); background-size: 1em; background-repeat: no-repeat; - background-position-x: .5em; background-position-y: center; + background-position-x: left .5em; + + &:-moz-locale-dir(rtl) { + background-position-x: right .5em; + } + padding-inline-start: 2em; -moz-context-properties: fill; fill: var(--color-accent-primary); ===================================== toolkit/components/resistfingerprinting/RFPHelper.sys.mjs ===================================== @@ -18,8 +18,6 @@ const kPrefLetterboxingTesting = "privacy.resistFingerprinting.letterboxing.testing"; const kPrefLetterboxingVcenter = "privacy.resistFingerprinting.letterboxing.vcenter"; -const kPrefLetterboxingGradient = - "privacy.resistFingerprinting.letterboxing.gradient"; const kPrefLetterboxingDidForceSize = "privacy.resistFingerprinting.letterboxing.didForceSize"; const kPrefLetterboxingRememberSize = @@ -28,10 +26,17 @@ const kPrefLetterboxingRememberSize = const kTopicDOMWindowOpened = "domwindowopened"; const kTopicDOMWindowClosed = "domwindowclosed"; +const kTopicFullscreenNavToolbox = "fullscreen-nav-toolbox"; + const kPrefResizeWarnings = "privacy.resistFingerprinting.resizeWarnings"; +const kPrefVerticalTabs = "sidebar.verticalTabs"; + const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + Color: "resource://gre/modules/Color.sys.mjs", +}); ChromeUtils.defineLazyGetter(lazy, "logConsole", () => console.createInstance({ prefix: "RFPHelper", @@ -155,7 +160,8 @@ class _RFPHelper { Services.prefs.addObserver(kPrefResistFingerprinting, this); Services.prefs.addObserver(kPrefLetterboxing, this); Services.prefs.addObserver(kPrefLetterboxingVcenter, this); - Services.prefs.addObserver(kPrefLetterboxingGradient, this); + Services.prefs.addObserver(kPrefVerticalTabs, this); + Services.obs.addObserver(this, kTopicFullscreenNavToolbox); XPCOMUtils.defineLazyPreferenceGetter( this, @@ -188,9 +194,10 @@ class _RFPHelper { // Remove unconditional observers Services.prefs.removeObserver(kPrefResistFingerprinting, this); - Services.prefs.removeObserver(kPrefLetterboxingGradient, this); Services.prefs.removeObserver(kPrefLetterboxingVcenter, this); Services.prefs.removeObserver(kPrefLetterboxing, this); + Services.prefs.removeObserver(kPrefVerticalTabs, this); + Services.obs.removeObserver(this, kTopicFullscreenNavToolbox); // Remove the RFP observers, swallowing exceptions if they weren't present this._removeLanguagePrefObservers(); } @@ -212,6 +219,15 @@ class _RFPHelper { case kTopicDOMWindowClosed: this._handleDOMWindowClosed(subject); break; + case kTopicFullscreenNavToolbox: + // The `subject` is the gNavToolbox. + // Record whether the toobox has been hidden when the browser (not + // content) is in fullscreen. + subject.ownerGlobal.gBrowser.tabbox.classList.toggle( + "letterboxing-nav-toolbox-hidden", + data === "hidden" + ); + break; default: break; } @@ -226,6 +242,13 @@ class _RFPHelper { resizeObserver.observe(browser.parentElement); break; } + case "nativethemechange": + // NOTE: "nativethemechange" seems to always be sent after + // "windowlwthemeupdate". So all the lwtheme CSS properties should be + // set to the new theme's values already, so we don't need to wait for + // windowlwthemeupdate. + this._updateLetterboxingColors(aMessage.currentTarget, true); + break; default: break; } @@ -245,9 +268,13 @@ class _RFPHelper { Services.prefs.clearUserPref(kPrefLetterboxingDidForceSize); // fall-through case kPrefLetterboxingVcenter: - case kPrefLetterboxingGradient: this._handleLetterboxingPrefChanged(); break; + case kPrefVerticalTabs: + if (this.letterboxingEnabled) { + forEachWindow(win => this._updateLetterboxingColors(win)); + } + break; default: break; } @@ -452,7 +479,7 @@ class _RFPHelper { // If not already cached on the document object, traverse the CSSOM and // find the rule applying the default letterboxing styles to browsers // preemptively in order to beat race conditions on tab/window creation - return (document._letterboxingMarginsRule ||= (() => { + return (document._letterboxingDefaultRule ||= (() => { const LETTERBOX_CSS_SELECTOR = ".letterboxing"; const LETTERBOX_CSS_URL = "chrome://global/content/resistfingerprinting/letterboxing.css"; @@ -688,26 +715,22 @@ class _RFPHelper { if (lastRoundedSize) { // Check whether the letterboxing margin is less than the border radius, - // and if so flatten the borders. - let borderRadius = parseInt( - win - .getComputedStyle(browserContainer) - .getPropertyValue("--letterboxing-border-radius") + // and if so do not show an outline. + const gapVertical = parentHeight - lastRoundedSize.height; + const gapHorizontal = parentWidth - lastRoundedSize.width; + browserParent.classList.toggle( + "letterboxing-show-outline", + gapVertical >= this._letterboxingBorderRadius || + gapHorizontal >= this._letterboxingBorderRadius + ); + // When the Letterboxing area is top-aligned, only show the sidebar corner + // if there is enough horizontal space. + // The factor of 4 is from the horizontal centre-alignment and wanting + // enough space for twice the corner radius. + browserParent.classList.toggle( + "letterboxing-show-sidebar-corner", + gapHorizontal >= 4 * this._letterboxingBorderRadius ); - if ( - borderRadius && - parentWidth - lastRoundedSize.width < borderRadius && - parentHeight - lastRoundedSize.height < borderRadius - ) { - borderRadius = 0; - } else { - borderRadius = ""; - } - styleChanges.queueIfNeeded(browserParent, { - "--letterboxing-decorator-visibility": - borderRadius === 0 ? "hidden" : "", - "--letterboxing-border-radius": borderRadius, - }); if (win.gBrowser.selectedBrowser == aBrowser) { const updateStatus = async args => { win.XULBrowserWindow.letterboxingStatus = args @@ -769,20 +792,31 @@ class _RFPHelper { _resetContentSize(aBrowser) { aBrowser.parentElement.classList.add("exclude-letterboxing"); + aBrowser.parentElement.classList.remove( + "letterboxing-show-outline", + "letterboxing-show-sidebar-corner" + ); } _updateSizeForTabsInWindow(aWindow) { let tabBrowser = aWindow.gBrowser; - tabBrowser.tabpanels?.classList.add("letterboxing"); - tabBrowser.tabpanels?.classList.toggle( + tabBrowser.tabbox.classList.add("letterboxing"); + tabBrowser.tabbox.classList.toggle( "letterboxing-vcenter", Services.prefs.getBoolPref(kPrefLetterboxingVcenter, false) ); - tabBrowser.tabpanels?.classList.toggle( - "letterboxing-gradient", - Services.prefs.getBoolPref(kPrefLetterboxingGradient, false) - ); + if (this._letterboxingBorderRadius === undefined && tabBrowser.tabbox) { + // Cache the value since it is not expected to change in a session for any + // window. + this._letterboxingBorderRadius = Math.ceil( + parseFloat( + aWindow + .getComputedStyle(tabBrowser.tabbox) + .getPropertyValue("--letterboxing-border-radius") + ) + ); + } for (let tab of tabBrowser.tabs) { let browser = tab.linkedBrowser; @@ -791,7 +825,7 @@ class _RFPHelper { // We need to add this class late because otherwise new windows get // maximized. aWindow.setTimeout(() => { - tabBrowser.tabpanels?.classList.add("letterboxing-ready"); + tabBrowser.tabbox.classList.add("letterboxing-ready"); if (!aWindow._rfpOriginalSize) { this._recordWindowSize(aWindow); } @@ -869,6 +903,247 @@ class _RFPHelper { this._resizeObservers.set(aWindow, resizeObserver); // Rounding the content viewport. this._updateSizeForTabsInWindow(aWindow); + + this._updateLetterboxingColors(aWindow, true); + aWindow.addEventListener("nativethemechange", this); + } + + /** + * Convert a CSS property to its RGBA value. + * + * @param {Window} win - The window for the element. + * @param {CSSStyleDeclaration} style - The computed style for the element we + * want to grab the color from. + * @param {string} property - The name of the property we want. + * + * @returns {InspectorRGBATuple} - The RGBA color. The "r", "g", "b" fields + * are relative to the 0-255 color range. The "a" field is in the 0-1 range. + */ + _convertToRGBA(win, style, property) { + let cssColor = style.getPropertyValue(property); + if (!cssColor) { + lazy.logConsole.error(`Missing color "${property}"`); + return { r: 0, g: 0, b: 0, a: 0 }; + } + const currentColorRegex = + /(^|[^a-zA-Z0-9_-])currentColor($|[^a-zA-Z0-9_-])/g; + if (currentColorRegex.test(cssColor)) { + const currentColor = style.color; + cssColor = cssColor.replace(currentColorRegex, (_, pre, post) => { + return pre + currentColor + post; + }); + lazy.logConsole.debug( + "Replaced currentColor.", + property, + currentColor, + cssColor + ); + } + /* Can drop the document argument after bugzilla bug 1973684 (142). */ + const colorRGBA = win.InspectorUtils.colorToRGBA(cssColor, win.document); + if (!colorRGBA) { + lazy.logConsole.error( + `Failed to convert "${property}" color (${cssColor}) to RGBA` + ); + return { r: 0, g: 0, b: 0, a: 0 }; + } + return colorRGBA; + } + + /** + * Compose two colors with alpha values on top of each other. + * + * @param {InspectorRGBATuple} topRGBA - The color to place on the top. + * @param {InspectorRGBATuple} bottomRGBA - The color to place on the bottom. + * + * @returns {InspectorRGBATuple} - The composed color. + */ + _composeRGBA(topRGBA, bottomRGBA) { + const topA = Math.max(0, Math.min(1, topRGBA.a)); + const bottomA = Math.max(0, Math.min(1, bottomRGBA.a)); + const a = topA + bottomA - topA * bottomA; // Should be 1 if either is 1. + if (a === 0) { + return { r: 0, g: 0, b: 0, a }; + } + const ret = { a }; + for (const field of ["r", "g", "b"]) { + ret[field] = + (topRGBA[field] * topA + bottomRGBA[field] * bottomA * (1 - topA)) / a; + } + return ret; + } + + /** + * Calculate the urlbar's container opaque background color, removing any + * transparency. + * + * @param {Window} win - The window to calculate the color for. + * @param {CSSStyleDeclaration} style - The computed style for the #nav-bar + * element. + * + * @returns {InspectorRGBATuple} - The calculated color, which will be opaque. + */ + _calculateUrlbarContainerColor(win, style) { + let colorRGBA; + if (!Services.prefs.getBoolPref(kPrefVerticalTabs)) { + lazy.logConsole.debug("Toolbar background used."); + colorRGBA = this._convertToRGBA(win, style, "--toolbar-bgcolor"); + if (colorRGBA.a === 1) { + return colorRGBA; + } + } else { + // The urlbar only has the toolbox colour. + colorRGBA = { r: 0, g: 0, b: 0, a: 0 }; + } + let toolboxHasBackgroundImage = false; + const isLwTheme = win.document.documentElement.hasAttribute("lwtheme"); + if (isLwTheme) { + for (const prop of ["--lwt-header-image", "--lwt-additional-images"]) { + const headerImage = style.getPropertyValue(prop); + if (headerImage && headerImage !== "none") { + // The theme sets a background image behind the urlbar. No easy way to + // derive a single colour from this. + toolboxHasBackgroundImage = true; + lazy.logConsole.debug( + "Toolbox has background image.", + prop, + headerImage + ); + break; + } + } + } + if (!toolboxHasBackgroundImage) { + lazy.logConsole.debug("Toolbox background used."); + colorRGBA = this._composeRGBA( + colorRGBA, + this._convertToRGBA(win, style, "--toolbox-bgcolor") + ); + if (colorRGBA.a === 1) { + return colorRGBA; + } + } + + // Determine whether the urlbar is dark. + // At this point, the urlbar background has some transparency, likely on top + // of an image. + // We use the theme's text colour to figure out whether the urlbar + // background is overall meant to be light or dark. Unlike the urlbar, we + // expect this colour to be (almost) opaque. + const textRGBA = this._convertToRGBA(win, style, "--toolbar-field-color"); + const textColor = new lazy.Color(textRGBA.r, textRGBA.g, textRGBA.b); + if (textColor.relativeLuminance >= 0.5) { + // Light text, so assume it has a dark background. + // Combine with a generic opaque dark colour. Copied from "frame" for the + // built-in dark theme. + lazy.logConsole.debug("Generic dark background used."); + const darkFrameRGBA = { r: 28, g: 27, b: 34, a: 1 }; + return this._composeRGBA(colorRGBA, darkFrameRGBA); + } + // Combine with an opaque light colour. Copied from "frame" for the built-in + // light theme. + lazy.logConsole.debug("Generic light background used."); + const lightFrameRGBA = { r: 234, g: 234, b: 237, a: 1 }; + return this._composeRGBA(colorRGBA, lightFrameRGBA); + } + + /** + * Update the Letterboxing colors and related classes, or clear them if + * Letterboxing is not enabled. + * + * @param {Window} win - The window to update the colors for. + * @param {boolean} letterboxingEnabled - Whether Letterboxing is enabled. + */ + _updateLetterboxingColors(win, letterboxingEnabled) { + let urlbarBackgroundRGBA; + let urlbarTextRGBA; + let contentSeparatorRGBA; + let urlbarBackgroundDark = false; + let lowBackgroundOutlineContrast = false; + + if (letterboxingEnabled) { + // Want the effective colour of various elements without any alpha values + // so they can be used consistently. + const navbarStyle = win.getComputedStyle( + win.document.getElementById("nav-bar") + ); + const containerRGBA = this._calculateUrlbarContainerColor( + win, + navbarStyle + ); + urlbarBackgroundRGBA = this._composeRGBA( + this._convertToRGBA( + win, + navbarStyle, + "--toolbar-field-background-color" + ), + containerRGBA + ); + urlbarTextRGBA = this._composeRGBA( + this._convertToRGBA(win, navbarStyle, "--toolbar-field-color"), + urlbarBackgroundRGBA + ); + /* Separator between the urlbar container #nav-bar and the tabbox. */ + const tabboxStyle = win.getComputedStyle(win.gBrowser.tabbox); + contentSeparatorRGBA = this._composeRGBA( + this._convertToRGBA( + win, + tabboxStyle, + "--chrome-content-separator-color" + ), + containerRGBA + ); + const bgColor = new lazy.Color( + urlbarBackgroundRGBA.r, + urlbarBackgroundRGBA.g, + urlbarBackgroundRGBA.b + ); + const outlineColor = new lazy.Color( + contentSeparatorRGBA.r, + contentSeparatorRGBA.g, + contentSeparatorRGBA.b + ); + const contrastRatio = bgColor.contrastRatio(outlineColor); + lazy.logConsole.debug( + "Outline-background contrast ratio.", + contrastRatio + ); + urlbarBackgroundDark = bgColor.relativeLuminance < 0.5; + /* Very low contrast ratio. For reference the default light theme has + * a contrast ratio of ~1.1. */ + lowBackgroundOutlineContrast = contrastRatio < 1.05; + } + for (const { name, colorRGBA } of [ + { + name: "--letterboxing-urlbar-text-color", + colorRGBA: urlbarTextRGBA, + }, + { + name: "--letterboxing-urlbar-background-color", + colorRGBA: urlbarBackgroundRGBA, + }, + { + name: "--letterboxing-content-separator-color", + colorRGBA: contentSeparatorRGBA, + }, + ]) { + if (letterboxingEnabled) { + win.gBrowser.tabbox.style.setProperty( + name, + `rgb(${colorRGBA.r}, ${colorRGBA.g}, ${colorRGBA.b})` + ); + } else { + win.gBrowser.tabbox.style.removeProperty(name); + } + } + win.gBrowser.tabbox.classList.toggle( + "letterboxing-urlbar-background-dark", + urlbarBackgroundDark + ); + win.gBrowser.tabbox.classList.toggle( + "letterboxing-low-background-outline-contrast", + lowBackgroundOutlineContrast + ); } _detachWindow(aWindow) { @@ -886,7 +1161,7 @@ class _RFPHelper { aWindow.removeEventListener("TabOpen", this); // revert tabpanel's style to default - tabBrowser.tabpanels?.classList.remove("letterboxing"); + tabBrowser.tabbox.classList.remove("letterboxing"); // and restore default size on each browser element for (let tab of tabBrowser.tabs) { @@ -896,6 +1171,9 @@ class _RFPHelper { aWindow.removeEventListener("dblclick", this._onWindowDoubleClick); delete aWindow.shrinkToLetterbox; aWindow.removeEventListener("sizemodechange", windowResizeHandler); + + aWindow.removeEventListener("nativethemechange", this); + this._updateLetterboxingColors(aWindow, false); } _handleDOMWindowOpened(win) { ===================================== toolkit/components/resistfingerprinting/content/letterboxing.css ===================================== @@ -7,30 +7,106 @@ * RFPHelper.sys.mjs (LETTERBOX_CSS_SELECTOR and LETTERBOX_CSS_URL, * respectively), where --letterboxing-width & --letterboxing-height are * actually set. + * Keep this block first and separate to the rules that do not necessarily + * require --letterboxing-width or --letterboxing-height. */ .letterboxing { - --letterboxing-bgcolor: var(--tabpanel-background-color); - --letterboxing-border-radius: 8px; - --letterboxing-border-top-radius: 0; + .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser { + width: var(--letterboxing-width) !important; + height: var(--letterboxing-height) !important; + } +} + +#tabbrowser-tabbox.letterboxing { + --letterboxing-bgcolor: var(--background-color-canvas); + /* Match the border radius used for the sidebar. */ + --letterboxing-border-radius: var(--border-radius-medium); + --letterboxing-border-radius-top: 0; --letterboxing-vertical-alignment: start; - --letterboxing-shadow-color: rgba(12, 12, 13, 0.10); - --letterboxing-gradient-color1: var(--letterboxing-bgcolor); - --letterboxing-gradient-color2: color-mix(in srgb, var(--chrome-content-separator-color) 50%, var(--letterboxing-bgcolor)); - --letterboxing-border-color: var(--letterboxing-bgcolor); - --letterboxing-decorator-visibility: visible; - - /* Re-styling for Tor Browser. */ - /* stylelint-disable declaration-block-no-duplicate-custom-properties */ - --letterboxing-bgcolor: light-dark(#F0F0F4, #52525E); - --letterboxing-gradient-color1: light-dark( - rgba(0, 219, 222, 0.02), - rgba(0, 219, 222, 0.06) - ); - --letterboxing-gradient-color2: light-dark( - rgba(252, 0, 255, 0.02), - rgba(252, 0, 255, 0.06) - ); - /* stylelint-enable declaration-block-no-duplicate-custom-properties */ + --letterboxing-shadow: none; + --letterboxing-outline-color: var(--border-color); + --letterboxing-outline-width: 1px; + + @media not ((prefers-contrast) or (forced-colors)) { + /* Match the #sidebar outline width. */ + --letterboxing-outline-width: 0.5px; + --letterboxing-shadow-color: rgba(58, 57, 68, 0.20); + --letterboxing-shadow: 0 2px 14px 0 var(--letterboxing-shadow-color); + + /* Match the effective urlbar background colour. */ + --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color); + /* Match the effective colour of the separator between the urlbar container + * and the content. */ + --letterboxing-outline-color: var(--letterboxing-content-separator-color); + + &.letterboxing-urlbar-background-dark { + --letterboxing-shadow-color: #15141a; + } + + &.letterboxing-low-background-outline-contrast { + /* The default content separator colour has insufficient contrast. */ + --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, black); + + &.letterboxing-urlbar-background-dark { + /* Lighten the colour. */ + --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, white); + } + } + } + + @media (prefers-contrast) and (not (forced-colors)) { + :root[lwtheme] & { + /* User with prefers-contrast coming from the system settings, but also an + * installed theme. */ + --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color); + /* Presumably a user with prefers-contrast and a custom theme has chosen + * a theme where the contrast between the urlbar and the text is + * sufficiently high or low. */ + --letterboxing-outline-color: var(--letterboxing-urlbar-text-color); + } + } + + background: var(--letterboxing-bgcolor); + + &:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline) { + /* Letterboxing outline is visible for the current tab. Replace the usual + * outline to match the Letterboxing outline. For most scenarios, this + * should be mostly the same colour as when Letterboxing is not visible. But + * it may make a difference for some theme combinations. */ + outline-color: var(--letterboxing-outline-color); + outline-width: var(--letterboxing-outline-width); + } + + #tabbrowser-tabpanels { + /* Override the --tabpanel-background-color. + * Also, make sure this remains transparent, otherwise it will overlap the + * parent's corner's border-radius due to it's "position: relative" rule. */ + /* TODO: FIX this for newtab pages. tor-browser#44085 */ + background: transparent; + } + + /* stylelint-disable-next-line media-query-no-invalid */ + @media -moz-pref("sidebar.revamp") { + :root:not([inDOMFullscreen]) &[sidebar-shown]:not(.letterboxing-nav-toolbox-hidden):is( + /* When the Letterboxing area is aligned to the top, show the rounded + * corner if there is enough vertical space between the sidebar and the + * browser element, which is not rounded at the top. */ + :not(.letterboxing-vcenter):has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-sidebar-corner), + /* When the Letterboxing area is aligned to the centre, show the rounded + * corner if the Letterboxing border is shown. */ + .letterboxing-vcenter:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline) + ) { + /* stylelint-disable-next-line media-query-no-invalid */ + @media -moz-pref("sidebar.position_start") { + border-start-start-radius: var(--letterboxing-border-radius); + } + + /* stylelint-disable-next-line media-query-no-invalid */ + @media not -moz-pref("sidebar.position_start") { + border-start-end-radius: var(--letterboxing-border-radius); + } + } + } .browserContainer { /* @@ -39,101 +115,71 @@ * doesn't get notified on horizontal shrinking. */ overflow: hidden; - background: var(--letterboxing-bgcolor); } - .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser { - box-shadow: 0 4px 8px 0 var(--letterboxing-shadow-color); - border-radius: var(--letterboxing-border-radius); - border-top-left-radius: var(--letterboxing-border-top-radius); - border-top-right-radius: var(--letterboxing-border-top-radius); - width: var(--letterboxing-width) !important; - height: var(--letterboxing-height) !important; - background: var(--letterboxing-gradient-color2); + &.letterboxing-vcenter { + --letterboxing-border-radius-top: var(--letterboxing-border-radius); + --letterboxing-vertical-alignment: center; } } -:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) { - place-content: start center; -} - -.browserDecorator { - display: none; - pointer-events: none; - background: transparent; - position: relative; - z-index: 1; -} +.browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) { + :root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready & { + place-content: var(--letterboxing-vertical-alignment) center; + } -.letterboxing.letterboxing-vcenter .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) { - --letterboxing-border-top-radius: var(--letterboxing-border-radius); - --letterboxing-vertical-alignment: center; -} + :root:not([inDOMFullscreen]) .letterboxing &.letterboxing-show-outline { + browser { + /* We use clip-path rather than border-radius because border-radius on its + * own leads to rendering artefacts in the corners (tested with GNOME). + * See tor-browser#44214 (comment 3262962). */ + /* TODO: Use border-radius once bugzilla bug 1991874 is resolved. */ + clip-path: rect(auto auto auto auto round var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius)); + } -.letterboxing.letterboxing-gradient .browserContainer { - background: linear-gradient(283deg, var(--letterboxing-gradient-color1) 0%, var(--letterboxing-gradient-color2) 100%), var(--letterboxing-bgcolor); -} + .browserDecorator { + /* Need a separate browserDecorator element because the clip-path on the + * browser would exclude the outline and box-shadow. */ + /* TODO: Move these rules to the browser element once bugzilla bug 1991874 + * is resolved, and drop browserDecorator. */ + display: block; + border-radius: var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius); + /* NOTE: The top outline will not be visible when this is aligned to the + * top. */ + outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color); + box-shadow: var(--letterboxing-shadow); + } -:root:not([inDOMFullscreen]) .letterboxing .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) - > .browserDecorator { - display: initial; - visibility: var(--letterboxing-decorator-visibility); - border-radius: var(--letterboxing-border-radius); - border-top-left-radius: var(--letterboxing-border-top-radius); - border-top-right-radius: var(--letterboxing-border-top-radius); - box-shadow: var(--letterboxing-border-color) 0 0 .1px inset, var(--letterboxing-border-color) 0 0 .1px; - border: .1px solid var(--letterboxing-border-color); - outline: .1px solid var(--letterboxing-bgcolor); - height: calc(var(--letterboxing-height) + 1px); - top: -1px; -} + #statuspanel:not([mirror]) #statuspanel-label { + border-end-start-radius: var(--letterboxing-border-radius); + } -.letterboxing-vcenter .browserDecorator { - height: auto !important; - top: 0 !important; -} + #statuspanel[mirror] #statuspanel-label { + border-end-end-radius: var(--letterboxing-border-radius); + } + } -/* - Align status bar with content. - TODO: switch to nested CSS selectors for conciseness when available (Firefox >= 117) -*/ -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel:not([hidden]) { - position: relative; - place-self: end left; - left: 0; - right: 0; - z-index: 2; - --letterboxing-status-left-radius: var(--letterboxing-border-radius); - --letterboxing-status-right-radius: 0; -} -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel:not([mirror]):-moz-locale-dir(rtl), -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel[mirror]:-moz-locale-dir(ltr) { - left: 0; - right: 0; - --letterboxing-status-right-radius: var(--letterboxing-border-radius); - --letterboxing-status-left-radius: 0; - justify-self: right; -} + #statuspanel { + position: relative; + place-self: end start; + z-index: 2; -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) -#statuspanel-label { - border-radius: 0 0 var(--letterboxing-status-right-radius) var(--letterboxing-status-left-radius); - margin: 0; - border: 1px solid var(--letterboxing-border-color); - max-width: calc(var(--letterboxing-width) * .5); -} + &[mirror] { + justify-self: end; + } + } -browser:fullscreen { - --letterboxing-border-top-radius: 0; - --letterboxing-border-radius: 0; + #statuspanel-label { + margin: 0; + outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color); + max-width: calc(var(--letterboxing-width) * .5); + } } -:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) { - place-content: var(--letterboxing-vertical-alignment) center; +.browserDecorator { + display: none; + pointer-events: none; + background: transparent; + position: relative; + z-index: 1; } View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/be15e4… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/be15e4… 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.14.0esr-14.5-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch mullvad-browser-128.14.0esr-14.5-1 at The Tor Project / Applications / Mullvad Browser Commits: accaaaf6 by hackademix at 2025-10-15T15:57:20+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -259,7 +260,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -272,7 +272,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -339,16 +341,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -373,7 +378,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -381,6 +386,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -453,7 +473,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/acc… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/acc… 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.14.0esr-14.5-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch base-browser-128.14.0esr-14.5-1 at The Tor Project / Applications / Tor Browser Commits: 14084797 by hackademix at 2025-10-15T15:57:13+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -247,7 +248,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -260,7 +260,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -327,16 +329,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -361,7 +366,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -369,6 +374,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -441,7 +461,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1408479… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1408479… 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.14.0esr-14.5-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch tor-browser-128.14.0esr-14.5-1 at The Tor Project / Applications / Tor Browser Commits: 19fc83ce by hackademix at 2025-10-15T15:57:05+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -247,7 +248,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -260,7 +260,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -327,16 +329,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -361,7 +366,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -369,6 +374,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -441,7 +461,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/19fc83c… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/19fc83c… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.4.0esr-15.0-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch mullvad-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: 42b098c7 by hackademix at 2025-10-15T15:56:42+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -259,7 +260,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -272,7 +272,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -339,16 +341,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -373,7 +378,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -381,6 +386,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -460,7 +480,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/42b… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/42b… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-140.4.0esr-15.0-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch base-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 29c65ad8 by hackademix at 2025-10-15T15:56:35+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -247,7 +248,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -260,7 +260,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -327,16 +329,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -361,7 +366,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -369,6 +374,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -448,7 +468,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/29c65ad… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/29c65ad… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.4.0esr-15.0-1] 2 commits: BB 43869: Hide pens with RFP.
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch mullvad-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: 4517a0aa by Pier Angelo Vendrame at 2025-10-15T13:56:10+00:00 BB 43869: Hide pens with RFP. - - - - - 1a3fdcf6 by Pier Angelo Vendrame at 2025-10-15T13:56:10+00:00 fixup! Firefox preference overrides. BB 43869: Remove prefs for touch. RFP now overrides them. - - - - - 11 changed files: - browser/app/profile/001-base-profile.js - dom/base/Element.cpp - dom/events/PointerEvent.cpp - dom/events/PointerEvent.h - dom/events/PointerEventHandler.cpp - dom/events/TouchEvent.cpp - dom/webidl/PointerEvent.webidl - layout/base/PositionedEventTargeting.cpp - toolkit/components/resistfingerprinting/RFPTargets.inc - toolkit/components/resistfingerprinting/nsRFPService.cpp - widget/WidgetEventImpl.cpp Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -497,12 +497,6 @@ pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API // randomized IDs when this pref is true). // Defense-in-depth (already the default value) from Firefox 119 or 120. pref("media.devices.enumerate.legacy.enabled", false); -// Touch events (tor-browser#10286, tor-browser#42069, tor-browser#44062) -#if defined(XP_WIN) || defined(ANDROID) -pref("dom.w3c_touch_events.enabled", 1); -#else -pref("dom.w3c_touch_events.enabled", 0); -#endif #ifndef ANDROID // Bug 42138: Disable touch-based overscroll UX pref("apz.overscroll.enabled", false); ===================================== dom/base/Element.cpp ===================================== @@ -302,11 +302,6 @@ nsDOMAttributeMap* Element::Attributes() { } void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } const PointerInfo* pointerInfo = PointerEventHandler::GetPointerInfo(aPointerId); if (!pointerInfo) { @@ -334,11 +329,6 @@ void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { } void Element::ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } if (!PointerEventHandler::GetPointerInfo(aPointerId)) { aError.ThrowNotFoundError("Invalid pointer id"); return; ===================================== dom/events/PointerEvent.cpp ===================================== @@ -224,39 +224,78 @@ NS_INTERFACE_MAP_END_INHERITING(MouseEvent) NS_IMPL_ADDREF_INHERITED(PointerEvent, MouseEvent) NS_IMPL_RELEASE_INHERITED(PointerEvent, MouseEvent) -void PointerEvent::GetPointerType(nsAString& aPointerType) { +uint16_t PointerEvent::ResistantInputSource(CallerType aCallerType) const { + const uint16_t inputSource = mEvent->AsPointerEvent()->mInputSource; + if (!ShouldResistFingerprinting(aCallerType)) { + return inputSource; + } + + MOZ_ASSERT(IsTrusted()); + + // Bug 1953665: Pen events are inconsistent between platforms. + // They might emit touch events on Windows and Android, but only mouse events + // in other platforms. In particular, touch is always disabled on macOS. +#if defined(XP_WIN) + if (inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH || + inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE) { + return inputSource; + } + // Similar to nsWindow::DispatchTouchEventFromWMPointer. + switch (mEvent->mMessage) { + case ePointerMove: + return mEvent->AsPointerEvent()->mPressure == 0 + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE // hover + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; + case ePointerUp: + case ePointerDown: + case ePointerCancel: + return MouseEvent_Binding::MOZ_SOURCE_TOUCH; + default: + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; + } +#elif defined(MOZ_WIDGET_ANDROID) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; +#elif defined(MOZ_WIDGET_GTK) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH + ? MouseEvent_Binding::MOZ_SOURCE_TOUCH + : MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#elif defined(MOZ_WIDGET_COCOA) + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#else + return inputSource; +#endif +} + +void PointerEvent::GetPointerType(nsAString& aPointerType, + CallerType aCallerType) { if (mPointerType.isSome()) { aPointerType = mPointerType.value(); return; } - -#if SPOOFED_MAX_TOUCH_POINTS <= 0 - if (ShouldResistFingerprinting()) { - aPointerType.AssignLiteral("mouse"); - return; - } -#endif - - ConvertPointerTypeToString(mEvent->AsPointerEvent()->mInputSource, - aPointerType); + ConvertPointerTypeToString(ResistantInputSource(aCallerType), aPointerType); } int32_t PointerEvent::PointerId() { - return (ShouldResistFingerprinting(true)) - ? PointerEventHandler::GetSpoofedPointerIdForRFP() - : mEvent->AsPointerEvent()->pointerId; + return mEvent->AsPointerEvent()->pointerId; } -double PointerEvent::Width() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mWidth; +double PointerEvent::Width(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mWidth; } -double PointerEvent::Height() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mHeight; +double PointerEvent::Height(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mHeight; } -float PointerEvent::Pressure() { - if (mEvent->mMessage == ePointerUp || !ShouldResistFingerprinting()) { +float PointerEvent::Pressure(CallerType aCallerType) { + if (mEvent->mMessage == ePointerUp || + !ShouldResistFingerprinting(aCallerType)) { return mEvent->AsPointerEvent()->mPressure; } @@ -273,14 +312,14 @@ float PointerEvent::Pressure() { return spoofedPressure; } -float PointerEvent::TangentialPressure() { - return ShouldResistFingerprinting() +float PointerEvent::TangentialPressure(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) ? 0 : mEvent->AsPointerEvent()->tangentialPressure; } -int32_t PointerEvent::TiltX() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltX(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltX.isSome()) { @@ -291,8 +330,8 @@ int32_t PointerEvent::TiltX() { return *mTiltX; } -int32_t PointerEvent::TiltY() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltY(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltY.isSome()) { @@ -303,12 +342,14 @@ int32_t PointerEvent::TiltY() { return *mTiltY; } -int32_t PointerEvent::Twist() { - return ShouldResistFingerprinting() ? 0 : mEvent->AsPointerEvent()->twist; +int32_t PointerEvent::Twist(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) + ? 0 + : mEvent->AsPointerEvent()->twist; } -double PointerEvent::AltitudeAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AltitudeAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAltitudeAngle(); } if (mAltitudeAngle.isSome()) { @@ -319,8 +360,8 @@ double PointerEvent::AltitudeAngle() { return *mAltitudeAngle; } -double PointerEvent::AzimuthAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AzimuthAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAzimuthAngle(); } if (mAzimuthAngle.isSome()) { @@ -421,22 +462,22 @@ void PointerEvent::GetPredictedEvents( aPointerEvents.AppendElements(mPredictedEvents); } -bool PointerEvent::ShouldResistFingerprinting(bool aForPointerId) const { - // There are three simple situations we don't need to spoof this pointer +bool PointerEvent::ShouldResistFingerprinting(CallerType aCallerType) const { + // There are a few simple situations we don't need to spoof this pointer // event. - // 1. The pref privcy.resistFingerprinting' is false, we fast return here - // since we don't need to do any QI of following codes. - // 2. This event is generated by scripts. - // 3. This event is a mouse pointer event. + // * We are being called by a System caller + // * The pref privcy.resistFingerprinting' is false, we fast return here + // since we don't need to do any QI of following codes. + // * This event is generated by scripts. + // * This event is a mouse pointer event. // We don't need to check for the system group since pointer events won't be // dispatched to the system group. - RFPTarget target = - aForPointerId ? RFPTarget::PointerId : RFPTarget::PointerEvents; - if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || + RFPTarget target = RFPTarget::PointerEvents; + if (aCallerType == CallerType::System || + !nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || !mEvent->IsTrusted() || - (mEvent->AsPointerEvent()->mInputSource == - MouseEvent_Binding::MOZ_SOURCE_MOUSE && - SPOOFED_MAX_TOUCH_POINTS == 0)) { + mEvent->AsPointerEvent()->mInputSource == + MouseEvent_Binding::MOZ_SOURCE_MOUSE) { return false; } ===================================== dom/events/PointerEvent.h ===================================== @@ -41,17 +41,19 @@ class PointerEvent : public MouseEvent { PointerEvent* AsPointerEvent() final { return this; } int32_t PointerId(); - double Width() const; - double Height() const; - float Pressure(); - float TangentialPressure(); - int32_t TiltX(); - int32_t TiltY(); - int32_t Twist(); - double AltitudeAngle(); - double AzimuthAngle(); + double Width(CallerType aCallerType = CallerType::System) const; + double Height(CallerType aCallerType = CallerType::System) const; + float Pressure(CallerType aCallerType = CallerType::System); + float TangentialPressure(CallerType aCallerType = CallerType::System); + int32_t TiltX(CallerType aCallerType = CallerType::System); + int32_t TiltY(CallerType aCallerType = CallerType::System); + int32_t Twist(CallerType aCallerType = CallerType::System); + double AltitudeAngle(CallerType aCallerType = CallerType::System); + double AzimuthAngle(CallerType aCallerType = CallerType::System); bool IsPrimary(); - void GetPointerType(nsAString& aPointerType); + void GetPointerType( + nsAString& aPointerType, + mozilla::dom::CallerType aCallerType = CallerType::System); static bool EnableGetCoalescedEvents(JSContext* aCx, JSObject* aGlobal); void GetCoalescedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); void GetPredictedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); @@ -62,7 +64,11 @@ class PointerEvent : public MouseEvent { private: // This method returns the boolean to indicate whether spoofing pointer // event for fingerprinting resistance. - bool ShouldResistFingerprinting(bool aForPointerId = false) const; + bool ShouldResistFingerprinting( + CallerType aCallerType = CallerType::System) const; + + uint16_t ResistantInputSource( + CallerType aCallerType = CallerType::System) const; // When the instance is a trusted `pointermove` event but the widget event // does not have proper coalesced events (typically, the event is synthesized ===================================== dom/events/PointerEventHandler.cpp ===================================== @@ -421,32 +421,6 @@ void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) { PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId); - // When fingerprinting resistance is enabled, we need to map other pointer - // ids into the spoofed one. We don't have to do the mapping if the capture - // info exists for the non-spoofed pointer id because of we won't allow - // content to set pointer capture other than the spoofed one. Thus, it must be - // from chrome if the capture info exists in this case. And we don't have to - // do anything if the pointer id is the same as the spoofed one. - if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check", - RFPTarget::PointerId) && - aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() && - !captureInfo) { - PointerCaptureInfo* spoofedCaptureInfo = - GetPointerCaptureInfo(GetSpoofedPointerIdForRFP()); - - // We need to check the target element's document should resist - // fingerprinting. If not, we don't need to send a capture event - // since the capture info of the original pointer id doesn't exist - // in this case. - if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement || - !spoofedCaptureInfo->mPendingElement->OwnerDoc() - ->ShouldResistFingerprinting(RFPTarget::PointerEvents)) { - return; - } - - captureInfo = spoofedCaptureInfo; - } - if (!captureInfo || captureInfo->mPendingElement == captureInfo->mOverrideElement) { return; ===================================== dom/events/TouchEvent.cpp ===================================== @@ -225,38 +225,40 @@ bool TouchEvent::PrefEnabled(nsIDocShell* aDocShell) { } else if (touchEventsOverride == mozilla::dom::TouchEventsOverride::Disabled) { enabled = false; + } else if (nsContentUtils::ShouldResistFingerprinting( + aDocShell, RFPTarget::PointerEvents)) { +#ifdef MOZ_WIDGET_COCOA + enabled = false; +#else + enabled = true; +#endif } else { const int32_t prefValue = StaticPrefs::dom_w3c_touch_events_enabled(); if (prefValue == 2) { - if (nsContentUtils::ShouldResistFingerprinting( - aDocShell, RFPTarget::PointerEvents)) { - enabled = SPOOFED_MAX_TOUCH_POINTS != 0; - } else { - enabled = PlatformSupportsTouch(); - - static bool firstTime = true; - // The touch screen data seems to be inaccurate in the parent process, - // and we really need the crash annotation in child processes. - if (firstTime && !XRE_IsParentProcess()) { - CrashReporter::RecordAnnotationBool( - CrashReporter::Annotation::HasDeviceTouchScreen, enabled); - firstTime = false; - } + enabled = PlatformSupportsTouch(); + + static bool firstTime = true; + // The touch screen data seems to be inaccurate in the parent process, + // and we really need the crash annotation in child processes. + if (firstTime && !XRE_IsParentProcess()) { + CrashReporter::RecordAnnotationBool( + CrashReporter::Annotation::HasDeviceTouchScreen, enabled); + firstTime = false; + } #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) - if (enabled && aDocShell) { - // APZ might be disabled on this particular widget, in which case - // TouchEvent support will also be disabled. Try to detect that. - RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); - if (pc) { - nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); - if (widget) { - enabled &= widget->AsyncPanZoomEnabled(); - } + if (enabled && aDocShell) { + // APZ might be disabled on this particular widget, in which case + // TouchEvent support will also be disabled. Try to detect that. + RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); + if (pc) { + nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); + if (widget) { + enabled &= widget->AsyncPanZoomEnabled(); } } -#endif } +#endif } else { enabled = !!prefValue; } ===================================== dom/webidl/PointerEvent.webidl ===================================== @@ -14,16 +14,26 @@ interface PointerEvent : MouseEvent readonly attribute long pointerId; + [NeedsCallerType] readonly attribute double width; + [NeedsCallerType] readonly attribute double height; + [NeedsCallerType] readonly attribute float pressure; + [NeedsCallerType] readonly attribute float tangentialPressure; + [NeedsCallerType] readonly attribute long tiltX; + [NeedsCallerType] readonly attribute long tiltY; + [NeedsCallerType] readonly attribute long twist; + [NeedsCallerType] readonly attribute double altitudeAngle; + [NeedsCallerType] readonly attribute double azimuthAngle; + [NeedsCallerType] readonly attribute DOMString pointerType; readonly attribute boolean isPrimary; ===================================== layout/base/PositionedEventTargeting.cpp ===================================== @@ -16,6 +16,7 @@ #include "mozilla/ToString.h" #include "mozilla/ViewportUtils.h" #include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/dom/TouchEvent.h" #include "mozilla/gfx/Matrix.h" #include "mozilla/layers/LayersTypes.h" #include "nsContainerFrame.h" @@ -173,9 +174,7 @@ static bool HasTouchListener(const nsIContent* aContent) { return false; } - // FIXME: Should this really use the pref rather than TouchEvent::PrefEnabled - // or such? - if (!StaticPrefs::dom_w3c_touch_events_enabled()) { + if (!TouchEvent::PrefEnabled(aContent->OwnerDoc()->GetDocShell())) { return false; } ===================================== toolkit/components/resistfingerprinting/RFPTargets.inc ===================================== @@ -34,7 +34,7 @@ ITEM_VALUE(NavigatorHWConcurrency, 16) ITEM_VALUE(NavigatorOscpu, 17) ITEM_VALUE(NavigatorPlatform, 18) ITEM_VALUE(NavigatorUserAgent, 19) -ITEM_VALUE(PointerId, 20) +// We no longer use PointerId, it can renamed and reused ITEM_VALUE(StreamVideoFacingMode, 21) ITEM_VALUE(JSDateTimeUTC, 22) ITEM_VALUE(JSMathFdlibm, 23) @@ -104,6 +104,7 @@ ITEM_VALUE(DiskStorageLimit, 70) ITEM_VALUE(WebCodecs, 71) ITEM_VALUE(NavigatorHWConcurrencyTiered,74) +// !!! Adding a new target? Rename PointerId and repurpose it. // !!! Don't forget to update kDefaultFingerprintingProtections in nsRFPService.cpp // if necessary. ===================================== toolkit/components/resistfingerprinting/nsRFPService.cpp ===================================== @@ -303,13 +303,6 @@ Maybe<bool> nsRFPService::HandleExeptionalRFPTargets( StaticPrefs::privacy_spoof_english_DoNotUseDirectly() == 2); } - // We don't spoof the pointerId on multi-touch devices. -#if SPOOFED_MAX_TOUCH_POINTS > 0 - if (aTarget == RFPTarget::PointerId) { - return Some(false); - } -#endif - return Nothing(); } ===================================== widget/WidgetEventImpl.cpp ===================================== @@ -589,23 +589,6 @@ bool WidgetEvent::IsBlockedForFingerprintingResistance() const { keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Control || keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_AltGraph); } - case ePointerEventClass: { - if (IsPointerEventMessageOriginallyMouseEventMessage(mMessage)) { - return false; - } - - if (SPOOFED_MAX_TOUCH_POINTS > 0) { - return false; - } - - const WidgetPointerEvent* pointerEvent = AsPointerEvent(); - - // We suppress the pointer events if it is not primary for fingerprinting - // resistance. It is because of that we want to spoof any pointer event - // into a mouse pointer event and the mouse pointer event only has - // isPrimary as true. - return !pointerEvent->mIsPrimary; - } default: return false; } View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/fb… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/fb… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-140.4.0esr-15.0-1] 2 commits: BB 43869: Hide pens with RFP.
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch base-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 822f61c9 by Pier Angelo Vendrame at 2025-10-15T13:54:52+00:00 BB 43869: Hide pens with RFP. - - - - - 0ed3602b by Pier Angelo Vendrame at 2025-10-15T13:54:54+00:00 fixup! Firefox preference overrides. BB 43869: Remove prefs for touch. RFP now overrides them. - - - - - 11 changed files: - browser/app/profile/001-base-profile.js - dom/base/Element.cpp - dom/events/PointerEvent.cpp - dom/events/PointerEvent.h - dom/events/PointerEventHandler.cpp - dom/events/TouchEvent.cpp - dom/webidl/PointerEvent.webidl - layout/base/PositionedEventTargeting.cpp - toolkit/components/resistfingerprinting/RFPTargets.inc - toolkit/components/resistfingerprinting/nsRFPService.cpp - widget/WidgetEventImpl.cpp Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -497,12 +497,6 @@ pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API // randomized IDs when this pref is true). // Defense-in-depth (already the default value) from Firefox 119 or 120. pref("media.devices.enumerate.legacy.enabled", false); -// Touch events (tor-browser#10286, tor-browser#42069, tor-browser#44062) -#if defined(XP_WIN) || defined(ANDROID) -pref("dom.w3c_touch_events.enabled", 1); -#else -pref("dom.w3c_touch_events.enabled", 0); -#endif #ifndef ANDROID // Bug 42138: Disable touch-based overscroll UX pref("apz.overscroll.enabled", false); ===================================== dom/base/Element.cpp ===================================== @@ -302,11 +302,6 @@ nsDOMAttributeMap* Element::Attributes() { } void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } const PointerInfo* pointerInfo = PointerEventHandler::GetPointerInfo(aPointerId); if (!pointerInfo) { @@ -334,11 +329,6 @@ void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { } void Element::ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } if (!PointerEventHandler::GetPointerInfo(aPointerId)) { aError.ThrowNotFoundError("Invalid pointer id"); return; ===================================== dom/events/PointerEvent.cpp ===================================== @@ -224,39 +224,78 @@ NS_INTERFACE_MAP_END_INHERITING(MouseEvent) NS_IMPL_ADDREF_INHERITED(PointerEvent, MouseEvent) NS_IMPL_RELEASE_INHERITED(PointerEvent, MouseEvent) -void PointerEvent::GetPointerType(nsAString& aPointerType) { +uint16_t PointerEvent::ResistantInputSource(CallerType aCallerType) const { + const uint16_t inputSource = mEvent->AsPointerEvent()->mInputSource; + if (!ShouldResistFingerprinting(aCallerType)) { + return inputSource; + } + + MOZ_ASSERT(IsTrusted()); + + // Bug 1953665: Pen events are inconsistent between platforms. + // They might emit touch events on Windows and Android, but only mouse events + // in other platforms. In particular, touch is always disabled on macOS. +#if defined(XP_WIN) + if (inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH || + inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE) { + return inputSource; + } + // Similar to nsWindow::DispatchTouchEventFromWMPointer. + switch (mEvent->mMessage) { + case ePointerMove: + return mEvent->AsPointerEvent()->mPressure == 0 + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE // hover + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; + case ePointerUp: + case ePointerDown: + case ePointerCancel: + return MouseEvent_Binding::MOZ_SOURCE_TOUCH; + default: + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; + } +#elif defined(MOZ_WIDGET_ANDROID) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; +#elif defined(MOZ_WIDGET_GTK) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH + ? MouseEvent_Binding::MOZ_SOURCE_TOUCH + : MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#elif defined(MOZ_WIDGET_COCOA) + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#else + return inputSource; +#endif +} + +void PointerEvent::GetPointerType(nsAString& aPointerType, + CallerType aCallerType) { if (mPointerType.isSome()) { aPointerType = mPointerType.value(); return; } - -#if SPOOFED_MAX_TOUCH_POINTS <= 0 - if (ShouldResistFingerprinting()) { - aPointerType.AssignLiteral("mouse"); - return; - } -#endif - - ConvertPointerTypeToString(mEvent->AsPointerEvent()->mInputSource, - aPointerType); + ConvertPointerTypeToString(ResistantInputSource(aCallerType), aPointerType); } int32_t PointerEvent::PointerId() { - return (ShouldResistFingerprinting(true)) - ? PointerEventHandler::GetSpoofedPointerIdForRFP() - : mEvent->AsPointerEvent()->pointerId; + return mEvent->AsPointerEvent()->pointerId; } -double PointerEvent::Width() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mWidth; +double PointerEvent::Width(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mWidth; } -double PointerEvent::Height() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mHeight; +double PointerEvent::Height(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mHeight; } -float PointerEvent::Pressure() { - if (mEvent->mMessage == ePointerUp || !ShouldResistFingerprinting()) { +float PointerEvent::Pressure(CallerType aCallerType) { + if (mEvent->mMessage == ePointerUp || + !ShouldResistFingerprinting(aCallerType)) { return mEvent->AsPointerEvent()->mPressure; } @@ -273,14 +312,14 @@ float PointerEvent::Pressure() { return spoofedPressure; } -float PointerEvent::TangentialPressure() { - return ShouldResistFingerprinting() +float PointerEvent::TangentialPressure(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) ? 0 : mEvent->AsPointerEvent()->tangentialPressure; } -int32_t PointerEvent::TiltX() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltX(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltX.isSome()) { @@ -291,8 +330,8 @@ int32_t PointerEvent::TiltX() { return *mTiltX; } -int32_t PointerEvent::TiltY() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltY(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltY.isSome()) { @@ -303,12 +342,14 @@ int32_t PointerEvent::TiltY() { return *mTiltY; } -int32_t PointerEvent::Twist() { - return ShouldResistFingerprinting() ? 0 : mEvent->AsPointerEvent()->twist; +int32_t PointerEvent::Twist(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) + ? 0 + : mEvent->AsPointerEvent()->twist; } -double PointerEvent::AltitudeAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AltitudeAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAltitudeAngle(); } if (mAltitudeAngle.isSome()) { @@ -319,8 +360,8 @@ double PointerEvent::AltitudeAngle() { return *mAltitudeAngle; } -double PointerEvent::AzimuthAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AzimuthAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAzimuthAngle(); } if (mAzimuthAngle.isSome()) { @@ -421,22 +462,22 @@ void PointerEvent::GetPredictedEvents( aPointerEvents.AppendElements(mPredictedEvents); } -bool PointerEvent::ShouldResistFingerprinting(bool aForPointerId) const { - // There are three simple situations we don't need to spoof this pointer +bool PointerEvent::ShouldResistFingerprinting(CallerType aCallerType) const { + // There are a few simple situations we don't need to spoof this pointer // event. - // 1. The pref privcy.resistFingerprinting' is false, we fast return here - // since we don't need to do any QI of following codes. - // 2. This event is generated by scripts. - // 3. This event is a mouse pointer event. + // * We are being called by a System caller + // * The pref privcy.resistFingerprinting' is false, we fast return here + // since we don't need to do any QI of following codes. + // * This event is generated by scripts. + // * This event is a mouse pointer event. // We don't need to check for the system group since pointer events won't be // dispatched to the system group. - RFPTarget target = - aForPointerId ? RFPTarget::PointerId : RFPTarget::PointerEvents; - if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || + RFPTarget target = RFPTarget::PointerEvents; + if (aCallerType == CallerType::System || + !nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || !mEvent->IsTrusted() || - (mEvent->AsPointerEvent()->mInputSource == - MouseEvent_Binding::MOZ_SOURCE_MOUSE && - SPOOFED_MAX_TOUCH_POINTS == 0)) { + mEvent->AsPointerEvent()->mInputSource == + MouseEvent_Binding::MOZ_SOURCE_MOUSE) { return false; } ===================================== dom/events/PointerEvent.h ===================================== @@ -41,17 +41,19 @@ class PointerEvent : public MouseEvent { PointerEvent* AsPointerEvent() final { return this; } int32_t PointerId(); - double Width() const; - double Height() const; - float Pressure(); - float TangentialPressure(); - int32_t TiltX(); - int32_t TiltY(); - int32_t Twist(); - double AltitudeAngle(); - double AzimuthAngle(); + double Width(CallerType aCallerType = CallerType::System) const; + double Height(CallerType aCallerType = CallerType::System) const; + float Pressure(CallerType aCallerType = CallerType::System); + float TangentialPressure(CallerType aCallerType = CallerType::System); + int32_t TiltX(CallerType aCallerType = CallerType::System); + int32_t TiltY(CallerType aCallerType = CallerType::System); + int32_t Twist(CallerType aCallerType = CallerType::System); + double AltitudeAngle(CallerType aCallerType = CallerType::System); + double AzimuthAngle(CallerType aCallerType = CallerType::System); bool IsPrimary(); - void GetPointerType(nsAString& aPointerType); + void GetPointerType( + nsAString& aPointerType, + mozilla::dom::CallerType aCallerType = CallerType::System); static bool EnableGetCoalescedEvents(JSContext* aCx, JSObject* aGlobal); void GetCoalescedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); void GetPredictedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); @@ -62,7 +64,11 @@ class PointerEvent : public MouseEvent { private: // This method returns the boolean to indicate whether spoofing pointer // event for fingerprinting resistance. - bool ShouldResistFingerprinting(bool aForPointerId = false) const; + bool ShouldResistFingerprinting( + CallerType aCallerType = CallerType::System) const; + + uint16_t ResistantInputSource( + CallerType aCallerType = CallerType::System) const; // When the instance is a trusted `pointermove` event but the widget event // does not have proper coalesced events (typically, the event is synthesized ===================================== dom/events/PointerEventHandler.cpp ===================================== @@ -421,32 +421,6 @@ void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) { PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId); - // When fingerprinting resistance is enabled, we need to map other pointer - // ids into the spoofed one. We don't have to do the mapping if the capture - // info exists for the non-spoofed pointer id because of we won't allow - // content to set pointer capture other than the spoofed one. Thus, it must be - // from chrome if the capture info exists in this case. And we don't have to - // do anything if the pointer id is the same as the spoofed one. - if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check", - RFPTarget::PointerId) && - aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() && - !captureInfo) { - PointerCaptureInfo* spoofedCaptureInfo = - GetPointerCaptureInfo(GetSpoofedPointerIdForRFP()); - - // We need to check the target element's document should resist - // fingerprinting. If not, we don't need to send a capture event - // since the capture info of the original pointer id doesn't exist - // in this case. - if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement || - !spoofedCaptureInfo->mPendingElement->OwnerDoc() - ->ShouldResistFingerprinting(RFPTarget::PointerEvents)) { - return; - } - - captureInfo = spoofedCaptureInfo; - } - if (!captureInfo || captureInfo->mPendingElement == captureInfo->mOverrideElement) { return; ===================================== dom/events/TouchEvent.cpp ===================================== @@ -225,38 +225,40 @@ bool TouchEvent::PrefEnabled(nsIDocShell* aDocShell) { } else if (touchEventsOverride == mozilla::dom::TouchEventsOverride::Disabled) { enabled = false; + } else if (nsContentUtils::ShouldResistFingerprinting( + aDocShell, RFPTarget::PointerEvents)) { +#ifdef MOZ_WIDGET_COCOA + enabled = false; +#else + enabled = true; +#endif } else { const int32_t prefValue = StaticPrefs::dom_w3c_touch_events_enabled(); if (prefValue == 2) { - if (nsContentUtils::ShouldResistFingerprinting( - aDocShell, RFPTarget::PointerEvents)) { - enabled = SPOOFED_MAX_TOUCH_POINTS != 0; - } else { - enabled = PlatformSupportsTouch(); - - static bool firstTime = true; - // The touch screen data seems to be inaccurate in the parent process, - // and we really need the crash annotation in child processes. - if (firstTime && !XRE_IsParentProcess()) { - CrashReporter::RecordAnnotationBool( - CrashReporter::Annotation::HasDeviceTouchScreen, enabled); - firstTime = false; - } + enabled = PlatformSupportsTouch(); + + static bool firstTime = true; + // The touch screen data seems to be inaccurate in the parent process, + // and we really need the crash annotation in child processes. + if (firstTime && !XRE_IsParentProcess()) { + CrashReporter::RecordAnnotationBool( + CrashReporter::Annotation::HasDeviceTouchScreen, enabled); + firstTime = false; + } #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) - if (enabled && aDocShell) { - // APZ might be disabled on this particular widget, in which case - // TouchEvent support will also be disabled. Try to detect that. - RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); - if (pc) { - nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); - if (widget) { - enabled &= widget->AsyncPanZoomEnabled(); - } + if (enabled && aDocShell) { + // APZ might be disabled on this particular widget, in which case + // TouchEvent support will also be disabled. Try to detect that. + RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); + if (pc) { + nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); + if (widget) { + enabled &= widget->AsyncPanZoomEnabled(); } } -#endif } +#endif } else { enabled = !!prefValue; } ===================================== dom/webidl/PointerEvent.webidl ===================================== @@ -14,16 +14,26 @@ interface PointerEvent : MouseEvent readonly attribute long pointerId; + [NeedsCallerType] readonly attribute double width; + [NeedsCallerType] readonly attribute double height; + [NeedsCallerType] readonly attribute float pressure; + [NeedsCallerType] readonly attribute float tangentialPressure; + [NeedsCallerType] readonly attribute long tiltX; + [NeedsCallerType] readonly attribute long tiltY; + [NeedsCallerType] readonly attribute long twist; + [NeedsCallerType] readonly attribute double altitudeAngle; + [NeedsCallerType] readonly attribute double azimuthAngle; + [NeedsCallerType] readonly attribute DOMString pointerType; readonly attribute boolean isPrimary; ===================================== layout/base/PositionedEventTargeting.cpp ===================================== @@ -16,6 +16,7 @@ #include "mozilla/ToString.h" #include "mozilla/ViewportUtils.h" #include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/dom/TouchEvent.h" #include "mozilla/gfx/Matrix.h" #include "mozilla/layers/LayersTypes.h" #include "nsContainerFrame.h" @@ -173,9 +174,7 @@ static bool HasTouchListener(const nsIContent* aContent) { return false; } - // FIXME: Should this really use the pref rather than TouchEvent::PrefEnabled - // or such? - if (!StaticPrefs::dom_w3c_touch_events_enabled()) { + if (!TouchEvent::PrefEnabled(aContent->OwnerDoc()->GetDocShell())) { return false; } ===================================== toolkit/components/resistfingerprinting/RFPTargets.inc ===================================== @@ -34,7 +34,7 @@ ITEM_VALUE(NavigatorHWConcurrency, 16) ITEM_VALUE(NavigatorOscpu, 17) ITEM_VALUE(NavigatorPlatform, 18) ITEM_VALUE(NavigatorUserAgent, 19) -ITEM_VALUE(PointerId, 20) +// We no longer use PointerId, it can renamed and reused ITEM_VALUE(StreamVideoFacingMode, 21) ITEM_VALUE(JSDateTimeUTC, 22) ITEM_VALUE(JSMathFdlibm, 23) @@ -104,6 +104,7 @@ ITEM_VALUE(DiskStorageLimit, 70) ITEM_VALUE(WebCodecs, 71) ITEM_VALUE(NavigatorHWConcurrencyTiered,74) +// !!! Adding a new target? Rename PointerId and repurpose it. // !!! Don't forget to update kDefaultFingerprintingProtections in nsRFPService.cpp // if necessary. ===================================== toolkit/components/resistfingerprinting/nsRFPService.cpp ===================================== @@ -303,13 +303,6 @@ Maybe<bool> nsRFPService::HandleExeptionalRFPTargets( StaticPrefs::privacy_spoof_english_DoNotUseDirectly() == 2); } - // We don't spoof the pointerId on multi-touch devices. -#if SPOOFED_MAX_TOUCH_POINTS > 0 - if (aTarget == RFPTarget::PointerId) { - return Some(false); - } -#endif - return Nothing(); } ===================================== widget/WidgetEventImpl.cpp ===================================== @@ -589,23 +589,6 @@ bool WidgetEvent::IsBlockedForFingerprintingResistance() const { keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Control || keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_AltGraph); } - case ePointerEventClass: { - if (IsPointerEventMessageOriginallyMouseEventMessage(mMessage)) { - return false; - } - - if (SPOOFED_MAX_TOUCH_POINTS > 0) { - return false; - } - - const WidgetPointerEvent* pointerEvent = AsPointerEvent(); - - // We suppress the pointer events if it is not primary for fingerprinting - // resistance. It is because of that we want to spoof any pointer event - // into a mouse pointer event and the mouse pointer event only has - // isPrimary as true. - return !pointerEvent->mIsPrimary; - } default: return false; } View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/2c531e… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/2c531e… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] 2 commits: BB 43869: Hide pens with RFP.
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 84fb1585 by Pier Angelo Vendrame at 2025-10-15T13:23:33+00:00 BB 43869: Hide pens with RFP. - - - - - be15e46e by Pier Angelo Vendrame at 2025-10-15T13:23:33+00:00 fixup! Firefox preference overrides. BB 43869: Remove prefs for touch. RFP now overrides them. - - - - - 11 changed files: - browser/app/profile/001-base-profile.js - dom/base/Element.cpp - dom/events/PointerEvent.cpp - dom/events/PointerEvent.h - dom/events/PointerEventHandler.cpp - dom/events/TouchEvent.cpp - dom/webidl/PointerEvent.webidl - layout/base/PositionedEventTargeting.cpp - toolkit/components/resistfingerprinting/RFPTargets.inc - toolkit/components/resistfingerprinting/nsRFPService.cpp - widget/WidgetEventImpl.cpp Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -497,12 +497,6 @@ pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API // randomized IDs when this pref is true). // Defense-in-depth (already the default value) from Firefox 119 or 120. pref("media.devices.enumerate.legacy.enabled", false); -// Touch events (tor-browser#10286, tor-browser#42069, tor-browser#44062) -#if defined(XP_WIN) || defined(ANDROID) -pref("dom.w3c_touch_events.enabled", 1); -#else -pref("dom.w3c_touch_events.enabled", 0); -#endif #ifndef ANDROID // Bug 42138: Disable touch-based overscroll UX pref("apz.overscroll.enabled", false); ===================================== dom/base/Element.cpp ===================================== @@ -302,11 +302,6 @@ nsDOMAttributeMap* Element::Attributes() { } void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } const PointerInfo* pointerInfo = PointerEventHandler::GetPointerInfo(aPointerId); if (!pointerInfo) { @@ -334,11 +329,6 @@ void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { } void Element::ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } if (!PointerEventHandler::GetPointerInfo(aPointerId)) { aError.ThrowNotFoundError("Invalid pointer id"); return; ===================================== dom/events/PointerEvent.cpp ===================================== @@ -224,39 +224,78 @@ NS_INTERFACE_MAP_END_INHERITING(MouseEvent) NS_IMPL_ADDREF_INHERITED(PointerEvent, MouseEvent) NS_IMPL_RELEASE_INHERITED(PointerEvent, MouseEvent) -void PointerEvent::GetPointerType(nsAString& aPointerType) { +uint16_t PointerEvent::ResistantInputSource(CallerType aCallerType) const { + const uint16_t inputSource = mEvent->AsPointerEvent()->mInputSource; + if (!ShouldResistFingerprinting(aCallerType)) { + return inputSource; + } + + MOZ_ASSERT(IsTrusted()); + + // Bug 1953665: Pen events are inconsistent between platforms. + // They might emit touch events on Windows and Android, but only mouse events + // in other platforms. In particular, touch is always disabled on macOS. +#if defined(XP_WIN) + if (inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH || + inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE) { + return inputSource; + } + // Similar to nsWindow::DispatchTouchEventFromWMPointer. + switch (mEvent->mMessage) { + case ePointerMove: + return mEvent->AsPointerEvent()->mPressure == 0 + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE // hover + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; + case ePointerUp: + case ePointerDown: + case ePointerCancel: + return MouseEvent_Binding::MOZ_SOURCE_TOUCH; + default: + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; + } +#elif defined(MOZ_WIDGET_ANDROID) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; +#elif defined(MOZ_WIDGET_GTK) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH + ? MouseEvent_Binding::MOZ_SOURCE_TOUCH + : MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#elif defined(MOZ_WIDGET_COCOA) + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#else + return inputSource; +#endif +} + +void PointerEvent::GetPointerType(nsAString& aPointerType, + CallerType aCallerType) { if (mPointerType.isSome()) { aPointerType = mPointerType.value(); return; } - -#if SPOOFED_MAX_TOUCH_POINTS <= 0 - if (ShouldResistFingerprinting()) { - aPointerType.AssignLiteral("mouse"); - return; - } -#endif - - ConvertPointerTypeToString(mEvent->AsPointerEvent()->mInputSource, - aPointerType); + ConvertPointerTypeToString(ResistantInputSource(aCallerType), aPointerType); } int32_t PointerEvent::PointerId() { - return (ShouldResistFingerprinting(true)) - ? PointerEventHandler::GetSpoofedPointerIdForRFP() - : mEvent->AsPointerEvent()->pointerId; + return mEvent->AsPointerEvent()->pointerId; } -double PointerEvent::Width() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mWidth; +double PointerEvent::Width(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mWidth; } -double PointerEvent::Height() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mHeight; +double PointerEvent::Height(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mHeight; } -float PointerEvent::Pressure() { - if (mEvent->mMessage == ePointerUp || !ShouldResistFingerprinting()) { +float PointerEvent::Pressure(CallerType aCallerType) { + if (mEvent->mMessage == ePointerUp || + !ShouldResistFingerprinting(aCallerType)) { return mEvent->AsPointerEvent()->mPressure; } @@ -273,14 +312,14 @@ float PointerEvent::Pressure() { return spoofedPressure; } -float PointerEvent::TangentialPressure() { - return ShouldResistFingerprinting() +float PointerEvent::TangentialPressure(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) ? 0 : mEvent->AsPointerEvent()->tangentialPressure; } -int32_t PointerEvent::TiltX() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltX(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltX.isSome()) { @@ -291,8 +330,8 @@ int32_t PointerEvent::TiltX() { return *mTiltX; } -int32_t PointerEvent::TiltY() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltY(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltY.isSome()) { @@ -303,12 +342,14 @@ int32_t PointerEvent::TiltY() { return *mTiltY; } -int32_t PointerEvent::Twist() { - return ShouldResistFingerprinting() ? 0 : mEvent->AsPointerEvent()->twist; +int32_t PointerEvent::Twist(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) + ? 0 + : mEvent->AsPointerEvent()->twist; } -double PointerEvent::AltitudeAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AltitudeAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAltitudeAngle(); } if (mAltitudeAngle.isSome()) { @@ -319,8 +360,8 @@ double PointerEvent::AltitudeAngle() { return *mAltitudeAngle; } -double PointerEvent::AzimuthAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AzimuthAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAzimuthAngle(); } if (mAzimuthAngle.isSome()) { @@ -421,22 +462,22 @@ void PointerEvent::GetPredictedEvents( aPointerEvents.AppendElements(mPredictedEvents); } -bool PointerEvent::ShouldResistFingerprinting(bool aForPointerId) const { - // There are three simple situations we don't need to spoof this pointer +bool PointerEvent::ShouldResistFingerprinting(CallerType aCallerType) const { + // There are a few simple situations we don't need to spoof this pointer // event. - // 1. The pref privcy.resistFingerprinting' is false, we fast return here - // since we don't need to do any QI of following codes. - // 2. This event is generated by scripts. - // 3. This event is a mouse pointer event. + // * We are being called by a System caller + // * The pref privcy.resistFingerprinting' is false, we fast return here + // since we don't need to do any QI of following codes. + // * This event is generated by scripts. + // * This event is a mouse pointer event. // We don't need to check for the system group since pointer events won't be // dispatched to the system group. - RFPTarget target = - aForPointerId ? RFPTarget::PointerId : RFPTarget::PointerEvents; - if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || + RFPTarget target = RFPTarget::PointerEvents; + if (aCallerType == CallerType::System || + !nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || !mEvent->IsTrusted() || - (mEvent->AsPointerEvent()->mInputSource == - MouseEvent_Binding::MOZ_SOURCE_MOUSE && - SPOOFED_MAX_TOUCH_POINTS == 0)) { + mEvent->AsPointerEvent()->mInputSource == + MouseEvent_Binding::MOZ_SOURCE_MOUSE) { return false; } ===================================== dom/events/PointerEvent.h ===================================== @@ -41,17 +41,19 @@ class PointerEvent : public MouseEvent { PointerEvent* AsPointerEvent() final { return this; } int32_t PointerId(); - double Width() const; - double Height() const; - float Pressure(); - float TangentialPressure(); - int32_t TiltX(); - int32_t TiltY(); - int32_t Twist(); - double AltitudeAngle(); - double AzimuthAngle(); + double Width(CallerType aCallerType = CallerType::System) const; + double Height(CallerType aCallerType = CallerType::System) const; + float Pressure(CallerType aCallerType = CallerType::System); + float TangentialPressure(CallerType aCallerType = CallerType::System); + int32_t TiltX(CallerType aCallerType = CallerType::System); + int32_t TiltY(CallerType aCallerType = CallerType::System); + int32_t Twist(CallerType aCallerType = CallerType::System); + double AltitudeAngle(CallerType aCallerType = CallerType::System); + double AzimuthAngle(CallerType aCallerType = CallerType::System); bool IsPrimary(); - void GetPointerType(nsAString& aPointerType); + void GetPointerType( + nsAString& aPointerType, + mozilla::dom::CallerType aCallerType = CallerType::System); static bool EnableGetCoalescedEvents(JSContext* aCx, JSObject* aGlobal); void GetCoalescedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); void GetPredictedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); @@ -62,7 +64,11 @@ class PointerEvent : public MouseEvent { private: // This method returns the boolean to indicate whether spoofing pointer // event for fingerprinting resistance. - bool ShouldResistFingerprinting(bool aForPointerId = false) const; + bool ShouldResistFingerprinting( + CallerType aCallerType = CallerType::System) const; + + uint16_t ResistantInputSource( + CallerType aCallerType = CallerType::System) const; // When the instance is a trusted `pointermove` event but the widget event // does not have proper coalesced events (typically, the event is synthesized ===================================== dom/events/PointerEventHandler.cpp ===================================== @@ -421,32 +421,6 @@ void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) { PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId); - // When fingerprinting resistance is enabled, we need to map other pointer - // ids into the spoofed one. We don't have to do the mapping if the capture - // info exists for the non-spoofed pointer id because of we won't allow - // content to set pointer capture other than the spoofed one. Thus, it must be - // from chrome if the capture info exists in this case. And we don't have to - // do anything if the pointer id is the same as the spoofed one. - if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check", - RFPTarget::PointerId) && - aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() && - !captureInfo) { - PointerCaptureInfo* spoofedCaptureInfo = - GetPointerCaptureInfo(GetSpoofedPointerIdForRFP()); - - // We need to check the target element's document should resist - // fingerprinting. If not, we don't need to send a capture event - // since the capture info of the original pointer id doesn't exist - // in this case. - if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement || - !spoofedCaptureInfo->mPendingElement->OwnerDoc() - ->ShouldResistFingerprinting(RFPTarget::PointerEvents)) { - return; - } - - captureInfo = spoofedCaptureInfo; - } - if (!captureInfo || captureInfo->mPendingElement == captureInfo->mOverrideElement) { return; ===================================== dom/events/TouchEvent.cpp ===================================== @@ -225,38 +225,40 @@ bool TouchEvent::PrefEnabled(nsIDocShell* aDocShell) { } else if (touchEventsOverride == mozilla::dom::TouchEventsOverride::Disabled) { enabled = false; + } else if (nsContentUtils::ShouldResistFingerprinting( + aDocShell, RFPTarget::PointerEvents)) { +#ifdef MOZ_WIDGET_COCOA + enabled = false; +#else + enabled = true; +#endif } else { const int32_t prefValue = StaticPrefs::dom_w3c_touch_events_enabled(); if (prefValue == 2) { - if (nsContentUtils::ShouldResistFingerprinting( - aDocShell, RFPTarget::PointerEvents)) { - enabled = SPOOFED_MAX_TOUCH_POINTS != 0; - } else { - enabled = PlatformSupportsTouch(); - - static bool firstTime = true; - // The touch screen data seems to be inaccurate in the parent process, - // and we really need the crash annotation in child processes. - if (firstTime && !XRE_IsParentProcess()) { - CrashReporter::RecordAnnotationBool( - CrashReporter::Annotation::HasDeviceTouchScreen, enabled); - firstTime = false; - } + enabled = PlatformSupportsTouch(); + + static bool firstTime = true; + // The touch screen data seems to be inaccurate in the parent process, + // and we really need the crash annotation in child processes. + if (firstTime && !XRE_IsParentProcess()) { + CrashReporter::RecordAnnotationBool( + CrashReporter::Annotation::HasDeviceTouchScreen, enabled); + firstTime = false; + } #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) - if (enabled && aDocShell) { - // APZ might be disabled on this particular widget, in which case - // TouchEvent support will also be disabled. Try to detect that. - RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); - if (pc) { - nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); - if (widget) { - enabled &= widget->AsyncPanZoomEnabled(); - } + if (enabled && aDocShell) { + // APZ might be disabled on this particular widget, in which case + // TouchEvent support will also be disabled. Try to detect that. + RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); + if (pc) { + nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); + if (widget) { + enabled &= widget->AsyncPanZoomEnabled(); } } -#endif } +#endif } else { enabled = !!prefValue; } ===================================== dom/webidl/PointerEvent.webidl ===================================== @@ -14,16 +14,26 @@ interface PointerEvent : MouseEvent readonly attribute long pointerId; + [NeedsCallerType] readonly attribute double width; + [NeedsCallerType] readonly attribute double height; + [NeedsCallerType] readonly attribute float pressure; + [NeedsCallerType] readonly attribute float tangentialPressure; + [NeedsCallerType] readonly attribute long tiltX; + [NeedsCallerType] readonly attribute long tiltY; + [NeedsCallerType] readonly attribute long twist; + [NeedsCallerType] readonly attribute double altitudeAngle; + [NeedsCallerType] readonly attribute double azimuthAngle; + [NeedsCallerType] readonly attribute DOMString pointerType; readonly attribute boolean isPrimary; ===================================== layout/base/PositionedEventTargeting.cpp ===================================== @@ -16,6 +16,7 @@ #include "mozilla/ToString.h" #include "mozilla/ViewportUtils.h" #include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/dom/TouchEvent.h" #include "mozilla/gfx/Matrix.h" #include "mozilla/layers/LayersTypes.h" #include "nsContainerFrame.h" @@ -173,9 +174,7 @@ static bool HasTouchListener(const nsIContent* aContent) { return false; } - // FIXME: Should this really use the pref rather than TouchEvent::PrefEnabled - // or such? - if (!StaticPrefs::dom_w3c_touch_events_enabled()) { + if (!TouchEvent::PrefEnabled(aContent->OwnerDoc()->GetDocShell())) { return false; } ===================================== toolkit/components/resistfingerprinting/RFPTargets.inc ===================================== @@ -34,7 +34,7 @@ ITEM_VALUE(NavigatorHWConcurrency, 16) ITEM_VALUE(NavigatorOscpu, 17) ITEM_VALUE(NavigatorPlatform, 18) ITEM_VALUE(NavigatorUserAgent, 19) -ITEM_VALUE(PointerId, 20) +// We no longer use PointerId, it can renamed and reused ITEM_VALUE(StreamVideoFacingMode, 21) ITEM_VALUE(JSDateTimeUTC, 22) ITEM_VALUE(JSMathFdlibm, 23) @@ -104,6 +104,7 @@ ITEM_VALUE(DiskStorageLimit, 70) ITEM_VALUE(WebCodecs, 71) ITEM_VALUE(NavigatorHWConcurrencyTiered,74) +// !!! Adding a new target? Rename PointerId and repurpose it. // !!! Don't forget to update kDefaultFingerprintingProtections in nsRFPService.cpp // if necessary. ===================================== toolkit/components/resistfingerprinting/nsRFPService.cpp ===================================== @@ -303,13 +303,6 @@ Maybe<bool> nsRFPService::HandleExeptionalRFPTargets( StaticPrefs::privacy_spoof_english_DoNotUseDirectly() == 2); } - // We don't spoof the pointerId on multi-touch devices. -#if SPOOFED_MAX_TOUCH_POINTS > 0 - if (aTarget == RFPTarget::PointerId) { - return Some(false); - } -#endif - #ifdef ANDROID if (aTarget == RFPTarget::FontVisibilityBaseSystem || aTarget == RFPTarget::FontVisibilityLangPack) { ===================================== widget/WidgetEventImpl.cpp ===================================== @@ -589,23 +589,6 @@ bool WidgetEvent::IsBlockedForFingerprintingResistance() const { keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Control || keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_AltGraph); } - case ePointerEventClass: { - if (IsPointerEventMessageOriginallyMouseEventMessage(mMessage)) { - return false; - } - - if (SPOOFED_MAX_TOUCH_POINTS > 0) { - return false; - } - - const WidgetPointerEvent* pointerEvent = AsPointerEvent(); - - // We suppress the pointer events if it is not primary for fingerprinting - // resistance. It is because of that we want to spoof any pointer event - // into a mouse pointer event and the mouse pointer event only has - // isPrimary as true. - return !pointerEvent->mIsPrimary; - } default: return false; } View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/7966a2… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/7966a2… 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.5.9 (Android-only, amended)
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch main at The Tor Project / Applications / Tor Browser update responses Commits: 5e9c60ba by hackademix at 2025-10-15T15:18:57+02:00 release: new version, 14.5.9 (Android-only, amended) - - - - - 2 changed files: - + update_3/release/.htaccess - + update_3/release/downloads.json Changes: ===================================== update_3/release/.htaccess ===================================== @@ -0,0 +1,23 @@ +RewriteEngine On +# bug 26570: Redirect pre-8.0 stable users to a separate update directory +RewriteRule ^[^/]+/[4567]\..*/.* https://aus1.torproject.org/torbrowser/update_pre8.0/release/$0 [last] +# tor-browser-build#40678: Force all <=11.5.7 users to update through 11.5.8 before 12.0 +RewriteRule ^[^/]+/[89]\..*/.* https://aus1.torproject.org/torbrowser/update_pre12.0/release/$0 [last] +RewriteRule ^[^/]+/10\..*/.* https://aus1.torproject.org/torbrowser/update_pre12.0/release/$0 [last] +RewriteRule ^[^/]+/11\.0.*/.* https://aus1.torproject.org/torbrowser/update_pre12.0/release/$0 [last] +RewriteRule ^[^/]+/11\.5/.* https://aus1.torproject.org/torbrowser/update_pre12.0/release/$0 [last] +RewriteRule ^[^/]+/11\.5\.[01234567]/.* https://aus1.torproject.org/torbrowser/update_pre12.0/release/$0 [last] +# tor-browser-build#41270: make 13.5.7 a watershed update +RewriteRule ^[^/]+/1[12]\.[05].*/.* https://aus1.torproject.org/torbrowser/update_pre14.0/release/$0 [last] +RewriteRule ^[^/]+/13\.0.*/.* https://aus1.torproject.org/torbrowser/update_pre14.0/release/$0 [last] +RewriteRule ^[^/]+/13\.5/.* https://aus1.torproject.org/torbrowser/update_pre14.0/release/$0 [last] +RewriteRule ^[^/]+/13\.5\.[0123456]/.* https://aus1.torproject.org/torbrowser/update_pre14.0/release/$0 [last] +RewriteRule ^Linux_aarch64-gcc3/(.*) linux-aarch64/$1 [last] +RewriteRule ^Linux_x86-gcc3/(.*) linux-i686/$1 [last] +RewriteRule ^Linux_x86_64-gcc3/(.*) linux-x86_64/$1 [last] +RewriteRule ^Darwin_x86_64-gcc3/(.*) macos/$1 [last] +RewriteRule ^Darwin_aarch64-gcc3/(.*) macos/$1 [last] +RewriteRule ^WINNT_x86-gcc3/(.*) windows-i686/$1 [last] +RewriteRule ^WINNT_x86-gcc3-x86/(.*) windows-i686/$1 [last] +RewriteRule ^WINNT_x86-gcc3-x64/(.*) windows-i686/$1 [last] +RewriteRule ^WINNT_x86_64-gcc3-x64/(.*) windows-x86_64/$1 [last] ===================================== update_3/release/downloads.json ===================================== @@ -0,0 +1,37 @@ +{ + "comment" : "This file is deprecated and should not be used. Please use the files download-$platform.json instead.", + "downloads" : { + "linux-i686" : { + "ALL" : { + "binary" : "https://dist.torproject.org/torbrowser/14.5.8/tor-browser-linux-i686-14.5.8…", + "sig" : "https://dist.torproject.org/torbrowser/14.5.8/tor-browser-linux-i686-14.5.8…" + } + }, + "linux-x86_64" : { + "ALL" : { + "binary" : "https://dist.torproject.org/torbrowser/14.5.8/tor-browser-linux-x86_64-14.5…", + "sig" : "https://dist.torproject.org/torbrowser/14.5.8/tor-browser-linux-x86_64-14.5…" + } + }, + "macos" : { + "ALL" : { + "binary" : "https://dist.torproject.org/torbrowser/14.5.8/tor-browser-macos-14.5.8.dmg", + "sig" : "https://dist.torproject.org/torbrowser/14.5.8/tor-browser-macos-14.5.8.dmg.…" + } + }, + "win32" : { + "ALL" : { + "binary" : "https://dist.torproject.org/torbrowser/14.5.8/tor-browser-windows-i686-port…", + "sig" : "https://dist.torproject.org/torbrowser/14.5.8/tor-browser-windows-i686-port…" + } + }, + "win64" : { + "ALL" : { + "binary" : "https://dist.torproject.org/torbrowser/14.5.8/tor-browser-windows-x86_64-po…", + "sig" : "https://dist.torproject.org/torbrowser/14.5.8/tor-browser-windows-x86_64-po…" + } + } + }, + "tag" : "tbb-14.5.8-build1", + "version" : "14.5.8" +} 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-140.4.0esr-15.0-1] fixup! [android] Disable features and functionality
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 7966a205 by Dan Ballard at 2025-10-15T13:18:10+00:00 fixup! [android] Disable features and functionality Bug 43676: preemptively disable unified trust panel by default so we are tracking for next ESR - - - - - 1 changed file: - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt Changes: ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt ===================================== @@ -2090,7 +2090,7 @@ class Settings(private val appContext: Context) : PreferencesHolder { */ var enableUnifiedTrustPanel by booleanPreference( key = appContext.getPreferenceKey(R.string.pref_key_enable_unified_trust_panel), - default = FeatureFlags.UNIFIED_TRUST_PANEL, + default = false ) /** View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/7966a20… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/7966a20… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 373460c6 by hackademix at 2025-10-15T12:36:16+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -247,7 +248,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -260,7 +260,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -327,16 +329,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -361,7 +366,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -369,6 +374,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -448,7 +468,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/373460c… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/373460c… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • ...
  • 798
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.