tbb-commits
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
September 2026
- 1 participants
- 218 discussions
[Git][tpo/applications/mullvad-browser][mullvad-browser-153.2.0esr-16.0-1] 26 commits: Add CI for Mullvad Browser
by Pier Angelo Vendrame (@pierov) 01 Sep '26
by Pier Angelo Vendrame (@pierov) 01 Sep '26
01 Sep '26
Pier Angelo Vendrame pushed to branch mullvad-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
8484b31e by Henry Wilkes at 2026-09-01T17:06:19+02:00
Add CI for Mullvad Browser
- - - - -
e1df0e16 by Pier Angelo Vendrame at 2026-09-01T17:06:20+02:00
MB 38: Mullvad Browser configuration
- - - - -
85a421d3 by Pier Angelo Vendrame at 2026-09-01T17:06:20+02:00
MB 1: Mullvad Browser branding
See also:
mullvad-browser#5: Product name and directory customization
mullvad-browser#12: Create new branding directories and integrate Mullvad icons+branding
mullvad-browser#14: Remove Default Built-in bookmarks
mullvad-browser#35: Add custom PDF icons for Windows builds
mullvad-browser#48: Replace Mozilla copyright and legal trademarks in mullvadbrowser.exe metadata
mullvad-browser#51: Update trademark string
mullvad-browser#104: Update shipped dll metadata copyright/licensing info
mullvad-browser#107: Add alpha and nightly icons
- - - - -
93f76b17 by Henry Wilkes at 2026-09-01T17:06:20+02:00
Mullvad Browser strings
This commit adds strings needed by the following Mullvad Browser
patches.
- - - - -
cdd24492 by Pier Angelo Vendrame at 2026-09-01T17:06:21+02:00
MB 20: Allow packaged-addons in PBM.
We install a few addons from the distribution directory, but they are
not automatically enabled for PBM mode.
This commit modifies the code that installs them to also add the PBM
permission to the known ones.
- - - - -
366bdb5a by Pier Angelo Vendrame at 2026-09-01T17:06:21+02:00
MB 63: Customize some about pages for Mullvad Browser
Also:
mullvad-browser#57: Purge unneeded about: pages
- - - - -
c5388c45 by Pier Angelo Vendrame at 2026-09-01T17:06:21+02:00
MB 37: Customization for the about dialog
- - - - -
8286de8a by Henry Wilkes at 2026-09-01T17:06:22+02:00
MB 39: Add home page about:mullvad-browser
- - - - -
4953d27f by hackademix at 2026-09-01T17:06:22+02:00
MB 97: Remove UI cues to install new extensions.
- - - - -
f2d376bb by hackademix at 2026-09-01T17:06:22+02:00
MB 47: uBlock Origin customization
- - - - -
c698846f by Pier Angelo Vendrame at 2026-09-01T17:06:23+02:00
MB 21: Disable the password manager
This commit disables the about:login page and removes the "Login and
Password" section of about:preferences.
We do not do anything to the real password manager of Firefox, that is
in toolkit: it contains C++ parts that make it difficult to actually
prevent it from being built..
Finally, we modify the the function that opens about:login to report an
error in the console so that we can quickly get a backtrace to the code
that tries to use it.
- - - - -
3ba62149 by Pier Angelo Vendrame at 2026-09-01T17:06:23+02:00
MB 112: Updater customization for Mullvad Browser
MB 71: Set the updater base URL to Mullvad domain
- - - - -
2607642c by Nicolas Vigier at 2026-09-01T17:06:23+02:00
MB 79: Add Mullvad Browser MAR signing keys
MB 256: Add mullvad-browser nightly mar signing key
- - - - -
3547d56e by Pier Angelo Vendrame at 2026-09-01T17:06:24+02:00
MB 34: Hide unwanted setting controls in Mullvad Browser.
- - - - -
681373de by hackademix at 2026-09-01T17:06:24+02:00
MB 163: Prevent uBlock Origin from being uninstalled/disabled
- - - - -
5d66b060 by Pier Angelo Vendrame at 2026-09-01T17:06:24+02:00
MB 160: Disable the cookie exceptions button
Besides disabling the "Delete on close checkbox", disable also the
"Manage Exceptions" button when always using PBM.
- - - - -
d9df70c7 by Richard Pospesel at 2026-09-01T17:06:25+02:00
MB 188: Customize Gitlab Issue and Merge templates
- - - - -
762855d2 by rui hildt at 2026-09-01T17:06:25+02:00
MB 213: Customize the search engines list.
- - - - -
939dd6ba by hackademix at 2026-09-01T17:06:25+02:00
MB 214: Enable cross-tab identity leak protection in "quiet" mode
- - - - -
73526876 by Pier Angelo Vendrame at 2026-09-01T17:06:26+02:00
MB 320: Temporarily disable WebRTC and WDBA on Windows.
WebRTC should be re-enabled when tor-browser#42758 is resolved, and and
the default browser agent when in general we make this feature work
again.
- - - - -
fb607306 by Henry Wilkes at 2026-09-01T17:06:26+02:00
MB 329: Customize toolbar for mullvad-browser.
- - - - -
ef01dd14 by Henry Wilkes at 2026-09-01T17:06:26+02:00
MB 419: Mullvad Browser migration procedures.
This commit implements the the Mullvad Browser's version of _migrateUI.
- - - - -
2f028def by Henry Wilkes at 2026-09-01T17:06:27+02:00
MB 488: Adjust search engine removal notification for Mullvad Leta.
- - - - -
3c008b54 by Pier Angelo Vendrame at 2026-09-01T17:06:27+02:00
MB 80: Enable Mullvad Browser as a default browser
- - - - -
3f69227f by Beatriz Rizental at 2026-09-01T17:06:27+02:00
MB 43564: Modify ./mach bootstrap for Mullvad Browser
- - - - -
bb465068 by june wilde at 2026-09-01T17:06:28+02:00
MB 446: Selective Saving Mode Backend
- - - - -
273 changed files:
- .gitlab/ci/jobs/update-translations.yml
- .gitlab/issue_templates/000 Bug Report.md
- .gitlab/issue_templates/010 Proposal.md
- .gitlab/issue_templates/020 Web Compatibility.md
- .gitlab/issue_templates/030 Test.md
- .gitlab/issue_templates/040 Feature.md
- .gitlab/issue_templates/041 Time-Gated Feature.md
- .gitlab/issue_templates/051 Revert.md
- + .gitlab/issue_templates/060 Rebase - Alpha.md
- + .gitlab/issue_templates/061 Rebase - Stable.md
- .gitlab/issue_templates/090 Emergency Security Issue.md
- .gitlab/merge_request_templates/Default.md
- + .gitlab/merge_request_templates/Rebase.md
- browser/app/Makefile.in
- browser/app/macbuild/Contents/Info.plist.in
- browser/app/module.ver
- browser/app/firefox.exe.manifest → browser/app/mullvadbrowser.exe.manifest
- + browser/app/profile/000-mullvad-browser.js
- browser/app/profile/001-base-profile.js
- browser/base/content/aboutDialog.xhtml
- browser/base/content/appmenu-viewcache.inc.xhtml
- browser/base/content/browser-menubar.inc.xhtml
- browser/base/content/browser-places.js
- browser/base/content/browser.js
- browser/base/content/default-bookmarks.html
- browser/base/content/nsContextMenu.sys.mjs
- browser/base/content/overrides/app-license.html
- browser/base/content/pageinfo/pageInfo.xhtml
- browser/base/content/utilityOverlay.js
- browser/branding/branding-common.mozbuild
- + browser/branding/mb-alpha/VisualElements_150.png
- + browser/branding/mb-alpha/VisualElements_70.png
- + browser/branding/mb-alpha/configure.sh
- + browser/branding/mb-alpha/content/about-logo.png
- + browser/branding/mb-alpha/content/about-logo.svg
- + browser/branding/mb-alpha/content/about-logo(a)2x.png
- + browser/branding/mb-alpha/content/about-wordmark.svg
- + browser/branding/mb-alpha/content/about.png
- + browser/branding/mb-alpha/content/aboutDialog.css
- + browser/branding/mb-alpha/content/document_pdf.svg
- + browser/branding/mb-alpha/content/firefox-wordmark.svg
- + browser/branding/mb-alpha/content/icon128.png
- + browser/branding/mb-alpha/content/icon16.png
- + browser/branding/mb-alpha/content/icon256.png
- + browser/branding/mb-alpha/content/icon32.png
- + browser/branding/mb-alpha/content/icon48.png
- + browser/branding/mb-alpha/content/icon64.png
- + browser/branding/mb-alpha/content/jar.mn
- + browser/branding/mb-alpha/content/moz.build
- + browser/branding/mb-alpha/content/mullvad-branding.css
- + browser/branding/mb-alpha/default128.png
- + browser/branding/mb-alpha/default16.png
- + browser/branding/mb-alpha/default22.png
- + browser/branding/mb-alpha/default24.png
- + browser/branding/mb-alpha/default256.png
- + browser/branding/mb-alpha/default32.png
- + browser/branding/mb-alpha/default48.png
- + browser/branding/mb-alpha/default64.png
- + browser/branding/mb-alpha/document.icns
- + browser/branding/mb-alpha/document.ico
- + browser/branding/mb-alpha/document_pdf.ico
- + browser/branding/mb-alpha/firefox.icns
- + browser/branding/mb-alpha/firefox.ico
- + browser/branding/mb-alpha/firefox.svg
- + browser/branding/mb-alpha/locales/en-US/brand.ftl
- + browser/branding/mb-alpha/locales/en-US/brand.properties
- + browser/branding/mb-alpha/locales/jar.mn
- + browser/branding/mb-alpha/locales/moz.build
- + browser/branding/mb-alpha/locales/mullvad-about-wordmark-en.ftl
- + browser/branding/mb-alpha/moz.build
- + browser/branding/mb-alpha/mullvadbrowser.VisualElementsManifest.xml
- + browser/branding/mb-alpha/newtab.ico
- + browser/branding/mb-alpha/newwindow.ico
- + browser/branding/mb-alpha/pbmode.ico
- + browser/branding/mb-alpha/pref/firefox-branding.js
- + browser/branding/mb-nightly/VisualElements_150.png
- + browser/branding/mb-nightly/VisualElements_70.png
- + browser/branding/mb-nightly/configure.sh
- + browser/branding/mb-nightly/content/about-logo.png
- + browser/branding/mb-nightly/content/about-logo.svg
- + browser/branding/mb-nightly/content/about-logo(a)2x.png
- + browser/branding/mb-nightly/content/about-wordmark.svg
- + browser/branding/mb-nightly/content/about.png
- + browser/branding/mb-nightly/content/aboutDialog.css
- + browser/branding/mb-nightly/content/document_pdf.svg
- + browser/branding/mb-nightly/content/firefox-wordmark.svg
- + browser/branding/mb-nightly/content/icon128.png
- + browser/branding/mb-nightly/content/icon16.png
- + browser/branding/mb-nightly/content/icon256.png
- + browser/branding/mb-nightly/content/icon32.png
- + browser/branding/mb-nightly/content/icon48.png
- + browser/branding/mb-nightly/content/icon64.png
- + browser/branding/mb-nightly/content/jar.mn
- + browser/branding/mb-nightly/content/moz.build
- + browser/branding/mb-nightly/content/mullvad-branding.css
- + browser/branding/mb-nightly/default128.png
- + browser/branding/mb-nightly/default16.png
- + browser/branding/mb-nightly/default22.png
- + browser/branding/mb-nightly/default24.png
- + browser/branding/mb-nightly/default256.png
- + browser/branding/mb-nightly/default32.png
- + browser/branding/mb-nightly/default48.png
- + browser/branding/mb-nightly/default64.png
- + browser/branding/mb-nightly/document.icns
- + browser/branding/mb-nightly/document.ico
- + browser/branding/mb-nightly/document_pdf.ico
- + browser/branding/mb-nightly/firefox.icns
- + browser/branding/mb-nightly/firefox.ico
- + browser/branding/mb-nightly/firefox.svg
- + browser/branding/mb-nightly/locales/en-US/brand.ftl
- + browser/branding/mb-nightly/locales/en-US/brand.properties
- + browser/branding/mb-nightly/locales/jar.mn
- + browser/branding/mb-nightly/locales/moz.build
- + browser/branding/mb-nightly/locales/mullvad-about-wordmark-en.ftl
- + browser/branding/mb-nightly/moz.build
- + browser/branding/mb-nightly/mullvadbrowser.VisualElementsManifest.xml
- + browser/branding/mb-nightly/newtab.ico
- + browser/branding/mb-nightly/newwindow.ico
- + browser/branding/mb-nightly/pbmode.ico
- + browser/branding/mb-nightly/pref/firefox-branding.js
- + browser/branding/mb-release/VisualElements_150.png
- + browser/branding/mb-release/VisualElements_70.png
- + browser/branding/mb-release/configure.sh
- + browser/branding/mb-release/content/about-logo.png
- + browser/branding/mb-release/content/about-logo.svg
- + browser/branding/mb-release/content/about-logo(a)2x.png
- + browser/branding/mb-release/content/about-wordmark.svg
- + browser/branding/mb-release/content/about.png
- + browser/branding/mb-release/content/aboutDialog.css
- + browser/branding/mb-release/content/document_pdf.svg
- + browser/branding/mb-release/content/firefox-wordmark.svg
- + browser/branding/mb-release/content/icon128.png
- + browser/branding/mb-release/content/icon16.png
- + browser/branding/mb-release/content/icon256.png
- + browser/branding/mb-release/content/icon32.png
- + browser/branding/mb-release/content/icon48.png
- + browser/branding/mb-release/content/icon64.png
- + browser/branding/mb-release/content/jar.mn
- + browser/branding/mb-release/content/moz.build
- + browser/branding/mb-release/content/mullvad-branding.css
- + browser/branding/mb-release/default128.png
- + browser/branding/mb-release/default16.png
- + browser/branding/mb-release/default22.png
- + browser/branding/mb-release/default24.png
- + browser/branding/mb-release/default256.png
- + browser/branding/mb-release/default32.png
- + browser/branding/mb-release/default48.png
- + browser/branding/mb-release/default64.png
- + browser/branding/mb-release/document.icns
- + browser/branding/mb-release/document.ico
- + browser/branding/mb-release/document_pdf.ico
- + browser/branding/mb-release/firefox.icns
- + browser/branding/mb-release/firefox.ico
- + browser/branding/mb-release/firefox.svg
- + browser/branding/mb-release/locales/en-US/brand.ftl
- + browser/branding/mb-release/locales/en-US/brand.properties
- + browser/branding/mb-release/locales/jar.mn
- + browser/branding/mb-release/locales/moz.build
- + browser/branding/mb-release/locales/mullvad-about-wordmark-en.ftl
- + browser/branding/mb-release/moz.build
- + browser/branding/mb-release/mullvadbrowser.VisualElementsManifest.xml
- + browser/branding/mb-release/newtab.ico
- + browser/branding/mb-release/newwindow.ico
- + browser/branding/mb-release/pbmode.ico
- + browser/branding/mb-release/pref/firefox-branding.js
- browser/components/BrowserContentHandler.sys.mjs
- browser/components/BrowserGlue.sys.mjs
- browser/components/DesktopActorRegistry.sys.mjs
- browser/components/ProfileDataUpgrader.sys.mjs
- browser/components/about/AboutRedirector.cpp
- browser/components/about/components.conf
- browser/components/customizableui/CustomizableUI.sys.mjs
- browser/components/moz.build
- + browser/components/mullvad-browser/AboutMullvadBrowserChild.sys.mjs
- + browser/components/mullvad-browser/AboutMullvadBrowserParent.sys.mjs
- + browser/components/mullvad-browser/content/2728-sparkles.svg
- + browser/components/mullvad-browser/content/aboutMullvadBrowser.css
- + browser/components/mullvad-browser/content/aboutMullvadBrowser.html
- + browser/components/mullvad-browser/content/aboutMullvadBrowser.js
- + browser/components/mullvad-browser/jar.mn
- + browser/components/mullvad-browser/moz.build
- browser/components/preferences/config/passwords-autofill.mjs
- browser/components/preferences/config/privacy.mjs
- browser/components/preferences/config/search.mjs
- browser/components/preferences/home.inc.xhtml
- browser/components/preferences/preferences.js
- browser/components/preferences/preferences.xhtml
- browser/components/preferences/privacy.inc.xhtml
- browser/components/preferences/search.inc.xhtml
- browser/components/search/SearchUIUtils.sys.mjs
- browser/components/shell/ShellService.sys.mjs
- browser/components/shell/WindowsDefaultBrowser.cpp
- browser/components/shell/nsWindowsShellService.cpp
- browser/components/tabbrowser/NewTabPagePreloading.sys.mjs
- browser/config/mozconfigs/base-browser
- + browser/config/mozconfigs/mullvad-browser
- browser/installer/package-manifest.in
- browser/installer/windows/nsis/updater_append.ini
- browser/locales/l10n.toml
- browser/modules/HomePage.sys.mjs
- browser/moz.build
- browser/moz.configure
- build/moz.configure/basebrowser-resources.configure
- build/moz.configure/bootstrap.configure
- config/create_rc.py
- devtools/client/aboutdebugging/src/actions/runtimes.js
- devtools/client/aboutdebugging/src/components/sidebar/Sidebar.js
- devtools/client/jar.mn
- devtools/client/themes/images/aboutdebugging-firefox-aurora.svg
- devtools/client/themes/images/aboutdebugging-firefox-beta.svg
- devtools/client/themes/images/aboutdebugging-firefox-logo.svg
- devtools/client/themes/images/aboutdebugging-firefox-nightly.svg
- devtools/client/themes/images/aboutdebugging-firefox-release.svg
- + devtools/client/themes/images/aboutdebugging-mullvadbrowser-logo.svg
- docshell/base/nsAboutRedirector.cpp
- docshell/build/components.conf
- moz.configure
- mozconfig-linux-aarch64
- mozconfig-linux-aarch64-dev
- mozconfig-linux-x86_64
- mozconfig-linux-x86_64-asan
- mozconfig-linux-x86_64-dev
- mozconfig-macos
- mozconfig-macos-dev
- mozconfig-windows-x86_64
- + other-licenses/nsis/Contrib/ApplicationID/Makefile
- other-licenses/nsis/Contrib/ApplicationID/Set.cpp
- + other-licenses/nsis/Contrib/CityHash/Makefile
- python/mozboot/mozboot/bootstrap.py
- python/mozbuild/mozbuild/backend/base.py
- python/mozbuild/mozbuild/tbbutils.py
- toolkit/components/aboutpdf/content/aboutPDF.html
- toolkit/components/extensions/child/ext-storage.js
- toolkit/components/extensions/parent/ext-storage.js
- toolkit/components/moz.build
- toolkit/components/passwordmgr/LoginHelper.sys.mjs
- toolkit/components/remote/nsDBusRemoteClient.cpp
- toolkit/components/remote/nsDBusRemoteServer.cpp
- toolkit/components/search/SearchService.sys.mjs
- toolkit/components/search/content/base-browser-search-engine-icons.json
- toolkit/components/search/content/base-browser-search-engines.json
- + toolkit/components/search/content/brave.svg
- + toolkit/components/search/content/mojeek.ico
- toolkit/components/search/tests/xpcshell/test_base_browser.js
- toolkit/components/securitylevel/SecurityLevel.sys.mjs
- + toolkit/components/selectivesavingmode/SelectiveSavingMode.manifest
- + toolkit/components/selectivesavingmode/SelectiveSavingMode.sys.mjs
- + toolkit/components/selectivesavingmode/components.conf
- + toolkit/components/selectivesavingmode/content/broom-clean.svg
- + toolkit/components/selectivesavingmode/content/broom-saved.svg
- + toolkit/components/selectivesavingmode/jar.mn
- + toolkit/components/selectivesavingmode/moz.build
- + toolkit/components/selectivesavingmode/tests/xpcshell/test_selective_saving_mode.js
- + toolkit/components/selectivesavingmode/tests/xpcshell/xpcshell.toml
- + toolkit/content/aboutTelemetryMullvad.xhtml
- toolkit/content/jar.mn
- + toolkit/locales/en-US/toolkit/global/mullvad-browser.ftl
- toolkit/mozapps/defaultagent/EventLog.h
- toolkit/mozapps/defaultagent/SetDefaultBrowser.cpp
- toolkit/mozapps/extensions/AddonManager.sys.mjs
- toolkit/mozapps/extensions/content/aboutaddons.css
- toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs
- toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs
- toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der
- toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der
- toolkit/mozapps/update/updater/release_primary.der
- toolkit/mozapps/update/updater/release_secondary.der
- + toolkit/themes/shared/icons/mullvadbrowser.png
- toolkit/themes/shared/minimal-toolkit.jar.inc.mn
- toolkit/xre/nsAppRunner.cpp
- tools/lint/fluent-lint/exclusions.yml
- widget/windows/WinTaskbar.cpp
- widget/windows/moz.build
The diff was not included because it is too large.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/1b…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/1b…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-153.2.0esr-16.0-1] fixup! TB 44806: Implement the tor integration in Rust.
by brizental (@brizental) 01 Sep '26
by brizental (@brizental) 01 Sep '26
01 Sep '26
brizental pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
f111c7eb by Beatriz Rizental at 2026-09-01T17:39:44+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Implement more tests.
- - - - -
3 changed files:
- toolkit/components/tor-integration/tor_provider/src/ctor/controller/escape.rs
- toolkit/components/tor-integration/tor_provider/src/ctor/controller/mod.rs
- toolkit/components/tor-integration/tor_provider/src/ctor/controller/unescape.rs
Changes:
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/controller/escape.rs
=====================================
@@ -58,9 +58,56 @@ mod tests {
}
#[test]
- fn escaped() {
+ fn escaped_special_chars() {
+ assert_eq!(tor_escape("'"), "\"\\'\"");
+ assert_eq!(tor_escape("\""), "\"\\\"\"");
+ assert_eq!(tor_escape("\\"), "\"\\\\\"");
assert_eq!(tor_escape("'\"\\\r\n\t"), "\"\\'\\\"\\\\\\r\\n\\t\"");
+ }
+
+ #[test]
+ fn escaped_control_chars() {
+ assert_eq!(tor_escape("\n"), "\"\\n\"");
+ assert_eq!(tor_escape("\t"), "\"\\t\"");
+ assert_eq!(tor_escape("\r"), "\"\\r\"");
+ }
+
+ #[test]
+ fn ascii_printable_boundaries() {
+ // 0x1F is just below the printable range and must be hex-escaped.
+ assert_eq!(tor_escape(b"\x1F"), "\"\\x1F\"");
+ // 0x20 (space) is the first byte of the printable range.
+ assert_eq!(tor_escape(b"\x20"), "\" \"");
+ // 0x7E ('~') is the last byte of the printable range.
+ assert_eq!(tor_escape(b"\x7E"), "\"~\"");
+ // 0x7F (DEL) is just above the printable range and must be hex-escaped.
+ assert_eq!(tor_escape(b"\x7F"), "\"\\x7F\"");
+ }
+
+ #[test]
+ fn hex_zero_padding() {
assert_eq!(tor_escape("\0"), "\"\\x00\"");
+ assert_eq!(tor_escape(b"\x0B"), "\"\\x0B\"");
+ assert_eq!(tor_escape(b"\x01"), "\"\\x01\"");
+ }
+
+ #[test]
+ fn hex_uppercase() {
+ assert_eq!(tor_escape(b"\xAB"), "\"\\xAB\"");
+ assert_eq!(tor_escape(b"\xFF"), "\"\\xFF\"");
+ }
+
+ #[test]
+ fn non_utf8_bytes() {
+ // Raw invalid UTF-8 bytes are escaped byte-by-byte, same as any
+ // other non-printable byte.
+ assert_eq!(tor_escape(b"\xF5"), "\"\\xF5\"");
+ assert_eq!(tor_escape(b"\xFF\xFE"), "\"\\xFF\\xFE\"");
+ assert_eq!(tor_escape(b"te\xF5st"), "\"te\\xF5st\"");
+ }
+
+ #[test]
+ fn unicode() {
assert_eq!(tor_escape("\u{1F9C5}"), "\"\\xF0\\x9F\\xA7\\x85\"");
}
}
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/controller/mod.rs
=====================================
@@ -8,3 +8,42 @@ mod unescape;
use escape::*;
use unescape::*;
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn round_trip(buf: &[u8]) {
+ let mut escaped = String::new();
+ tor_escape_into(buf, &mut escaped);
+ assert_eq!(&*tor_unescape(escaped.as_bytes()).unwrap(), buf);
+ }
+
+ #[test]
+ fn round_trip_simple() {
+ round_trip(b"test");
+ }
+
+ #[test]
+ fn round_trip_empty() {
+ round_trip(b"");
+ }
+
+ #[test]
+ fn round_trip_special_chars() {
+ round_trip(b"'\"\\\r\n\t");
+ }
+
+ #[test]
+ fn round_trip_non_utf8() {
+ round_trip(b"\xF5\xFF\xFE");
+ }
+
+ #[test]
+ fn round_trip_all_bytes() {
+ // Every possible byte value, escaped then unescaped,
+ // must come back unchanged.
+ let all_bytes: Vec<u8> = (0..=255).collect();
+ round_trip(&all_bytes);
+ }
+}
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/controller/unescape.rs
=====================================
@@ -140,6 +140,26 @@ mod tests {
assert_eq!(&*tor_unescape(b"\"\"").unwrap(), &[]);
}
+ #[test]
+ fn lone_quote() {
+ // A single quote character is not a valid empty quoted string: there
+ // is no closing quote.
+ assert_eq!(
+ tor_unescape(b"\"").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ }
+
+ #[test]
+ fn quoted_spaces() {
+ // Spaces are only rejected in unquoted strings, so they must be
+ // preserved as-is once inside quotes.
+ assert_eq!(&*tor_unescape(b"\"test test\"").unwrap(), b"test test");
+ assert_eq!(&*tor_unescape(b"\" test\"").unwrap(), b" test");
+ assert_eq!(&*tor_unescape(b"\"test \"").unwrap(), b"test ");
+ assert_eq!(&*tor_unescape(b"\"t e s t\"").unwrap(), b"t e s t");
+ }
+
#[test]
fn unescape_simple() {
assert_eq!(&*tor_unescape(b"\"\\n\"").unwrap(), b"\n");
@@ -190,6 +210,23 @@ mod tests {
assert_eq!(&*tor_unescape(b"\"\\40test\\0\"").unwrap(), b" test\0");
}
+ #[test]
+ fn unescape_octal_max_digits() {
+ // Only up to 3 octal digits are consumed per escape, even if a 4th
+ // digit-looking byte follows.
+ assert_eq!(&*tor_unescape(b"\"\\0004\"").unwrap(), b"\x004");
+ assert_eq!(&*tor_unescape(b"\"\\0007\"").unwrap(), b"\x007");
+ }
+
+ #[test]
+ fn unescape_octal_ambiguous_digit() {
+ // '8' and '9' are not valid octal digits, so they end the escape
+ // early and are then treated as literal characters, even though at
+ // a glance "\048" looks like it could mean octal 048.
+ assert_eq!(&*tor_unescape(b"\"\\048\"").unwrap(), b"\x048");
+ assert_eq!(&*tor_unescape(b"\"\\09\"").unwrap(), b"\x009");
+ }
+
#[test]
fn invalid_unicode() {
// Raw invalid sequence, without quotes
@@ -287,4 +324,13 @@ mod tests {
UnescapeError::OctalOverflow,
);
}
+
+ #[test]
+ fn stress() {
+ // A mix of literals, spaces, and every kind of escape back to back.
+ assert_eq!(
+ &*tor_unescape(b"\"a \\n b\\tc\\r\\\\d\\'e\\\"f\\x20g\\040h\\048\"").unwrap(),
+ b"a \n b\tc\r\\d'e\"f g h\x048"
+ );
+ }
}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/f111c7e…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/f111c7e…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/mullvad-browser-update-responses][main] 4 commits: release: new version, 15.0.21 (linux-x86_64)
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed to branch main at The Tor Project / Applications / mullvad-browser-update-responses
Commits:
8dfb2a96 by hackademix at 2026-09-01T16:54:13+02:00
release: new version, 15.0.21 (linux-x86_64)
- - - - -
3c42d8f4 by hackademix at 2026-09-01T16:54:13+02:00
release: new version, 15.0.21 (macos)
- - - - -
316247fe by hackademix at 2026-09-01T16:54:13+02:00
release: new version, 15.0.21 (windows-x86_64)
- - - - -
bba9e13a by hackademix at 2026-09-01T16:54:13+02:00
release: new version, 15.0.21
- - - - -
31 changed files:
- update_1/release/download-linux-x86_64.json
- update_1/release/download-macos.json
- update_1/release/download-windows-x86_64.json
- update_1/release/downloads.json
- update_1/release/linux-x86_64/.htaccess
- − update_1/release/linux-x86_64/update-15.0.16-15.0.20-linux-x86_64.xml
- − update_1/release/linux-x86_64/update-15.0.18-15.0.20-linux-x86_64.xml
- + update_1/release/linux-x86_64/update-15.0.18-15.0.21-linux-x86_64.xml
- − update_1/release/linux-x86_64/update-15.0.19-15.0.20-linux-x86_64.xml
- + update_1/release/linux-x86_64/update-15.0.19-15.0.21-linux-x86_64.xml
- + update_1/release/linux-x86_64/update-15.0.20-15.0.21-linux-x86_64.xml
- − update_1/release/linux-x86_64/update-15.0.20-linux-x86_64.xml
- + update_1/release/linux-x86_64/update-15.0.21-linux-x86_64.xml
- update_1/release/macos/.htaccess
- − update_1/release/macos/update-15.0.16-15.0.20-macos.xml
- − update_1/release/macos/update-15.0.18-15.0.20-macos.xml
- + update_1/release/macos/update-15.0.18-15.0.21-macos.xml
- − update_1/release/macos/update-15.0.19-15.0.20-macos.xml
- + update_1/release/macos/update-15.0.19-15.0.21-macos.xml
- + update_1/release/macos/update-15.0.20-15.0.21-macos.xml
- − update_1/release/macos/update-15.0.20-macos.xml
- + update_1/release/macos/update-15.0.21-macos.xml
- update_1/release/windows-x86_64/.htaccess
- − update_1/release/windows-x86_64/update-15.0.16-15.0.20-windows-x86_64.xml
- − update_1/release/windows-x86_64/update-15.0.18-15.0.20-windows-x86_64.xml
- + update_1/release/windows-x86_64/update-15.0.18-15.0.21-windows-x86_64.xml
- − update_1/release/windows-x86_64/update-15.0.19-15.0.20-windows-x86_64.xml
- + update_1/release/windows-x86_64/update-15.0.19-15.0.21-windows-x86_64.xml
- + update_1/release/windows-x86_64/update-15.0.20-15.0.21-windows-x86_64.xml
- − update_1/release/windows-x86_64/update-15.0.20-windows-x86_64.xml
- + update_1/release/windows-x86_64/update-15.0.21-windows-x86_64.xml
The diff was not included because it is too large.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser-update-respo…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser-update-respo…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser] Pushed new tag base-browser-153.2.0esr-16.0-1-build2
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed new tag base-browser-153.2.0esr-16.0-1-build2 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/base-brow…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-153.2.0esr-16.0-1] 4 commits: Bug 2045435 - Prune dying entries from wasm::Realm::instances_ during sweeping. r=jpages
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed to branch mullvad-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
641493ef by Yury Delendik at 2026-09-01T16:51:37+02:00
Bug 2045435 - Prune dying entries from wasm::Realm::instances_ during sweeping. r=jpages
wasm::Realm::instances_ is a weak list. Readers rely on the
instances()[i]->object() read barrier, but that barrier is a no-op once the
owning zone is being incrementally swept, and entries are otherwise only
removed at Instance finalization (~Instance -> unregisterInstance). So between
marking a zone's instance objects dead and finalizing them, the list could
still hand an about-to-be-finalized instance to a reader.
Prune such entries at the start of zone sweeping via a new
wasm::Realm::traceWeakInstances(), called from beginSweepingSweepGroup
alongside the other per-realm weak-collection sweeps. This makes instances_
behave like the engine's other weak collections, so it never exposes an
about-to-be-finalized instance to the mutator during sweep slices.
Differential Revision: https://phabricator.services.mozilla.com/D314032
- - - - -
13e8e72e by Ting-Yu Lin at 2026-09-01T16:51:38+02:00
Bug 2053578 - Use static_cast in nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache(). r=layout-reviewers,jfkthame
`nsSplittableFrame` is a subclass of `nsIFrame`, it is sufficient to use
`static_cast`.
Differential Revision: https://phabricator.services.mozilla.com/D315157
- - - - -
7ce0a5f6 by Ting-Yu Lin at 2026-09-01T16:51:38+02:00
Bug 2053578 - Update first-in-flow cache when a continuation is changing from fluid to non-fluid. r=layout-reviewers,jfkthame
The original case in bug 2053578 comment 5 can reproduce an ASAN
use-after-poison with the patch bug 2053578 comment 6 applied. However, with
unpatched code, the best we can do is using a DEBUG-only assertion to catch the
error condition that detect a stale first-in-flow cache in next-in-flow.
`bidi-inline-continuation-first-in-flow.html` is generated with the help of
Claude code, and it can trigger the assertion without other fix in this patch.
Differential Revision: https://phabricator.services.mozilla.com/D315158
- - - - -
1b03bab9 by Iain Ireland at 2026-09-01T16:51:39+02:00
Bug 2058626: Check for mutually exclusive flags when deserializing cloned RegExp r=spidermonkey-reviewers,jonco
Differential Revision: https://phabricator.services.mozilla.com/D316904
- - - - -
8 changed files:
- js/src/gc/GCRuntime.h
- js/src/gc/Sweeping.cpp
- + js/src/jit-test/tests/structured-clone/bug2058626.js
- js/src/vm/StructuredClone.cpp
- js/src/wasm/WasmRealm.cpp
- js/src/wasm/WasmRealm.h
- layout/generic/nsSplittableFrame.cpp
- + testing/web-platform/tests/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html
Changes:
=====================================
js/src/gc/GCRuntime.h
=====================================
@@ -999,6 +999,7 @@ class GCRuntime {
void updateAtomsBitmap();
void sweepCCWrappers();
void sweepRealmGlobals();
+ void sweepWasmInstances();
void sweepEmbeddingWeakPointers(JS::GCContext* gcx);
void sweepMisc();
void sweepCompressionTasks();
=====================================
js/src/gc/Sweeping.cpp
=====================================
@@ -1387,6 +1387,13 @@ void GCRuntime::sweepRealmGlobals() {
}
}
+void GCRuntime::sweepWasmInstances() {
+ for (SweepGroupRealmsIter r(this); !r.done(); r.next()) {
+ AutoSetThreadIsSweeping threadIsSweeping(r->zone());
+ r->wasm.traceWeakInstances();
+ }
+}
+
void GCRuntime::sweepMisc() {
SweepingTracer trc(rt);
for (SweepGroupRealmsIter r(this); !r.done(); r.next()) {
@@ -1743,6 +1750,11 @@ IncrementalProgress GCRuntime::beginSweepingSweepGroup(JS::GCContext* gcx,
// This must happen before updating embedding weak pointers.
sweepRealmGlobals();
+ // Prune dying wasm instances from each realm's weak instance list now, at the
+ // start of sweeping, before the mutator can observe them via the (now no-op)
+ // instances() read barrier during later incremental slices.
+ sweepWasmInstances();
+
sweepEmbeddingWeakPointers(gcx);
maybeWriteCoverageAndSpew();
=====================================
js/src/jit-test/tests/structured-clone/bug2058626.js
=====================================
@@ -0,0 +1,16 @@
+function forge(pattern, srcFlags, flagsByte) {
+ var cb = serialize(new RegExp(pattern, srcFlags), undefined, { scope: "DifferentProcess" });
+ var u8 = new Uint8Array(cb.arraybuffer);
+ for (var i = 0; i + 8 <= u8.length; i += 4) {
+ var tag = u8[i+4] | (u8[i+5] << 8) | (u8[i+6] << 16) | (u8[i+7] << 24);
+ if ((tag >>> 0) === 0xFFFF0006) { u8[i] = flagsByte; break; }
+ }
+ cb.clonebuffer = u8.buffer;
+ return deserialize(cb, { scope: "DifferentProcess" });
+}
+try {
+ var forged = forge("[\\q{abc|de}]", "v", 0x90);
+ var bad = new RegExp(forged, "u");
+ try { bad.exec("abc"); } catch {}
+ var good = new RegExp("[\\q{abc|de}]", "u");
+} catch {}
=====================================
js/src/vm/StructuredClone.cpp
=====================================
@@ -3234,7 +3234,9 @@ bool JSStructuredCloneReader::startReadUnchecked(
}
case SCTAG_REGEXP_OBJECT: {
- if ((data & RegExpFlag::AllFlags) != data) {
+ // Reject invalid flags. /u and /v are mutually exclusive.
+ if ((data & RegExpFlag::AllFlags) != data ||
+ ((data & RegExpFlag::Unicode) && (data & RegExpFlag::UnicodeSets))) {
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "regexp");
return false;
=====================================
js/src/wasm/WasmRealm.cpp
=====================================
@@ -16,6 +16,7 @@
#include "wasm/WasmRealm.h"
+#include "gc/Marking.h"
#include "vm/GlobalObject.h"
#include "vm/Realm.h"
#include "wasm/WasmDebug.h"
@@ -111,6 +112,19 @@ void wasm::Realm::unregisterInstance(Instance& instance) {
}
}
+void wasm::Realm::traceWeakInstances() {
+ // Registration/unregistration of instances_ is tied to Instance lifetime, so
+ // an instance whose owning object is about to be finalized is still present
+ // here until ~Instance runs. Remove such entries now, at the start of zone
+ // sweeping, because the instances() read barrier that otherwise protects
+ // readers is a no-op once the zone is being swept. erase order is preserved,
+ // so the pointer-sorted invariant used by BinarySearchIf holds.
+ instances_.eraseIf([](Instance* instance) {
+ return js::gc::IsAboutToBeFinalizedUnbarriered(
+ instance->objectUnbarriered());
+ });
+}
+
void wasm::Realm::ensureProfilingLabels(bool profilingEnabled) {
for (Instance* instance : instances_) {
instance->ensureProfilingLabels(profilingEnabled);
=====================================
js/src/wasm/WasmRealm.h
=====================================
@@ -51,10 +51,17 @@ class Realm {
// Return a vector of all live instances in the realm. The lifetime of
// these Instances is determined by their owning WasmInstanceObject.
// Note that accessing instances()[i]->object() triggers a read barrier
- // since instances() is effectively a weak list.
+ // since instances() is effectively a weak list. This read barrier is only
+ // effective while the owning zone is being marked; traceWeakInstances()
+ // prunes dying entries at the start of sweeping so that the list never
+ // exposes an about-to-be-finalized instance to the mutator.
const InstanceVector& instances() const { return instances_; }
+ // Remove instances whose owning object is about to be finalized. Called at
+ // the start of zone sweeping, when the instances() read barrier is a no-op.
+ void traceWeakInstances();
+
// Ensure all Instances in this Realm have profiling labels created.
void ensureProfilingLabels(bool profilingEnabled);
=====================================
layout/generic/nsSplittableFrame.cpp
=====================================
@@ -9,6 +9,7 @@
#include "nsSplittableFrame.h"
+#include "mozilla/DebugOnly.h"
#include "mozilla/ReflowInput.h"
#include "nsContainerFrame.h"
#include "nsFieldSetFrame.h"
@@ -214,7 +215,7 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
if (oldCachedFirstContinuation != newFirstContinuation) {
// Update the first-continuation cache for us and our next-continuations.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
f->mFirstContinuation = newFirstContinuation;
}
}
@@ -227,7 +228,7 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
// behavior when a frame list is destroyed from the front. To avoid that
// pathological behavior, we simply purge the cached values.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
f->mFirstContinuation = nullptr;
}
}
@@ -239,22 +240,41 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
if (oldCachedFirstInFlow != newFirstInFlow) {
// Update the first-in-flow cache for us and our next-in-flows.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
f->mFirstInFlow = newFirstInFlow;
}
}
} else {
- // We become the new first-in-flow due to our prev-in-flow being removed.
- if (oldCachedFirstInFlow) {
- // It's tempting to update the first-in-flow cache for our
- // next-in-flows here, but that would result in overall O(n^2)
- // behavior when a frame list is destroyed from the front. To avoid that
- // pathological behavior, we simply purge the cached values.
+ if (GetPrevContinuation()) {
+ // We become the new first-in-flow after changing from fluid to non-fluid.
+ // Update the stale first-in-flow cache for us and all next-in-flows.
+ //
+ // Note that this has no counterpart in the above mFirstContinuation cache
+ // since GetPrevContinuation() does not depend on the
+ // NS_FRAME_IS_FLUID_CONTINUATION bit.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
- f->mFirstInFlow = nullptr;
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f->mFirstInFlow = this;
+ }
+ } else {
+ // We become the new first-in-flow due to our prev-in-flow being removed.
+ if (oldCachedFirstInFlow) {
+ // It's tempting to update the first-in-flow cache for our
+ // next-in-flows here, but that would result in overall O(n^2)
+ // behavior when a frame list is destroyed from the front. To avoid that
+ // pathological behavior, we simply purge the cached values.
+ for (nsSplittableFrame* f = this; f;
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f->mFirstInFlow = nullptr;
+ }
}
}
+
+ DebugOnly<nsSplittableFrame*> nextInFlow =
+ static_cast<nsSplittableFrame*>(GetNextInFlow());
+ MOZ_ASSERT(!nextInFlow || !nextInFlow->mFirstInFlow ||
+ nextInFlow->mFirstInFlow == this,
+ "Our next-in-flow caches a stale first-in-flow!");
}
}
=====================================
testing/web-platform/tests/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html
=====================================
@@ -0,0 +1,194 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=2053578">
+
+<!-- The operations in <script> are generated from one of the runs from
+ the original testcase (Bug 2053578 Comment 5) that triggers
+ the assertion. -->
+
+<body></body>
+
+<script>
+var n1 = document.createElement("div");
+n1.style.width = "50px";
+var n2 = document.createElement("bdo");
+n2.setAttribute("dir", "rtl");
+var n3 = document.createTextNode("كلمة");
+n2.appendChild(n3);
+n1.appendChild(n2);
+var n4 = document.createElement("em");
+var n5 = document.createElement("span");
+var n6 = document.createElement("bdo");
+var n7 = document.createTextNode("word m mix");
+n6.appendChild(n7);
+n5.appendChild(n6);
+var n8 = document.createTextNode("מלל");
+n5.appendChild(n8);
+var n9 = document.createElement("b");
+n9.setAttribute("dir", "ltr");
+n9.style.unicodeBidi = "bidi-override";
+var n10 = document.createTextNode("alpha");
+n9.appendChild(n10);
+var n11 = document.createTextNode("نص مرحبا كلمة");
+n9.appendChild(n11);
+var n12 = document.createTextNode("مرحبا نص نص");
+n9.appendChild(n12);
+n5.appendChild(n9);
+n4.appendChild(n5);
+var n13 = document.createElement("span");
+n13.style.unicodeBidi = "bidi-override";
+var n14 = document.createElement("bdi");
+var n15 = document.createTextNode("اختبار");
+n14.appendChild(n15);
+n13.appendChild(n14);
+var n16 = document.createElement("em");
+var n17 = document.createTextNode("בדיקה מלל m نص");
+n16.appendChild(n17);
+var n18 = document.createTextNode("كلمة");
+n16.appendChild(n18);
+var n19 = document.createTextNode("نص مرحبا كلمة");
+n16.appendChild(n19);
+n13.appendChild(n16);
+var n20 = document.createElement("span");
+var n21 = document.createTextNode("שלום מלל בדיקה שלום");
+n20.appendChild(n21);
+n13.appendChild(n20);
+n4.appendChild(n13);
+n1.appendChild(n4);
+document.body.appendChild(n1);
+var n22 = document.createElement("bdo");
+n22.style.unicodeBidi = "plaintext";
+var n23 = document.createElement("span");
+var n24 = document.createTextNode("עברית שלום مرحبا alpha");
+n23.appendChild(n24);
+n22.appendChild(n23);
+n14.style.direction = "ltr";
+var n25 = document.createTextNode("كلمة שלום");
+n2.insertBefore(n25, n3);
+n21.remove();
+n9.setAttribute("dir", "ltr");
+var n26 = document.createTextNode("בדיקה mm");
+n4.appendChild(n26);
+n12.data = "שלום עברית שלום עברית";
+n7.remove();
+n2.style.direction = "ltr";
+n1.style.width = "102px";
+var n27 = document.createTextNode("עברית mm m");
+n22.appendChild(n27);
+n22.setAttribute("dir", "rtl");
+n4.style.direction = "";
+n14.style.direction = "rtl";
+n22.insertBefore(n9, n23);
+var n28 = document.createElement("br");
+n9.insertBefore(n28, n12);
+n14.style.unicodeBidi = "embed";
+n5.insertBefore(n22, n6);
+n9.style.direction = "";
+n9.style.unicodeBidi = "bidi-override";
+n22.remove();
+var n29 = document.createTextNode("עברית שלום");
+n5.insertBefore(n29, n6);
+n18.data = "mix";
+n19.data = "alpha m word";
+n20.style.direction = "ltr";
+var n30 = document.createTextNode("mm עברית עברית نص");
+n6.appendChild(n30);
+n6.style.unicodeBidi = "isolate";
+n20.style.unicodeBidi = "embed";
+n16.setAttribute("dir", "rtl");
+n2.insertBefore(n20, n25);
+var n31 = document.createTextNode("שלום مرحبا mix mm");
+n2.insertBefore(n31, n20);
+var n32 = document.createTextNode("m בדיקה word mix");
+n20.appendChild(n32);
+n16.removeAttribute("dir");
+n16.setAttribute("dir", "auto");
+n5.remove();
+n17.remove();
+n31.data = "בדיקה mm";
+n14.style.unicodeBidi = "isolate";
+n4.remove();
+n20.style.direction = "";
+n20.style.direction = "rtl";
+n3.data = "בדיקה שלום word";
+n25.data = "mm";
+n25.data = "m mix word";
+n1.style.direction = "rtl";
+n31.data = "كلمة اختبار";
+n20.remove();
+n3.data = "שלום نص";
+n31.remove();
+n2.setAttribute("dir", "ltr");
+n2.removeAttribute("dir");
+n1.insertBefore(n4, n2);
+n19.data = "מלל word";
+n15.remove();
+n13.setAttribute("dir", "ltr");
+n25.remove();
+n2.setAttribute("dir", "ltr");
+n14.appendChild(n2);
+n3.data = "مرحبا اختبار مرحبا";
+n18.data = "שלום";
+n16.insertBefore(n20, n18);
+var n33 = document.createTextNode("كلمة نص mm");
+n2.appendChild(n33);
+n19.data = "اختبار مرحبا كلمة كلمة";
+n18.data = "mm mix mix";
+var n34 = document.createElement("br");
+n4.insertBefore(n34, n13);
+n13.style.unicodeBidi = "isolate-override";
+n13.remove();
+n34.remove();
+n26.data = "كلمة كلمة مرحبا";
+n4.setAttribute("dir", "rtl");
+n26.data = "mm word m mm";
+n26.data = "اختبار نص";
+n4.setAttribute("dir", "auto");
+var n35 = document.createTextNode("مرحبا מלל كلمة שלום");
+n4.insertBefore(n35, n26);
+n4.style.unicodeBidi = "embed";
+n4.style.direction = "";
+var n36 = document.createTextNode("كلمة mix בדיקה نص");
+n4.insertBefore(n36, n35);
+n36.remove();
+n4.removeAttribute("dir");
+n4.style.unicodeBidi = "";
+n1.style.width = "87px";
+n26.data = "שלום מלל מלל עברית";
+var n37 = document.createElement("br");
+n4.insertBefore(n37, n35);
+n35.data = "مرحبا نص اختبار";
+n4.setAttribute("dir", "rtl");
+n33.data = "word alpha mm";
+var n38 = document.createTextNode("שלום word");
+n16.insertBefore(n38, n20);
+n2.setAttribute("dir", "auto");
+n2.remove();
+n37.remove();
+var n39 = document.createTextNode("עברית اختبار בדיקה");
+n13.appendChild(n39);
+var n40 = document.createTextNode("בדיקה m نص نص");
+n13.insertBefore(n40, n14);
+var n41 = document.createElement("br");
+n20.appendChild(n41);
+n35.data = "alpha";
+n16.remove();
+n39.data = "نص mm בדיקה اختبار";
+n40.data = "mix mix word m";
+n35.data = "שלום בדיקה";
+n35.data = "m mix mm m";
+n40.data = "مرحبا اختبار";
+n13.style.unicodeBidi = "embed";
+n4.insertBefore(n16, n26);
+var n42 = document.createTextNode("نص");
+n13.insertBefore(n42, n40);
+n41.remove();
+n20.insertBefore(n2, n32);
+document.body.offsetHeight;
+n39.data = "مرحبا alpha עברית word";
+n18.data = "שלום";
+n2.setAttribute("dir", "auto");
+document.body.offsetHeight;
+n20.style.direction = "ltr";
+</script>
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/69…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/69…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-153.2.0esr-16.0-1-build2
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed new tag tor-browser-153.2.0esr-16.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. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-153.2.0esr-16.0-1] 5 commits: Bug 2045435 - Prune dying entries from wasm::Realm::instances_ during sweeping. r=jpages
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
3e75dcee by Yury Delendik at 2026-09-01T16:47:31+02:00
Bug 2045435 - Prune dying entries from wasm::Realm::instances_ during sweeping. r=jpages
wasm::Realm::instances_ is a weak list. Readers rely on the
instances()[i]->object() read barrier, but that barrier is a no-op once the
owning zone is being incrementally swept, and entries are otherwise only
removed at Instance finalization (~Instance -> unregisterInstance). So between
marking a zone's instance objects dead and finalizing them, the list could
still hand an about-to-be-finalized instance to a reader.
Prune such entries at the start of zone sweeping via a new
wasm::Realm::traceWeakInstances(), called from beginSweepingSweepGroup
alongside the other per-realm weak-collection sweeps. This makes instances_
behave like the engine's other weak collections, so it never exposes an
about-to-be-finalized instance to the mutator during sweep slices.
Differential Revision: https://phabricator.services.mozilla.com/D314032
- - - - -
3c493b1e by Ting-Yu Lin at 2026-09-01T16:47:32+02:00
Bug 2053578 - Use static_cast in nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache(). r=layout-reviewers,jfkthame
`nsSplittableFrame` is a subclass of `nsIFrame`, it is sufficient to use
`static_cast`.
Differential Revision: https://phabricator.services.mozilla.com/D315157
- - - - -
a6141d0a by Ting-Yu Lin at 2026-09-01T16:47:32+02:00
Bug 2053578 - Update first-in-flow cache when a continuation is changing from fluid to non-fluid. r=layout-reviewers,jfkthame
The original case in bug 2053578 comment 5 can reproduce an ASAN
use-after-poison with the patch bug 2053578 comment 6 applied. However, with
unpatched code, the best we can do is using a DEBUG-only assertion to catch the
error condition that detect a stale first-in-flow cache in next-in-flow.
`bidi-inline-continuation-first-in-flow.html` is generated with the help of
Claude code, and it can trigger the assertion without other fix in this patch.
Differential Revision: https://phabricator.services.mozilla.com/D315158
- - - - -
23f5704c by Iain Ireland at 2026-09-01T16:47:33+02:00
Bug 2058626: Check for mutually exclusive flags when deserializing cloned RegExp r=spidermonkey-reviewers,jonco
Differential Revision: https://phabricator.services.mozilla.com/D316904
- - - - -
b781a92b by Gela at 2026-09-01T16:47:34+02:00
Bug 2053320 - Part 1: Don't tie Nimbus tooling to `HomeActivity` UI a=RyanVM
Original Revision: https://phabricator.services.mozilla.com/D319628
Differential Revision: https://phabricator.services.mozilla.com/D320784
- - - - -
12 changed files:
- js/src/gc/GCRuntime.h
- js/src/gc/Sweeping.cpp
- + js/src/jit-test/tests/structured-clone/bug2058626.js
- js/src/vm/StructuredClone.cpp
- js/src/wasm/WasmRealm.cpp
- js/src/wasm/WasmRealm.h
- layout/generic/nsSplittableFrame.cpp
- mobile/android/fenix/app/src/main/AndroidManifest.xml
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
- + mobile/android/fenix/app/src/main/java/org/mozilla/fenix/experiments/QANimbusToolingReceiver.kt
- + mobile/android/fenix/app/src/test/java/org/mozilla/fenix/experiments/QANimbusToolingReceiverTest.kt
- + testing/web-platform/tests/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html
Changes:
=====================================
js/src/gc/GCRuntime.h
=====================================
@@ -999,6 +999,7 @@ class GCRuntime {
void updateAtomsBitmap();
void sweepCCWrappers();
void sweepRealmGlobals();
+ void sweepWasmInstances();
void sweepEmbeddingWeakPointers(JS::GCContext* gcx);
void sweepMisc();
void sweepCompressionTasks();
=====================================
js/src/gc/Sweeping.cpp
=====================================
@@ -1387,6 +1387,13 @@ void GCRuntime::sweepRealmGlobals() {
}
}
+void GCRuntime::sweepWasmInstances() {
+ for (SweepGroupRealmsIter r(this); !r.done(); r.next()) {
+ AutoSetThreadIsSweeping threadIsSweeping(r->zone());
+ r->wasm.traceWeakInstances();
+ }
+}
+
void GCRuntime::sweepMisc() {
SweepingTracer trc(rt);
for (SweepGroupRealmsIter r(this); !r.done(); r.next()) {
@@ -1743,6 +1750,11 @@ IncrementalProgress GCRuntime::beginSweepingSweepGroup(JS::GCContext* gcx,
// This must happen before updating embedding weak pointers.
sweepRealmGlobals();
+ // Prune dying wasm instances from each realm's weak instance list now, at the
+ // start of sweeping, before the mutator can observe them via the (now no-op)
+ // instances() read barrier during later incremental slices.
+ sweepWasmInstances();
+
sweepEmbeddingWeakPointers(gcx);
maybeWriteCoverageAndSpew();
=====================================
js/src/jit-test/tests/structured-clone/bug2058626.js
=====================================
@@ -0,0 +1,16 @@
+function forge(pattern, srcFlags, flagsByte) {
+ var cb = serialize(new RegExp(pattern, srcFlags), undefined, { scope: "DifferentProcess" });
+ var u8 = new Uint8Array(cb.arraybuffer);
+ for (var i = 0; i + 8 <= u8.length; i += 4) {
+ var tag = u8[i+4] | (u8[i+5] << 8) | (u8[i+6] << 16) | (u8[i+7] << 24);
+ if ((tag >>> 0) === 0xFFFF0006) { u8[i] = flagsByte; break; }
+ }
+ cb.clonebuffer = u8.buffer;
+ return deserialize(cb, { scope: "DifferentProcess" });
+}
+try {
+ var forged = forge("[\\q{abc|de}]", "v", 0x90);
+ var bad = new RegExp(forged, "u");
+ try { bad.exec("abc"); } catch {}
+ var good = new RegExp("[\\q{abc|de}]", "u");
+} catch {}
=====================================
js/src/vm/StructuredClone.cpp
=====================================
@@ -3234,7 +3234,9 @@ bool JSStructuredCloneReader::startReadUnchecked(
}
case SCTAG_REGEXP_OBJECT: {
- if ((data & RegExpFlag::AllFlags) != data) {
+ // Reject invalid flags. /u and /v are mutually exclusive.
+ if ((data & RegExpFlag::AllFlags) != data ||
+ ((data & RegExpFlag::Unicode) && (data & RegExpFlag::UnicodeSets))) {
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "regexp");
return false;
=====================================
js/src/wasm/WasmRealm.cpp
=====================================
@@ -16,6 +16,7 @@
#include "wasm/WasmRealm.h"
+#include "gc/Marking.h"
#include "vm/GlobalObject.h"
#include "vm/Realm.h"
#include "wasm/WasmDebug.h"
@@ -111,6 +112,19 @@ void wasm::Realm::unregisterInstance(Instance& instance) {
}
}
+void wasm::Realm::traceWeakInstances() {
+ // Registration/unregistration of instances_ is tied to Instance lifetime, so
+ // an instance whose owning object is about to be finalized is still present
+ // here until ~Instance runs. Remove such entries now, at the start of zone
+ // sweeping, because the instances() read barrier that otherwise protects
+ // readers is a no-op once the zone is being swept. erase order is preserved,
+ // so the pointer-sorted invariant used by BinarySearchIf holds.
+ instances_.eraseIf([](Instance* instance) {
+ return js::gc::IsAboutToBeFinalizedUnbarriered(
+ instance->objectUnbarriered());
+ });
+}
+
void wasm::Realm::ensureProfilingLabels(bool profilingEnabled) {
for (Instance* instance : instances_) {
instance->ensureProfilingLabels(profilingEnabled);
=====================================
js/src/wasm/WasmRealm.h
=====================================
@@ -51,10 +51,17 @@ class Realm {
// Return a vector of all live instances in the realm. The lifetime of
// these Instances is determined by their owning WasmInstanceObject.
// Note that accessing instances()[i]->object() triggers a read barrier
- // since instances() is effectively a weak list.
+ // since instances() is effectively a weak list. This read barrier is only
+ // effective while the owning zone is being marked; traceWeakInstances()
+ // prunes dying entries at the start of sweeping so that the list never
+ // exposes an about-to-be-finalized instance to the mutator.
const InstanceVector& instances() const { return instances_; }
+ // Remove instances whose owning object is about to be finalized. Called at
+ // the start of zone sweeping, when the instances() read barrier is a no-op.
+ void traceWeakInstances();
+
// Ensure all Instances in this Realm have profiling labels created.
void ensureProfilingLabels(bool profilingEnabled);
=====================================
layout/generic/nsSplittableFrame.cpp
=====================================
@@ -9,6 +9,7 @@
#include "nsSplittableFrame.h"
+#include "mozilla/DebugOnly.h"
#include "mozilla/ReflowInput.h"
#include "nsContainerFrame.h"
#include "nsFieldSetFrame.h"
@@ -214,7 +215,7 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
if (oldCachedFirstContinuation != newFirstContinuation) {
// Update the first-continuation cache for us and our next-continuations.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
f->mFirstContinuation = newFirstContinuation;
}
}
@@ -227,7 +228,7 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
// behavior when a frame list is destroyed from the front. To avoid that
// pathological behavior, we simply purge the cached values.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
f->mFirstContinuation = nullptr;
}
}
@@ -239,22 +240,41 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
if (oldCachedFirstInFlow != newFirstInFlow) {
// Update the first-in-flow cache for us and our next-in-flows.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
f->mFirstInFlow = newFirstInFlow;
}
}
} else {
- // We become the new first-in-flow due to our prev-in-flow being removed.
- if (oldCachedFirstInFlow) {
- // It's tempting to update the first-in-flow cache for our
- // next-in-flows here, but that would result in overall O(n^2)
- // behavior when a frame list is destroyed from the front. To avoid that
- // pathological behavior, we simply purge the cached values.
+ if (GetPrevContinuation()) {
+ // We become the new first-in-flow after changing from fluid to non-fluid.
+ // Update the stale first-in-flow cache for us and all next-in-flows.
+ //
+ // Note that this has no counterpart in the above mFirstContinuation cache
+ // since GetPrevContinuation() does not depend on the
+ // NS_FRAME_IS_FLUID_CONTINUATION bit.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
- f->mFirstInFlow = nullptr;
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f->mFirstInFlow = this;
+ }
+ } else {
+ // We become the new first-in-flow due to our prev-in-flow being removed.
+ if (oldCachedFirstInFlow) {
+ // It's tempting to update the first-in-flow cache for our
+ // next-in-flows here, but that would result in overall O(n^2)
+ // behavior when a frame list is destroyed from the front. To avoid that
+ // pathological behavior, we simply purge the cached values.
+ for (nsSplittableFrame* f = this; f;
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f->mFirstInFlow = nullptr;
+ }
}
}
+
+ DebugOnly<nsSplittableFrame*> nextInFlow =
+ static_cast<nsSplittableFrame*>(GetNextInFlow());
+ MOZ_ASSERT(!nextInFlow || !nextInFlow->mFirstInFlow ||
+ nextInFlow->mFirstInFlow == this,
+ "Our next-in-flow caches a stale first-in-flow!");
}
}
=====================================
mobile/android/fenix/app/src/main/AndroidManifest.xml
=====================================
@@ -820,6 +820,16 @@
<action android:name="org.mozilla.fenix.TRIGGER_MESSAGE_WORKER" />
</intent-filter>
</receiver>
+
+ <receiver
+ android:name="org.mozilla.fenix.experiments.QANimbusToolingReceiver"
+ android:exported="true"
+ android:enabled="true"
+ android:permission="android.permission.DUMP">
+ <intent-filter>
+ <action android:name="org.mozilla.fenix.NIMBUS_TOOLING" />
+ </intent-filter>
+ </receiver>
</application>
</manifest>
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
=====================================
@@ -99,7 +99,6 @@ import mozilla.components.support.utils.toSafeIntent
import mozilla.components.support.webextensions.WebExtensionOptionsPageObserver
import mozilla.components.support.webextensions.WebExtensionPopupObserver
import mozilla.telemetry.glean.private.NoExtras
-import org.mozilla.experiments.nimbus.initializeTooling
import org.mozilla.fenix.GleanMetrics.AppIcon
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.Metrics
@@ -463,9 +462,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, Crash
}
}
}
-
- // Setup nimbus-cli tooling. This is a NOOP when launching normally.
- components.nimbus.sdk.initializeTooling(applicationContext, intent)
components.strictMode.attachListenerToDisablePenaltyDeath(supportFragmentManager)
MarkersFragmentLifecycleCallbacks.register(supportFragmentManager, components.core.engine)
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/experiments/QANimbusToolingReceiver.kt
=====================================
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.experiments
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import mozilla.components.support.base.log.logger.Logger
+import org.mozilla.experiments.nimbus.initializeTooling
+import org.mozilla.fenix.ext.components
+
+private val logger = Logger("QANimbusToolingReceiver")
+
+/**
+ * Receiver triggered on demand via `nimbus-cli` to manually enroll into Nimbus experiments.
+ *
+ * ```
+ * adb shell am broadcast -a org.mozilla.fenix.NIMBUS_TOOLING \
+ * -p org.mozilla.fenix
+ * ```
+ *
+ * `-p org.mozilla.fenix` is the package name, so adjust that value for release/beta/nightly/debug.
+ *
+ * @param dispatcher the [CoroutineDispatcher] the tooling commands are applied on.
+ */
+class QANimbusToolingReceiver(private val dispatcher: CoroutineDispatcher = Dispatchers.IO) : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action != ACTION_NIMBUS_TOOLING) return
+
+ logger.info("Enqueueing QANimbusToolingReceiver via debug trigger")
+
+ val applicationContext = context.applicationContext
+
+ val pendingResult: PendingResult? = goAsync()
+ CoroutineScope(dispatcher).launch {
+ try {
+ applicationContext.components.nimbus.sdk.initializeTooling(
+ applicationContext,
+ intent,
+ )
+ } finally {
+ logger.info("Nimbus tooling command processed")
+ pendingResult?.finish()
+ }
+ }
+ }
+
+ companion object {
+ const val ACTION_NIMBUS_TOOLING = "org.mozilla.fenix.NIMBUS_TOOLING"
+ }
+}
=====================================
mobile/android/fenix/app/src/test/java/org/mozilla/fenix/experiments/QANimbusToolingReceiverTest.kt
=====================================
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.experiments
+
+import android.content.Context
+import android.content.Intent
+import io.mockk.every
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import mozilla.components.support.test.robolectric.testContext
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mozilla.fenix.experiments.QANimbusToolingReceiver.Companion.ACTION_NIMBUS_TOOLING
+import org.mozilla.fenix.ext.components
+import org.mozilla.fenix.nimbus.TestNimbusApi
+import org.robolectric.RobolectricTestRunner
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricTestRunner::class)
+class QANimbusToolingReceiverTest {
+
+ private val nimbusApi = FakeNimbusApi(testContext)
+ private val receiver = QANimbusToolingReceiver(UnconfinedTestDispatcher())
+
+ @Before
+ fun setUp() {
+ every { testContext.components.nimbus.sdk } returns nimbusApi
+ }
+
+ @Test
+ fun `GIVEN a tooling command WHEN the tooling action is received THEN the command is applied`() {
+ receiver.onReceive(testContext, toolingIntent(ACTION_NIMBUS_TOOLING))
+
+ assertEquals(EXPERIMENTS, nimbusApi.appliedExperiments)
+ assertEquals(false, nimbusApi.fetchEnabled)
+ assertTrue(nimbusApi.databaseReset)
+ assertTrue(nimbusApi.stateDumped)
+ }
+
+ @Test
+ fun `GIVEN a tooling command WHEN another action is received THEN the command is ignored`() {
+ receiver.onReceive(testContext, toolingIntent("org.mozilla.fenix.ACTION_PRINT"))
+
+ assertNull(nimbusApi.appliedExperiments)
+ assertNull(nimbusApi.fetchEnabled)
+ assertFalse(nimbusApi.databaseReset)
+ assertFalse(nimbusApi.stateDumped)
+ }
+
+ @Test
+ fun `GIVEN no tooling command WHEN the tooling action is received THEN nothing is applied`() {
+ receiver.onReceive(testContext, Intent(ACTION_NIMBUS_TOOLING))
+
+ assertNull(nimbusApi.appliedExperiments)
+ assertNull(nimbusApi.fetchEnabled)
+ assertFalse(nimbusApi.databaseReset)
+ assertFalse(nimbusApi.stateDumped)
+ }
+
+ @Test
+ fun `GIVEN a tooling command without the version extra WHEN the tooling action is received THEN nothing is applied`() {
+ val intent = toolingIntent(ACTION_NIMBUS_TOOLING).apply { removeExtra("version") }
+
+ receiver.onReceive(testContext, intent)
+
+ assertNull(nimbusApi.appliedExperiments)
+ assertFalse(nimbusApi.stateDumped)
+ }
+
+ private fun toolingIntent(action: String) =
+ Intent(action).apply {
+ putExtra("nimbus-cli", null as String?)
+ putExtra("version", 1)
+ putExtra("experiments", EXPERIMENTS)
+ putExtra("reset-db", true)
+ putExtra("log-state", true)
+ }
+
+ private class FakeNimbusApi(context: Context) : TestNimbusApi(context) {
+ var appliedExperiments: String? = null
+ var fetchEnabled: Boolean? = null
+ var databaseReset = false
+ var stateDumped = false
+
+ override fun applyLocalExperiments(experimentsJson: String): Job {
+ appliedExperiments = experimentsJson
+ return completedJob()
+ }
+
+ override fun resetEnrollmentsDatabase(): Job {
+ databaseReset = true
+ return completedJob()
+ }
+
+ override fun setFetchEnabled(enabled: Boolean) {
+ fetchEnabled = enabled
+ }
+
+ override fun dumpStateToLog() {
+ stateDumped = true
+ }
+
+ private fun completedJob() = Job().apply { complete() }
+ }
+
+ companion object {
+ private const val EXPERIMENTS = """{"data":[]}"""
+ }
+}
=====================================
testing/web-platform/tests/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html
=====================================
@@ -0,0 +1,194 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=2053578">
+
+<!-- The operations in <script> are generated from one of the runs from
+ the original testcase (Bug 2053578 Comment 5) that triggers
+ the assertion. -->
+
+<body></body>
+
+<script>
+var n1 = document.createElement("div");
+n1.style.width = "50px";
+var n2 = document.createElement("bdo");
+n2.setAttribute("dir", "rtl");
+var n3 = document.createTextNode("كلمة");
+n2.appendChild(n3);
+n1.appendChild(n2);
+var n4 = document.createElement("em");
+var n5 = document.createElement("span");
+var n6 = document.createElement("bdo");
+var n7 = document.createTextNode("word m mix");
+n6.appendChild(n7);
+n5.appendChild(n6);
+var n8 = document.createTextNode("מלל");
+n5.appendChild(n8);
+var n9 = document.createElement("b");
+n9.setAttribute("dir", "ltr");
+n9.style.unicodeBidi = "bidi-override";
+var n10 = document.createTextNode("alpha");
+n9.appendChild(n10);
+var n11 = document.createTextNode("نص مرحبا كلمة");
+n9.appendChild(n11);
+var n12 = document.createTextNode("مرحبا نص نص");
+n9.appendChild(n12);
+n5.appendChild(n9);
+n4.appendChild(n5);
+var n13 = document.createElement("span");
+n13.style.unicodeBidi = "bidi-override";
+var n14 = document.createElement("bdi");
+var n15 = document.createTextNode("اختبار");
+n14.appendChild(n15);
+n13.appendChild(n14);
+var n16 = document.createElement("em");
+var n17 = document.createTextNode("בדיקה מלל m نص");
+n16.appendChild(n17);
+var n18 = document.createTextNode("كلمة");
+n16.appendChild(n18);
+var n19 = document.createTextNode("نص مرحبا كلمة");
+n16.appendChild(n19);
+n13.appendChild(n16);
+var n20 = document.createElement("span");
+var n21 = document.createTextNode("שלום מלל בדיקה שלום");
+n20.appendChild(n21);
+n13.appendChild(n20);
+n4.appendChild(n13);
+n1.appendChild(n4);
+document.body.appendChild(n1);
+var n22 = document.createElement("bdo");
+n22.style.unicodeBidi = "plaintext";
+var n23 = document.createElement("span");
+var n24 = document.createTextNode("עברית שלום مرحبا alpha");
+n23.appendChild(n24);
+n22.appendChild(n23);
+n14.style.direction = "ltr";
+var n25 = document.createTextNode("كلمة שלום");
+n2.insertBefore(n25, n3);
+n21.remove();
+n9.setAttribute("dir", "ltr");
+var n26 = document.createTextNode("בדיקה mm");
+n4.appendChild(n26);
+n12.data = "שלום עברית שלום עברית";
+n7.remove();
+n2.style.direction = "ltr";
+n1.style.width = "102px";
+var n27 = document.createTextNode("עברית mm m");
+n22.appendChild(n27);
+n22.setAttribute("dir", "rtl");
+n4.style.direction = "";
+n14.style.direction = "rtl";
+n22.insertBefore(n9, n23);
+var n28 = document.createElement("br");
+n9.insertBefore(n28, n12);
+n14.style.unicodeBidi = "embed";
+n5.insertBefore(n22, n6);
+n9.style.direction = "";
+n9.style.unicodeBidi = "bidi-override";
+n22.remove();
+var n29 = document.createTextNode("עברית שלום");
+n5.insertBefore(n29, n6);
+n18.data = "mix";
+n19.data = "alpha m word";
+n20.style.direction = "ltr";
+var n30 = document.createTextNode("mm עברית עברית نص");
+n6.appendChild(n30);
+n6.style.unicodeBidi = "isolate";
+n20.style.unicodeBidi = "embed";
+n16.setAttribute("dir", "rtl");
+n2.insertBefore(n20, n25);
+var n31 = document.createTextNode("שלום مرحبا mix mm");
+n2.insertBefore(n31, n20);
+var n32 = document.createTextNode("m בדיקה word mix");
+n20.appendChild(n32);
+n16.removeAttribute("dir");
+n16.setAttribute("dir", "auto");
+n5.remove();
+n17.remove();
+n31.data = "בדיקה mm";
+n14.style.unicodeBidi = "isolate";
+n4.remove();
+n20.style.direction = "";
+n20.style.direction = "rtl";
+n3.data = "בדיקה שלום word";
+n25.data = "mm";
+n25.data = "m mix word";
+n1.style.direction = "rtl";
+n31.data = "كلمة اختبار";
+n20.remove();
+n3.data = "שלום نص";
+n31.remove();
+n2.setAttribute("dir", "ltr");
+n2.removeAttribute("dir");
+n1.insertBefore(n4, n2);
+n19.data = "מלל word";
+n15.remove();
+n13.setAttribute("dir", "ltr");
+n25.remove();
+n2.setAttribute("dir", "ltr");
+n14.appendChild(n2);
+n3.data = "مرحبا اختبار مرحبا";
+n18.data = "שלום";
+n16.insertBefore(n20, n18);
+var n33 = document.createTextNode("كلمة نص mm");
+n2.appendChild(n33);
+n19.data = "اختبار مرحبا كلمة كلمة";
+n18.data = "mm mix mix";
+var n34 = document.createElement("br");
+n4.insertBefore(n34, n13);
+n13.style.unicodeBidi = "isolate-override";
+n13.remove();
+n34.remove();
+n26.data = "كلمة كلمة مرحبا";
+n4.setAttribute("dir", "rtl");
+n26.data = "mm word m mm";
+n26.data = "اختبار نص";
+n4.setAttribute("dir", "auto");
+var n35 = document.createTextNode("مرحبا מלל كلمة שלום");
+n4.insertBefore(n35, n26);
+n4.style.unicodeBidi = "embed";
+n4.style.direction = "";
+var n36 = document.createTextNode("كلمة mix בדיקה نص");
+n4.insertBefore(n36, n35);
+n36.remove();
+n4.removeAttribute("dir");
+n4.style.unicodeBidi = "";
+n1.style.width = "87px";
+n26.data = "שלום מלל מלל עברית";
+var n37 = document.createElement("br");
+n4.insertBefore(n37, n35);
+n35.data = "مرحبا نص اختبار";
+n4.setAttribute("dir", "rtl");
+n33.data = "word alpha mm";
+var n38 = document.createTextNode("שלום word");
+n16.insertBefore(n38, n20);
+n2.setAttribute("dir", "auto");
+n2.remove();
+n37.remove();
+var n39 = document.createTextNode("עברית اختبار בדיקה");
+n13.appendChild(n39);
+var n40 = document.createTextNode("בדיקה m نص نص");
+n13.insertBefore(n40, n14);
+var n41 = document.createElement("br");
+n20.appendChild(n41);
+n35.data = "alpha";
+n16.remove();
+n39.data = "نص mm בדיקה اختبار";
+n40.data = "mix mix word m";
+n35.data = "שלום בדיקה";
+n35.data = "m mix mm m";
+n40.data = "مرحبا اختبار";
+n13.style.unicodeBidi = "embed";
+n4.insertBefore(n16, n26);
+var n42 = document.createTextNode("نص");
+n13.insertBefore(n42, n40);
+n41.remove();
+n20.insertBefore(n2, n32);
+document.body.offsetHeight;
+n39.data = "مرحبا alpha עברית word";
+n18.data = "שלום";
+n2.setAttribute("dir", "auto");
+document.body.offsetHeight;
+n20.style.direction = "ltr";
+</script>
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/1f8e95…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/1f8e95…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-153.2.0esr-16.0-1] fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
1f8e95ba by hackademix at 2026-09-01T16:39:51+02:00
fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser
TB 45264: Use parent process for TorConnect redirections
- - - - -
2 changed files:
- browser/components/torconnect/TorConnectChild.sys.mjs
- browser/components/torconnect/TorConnectParent.sys.mjs
Changes:
=====================================
browser/components/torconnect/TorConnectChild.sys.mjs
=====================================
@@ -60,12 +60,7 @@ export class TorConnectChild extends RemotePageChild {
console.error(`Invalid redirect URL "${redirect}"`);
}
- // Replace the destination to prevent "about:torconnect" entering the
- // history.
- // NOTE: This is done here, in the window actor, rather than in content
- // because we have the privilege to redirect to a "chrome:" uri here (for
- // when the HomePage is set to be blank).
- this.contentWindow.location.replace(replaceURI);
+ this.sendQuery("torconnect:redirect", replaceURI);
}
actorCreated() {
=====================================
browser/components/torconnect/TorConnectParent.sys.mjs
=====================================
@@ -112,6 +112,16 @@ export class TorConnectParent extends JSWindowActorParent {
return Promise.resolve(
TorConnectParent.fixupURIs(lazy.HomePage.get())[0]
);
+ case "torconnect:redirect":
+ this.browsingContext.top.embedderElement.loadURI(
+ Services.io.newURI(message.data),
+ {
+ triggeringPrincipal:
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY,
+ }
+ );
+ break;
case "torconnect:set-quickstart":
TorConnect.quickstart = message.data;
break;
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1f8e95b…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1f8e95b…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-153.2.0esr-16.0-1] 2 commits: fixup! TB 40562: Added Tor Browser preferences to 000-tor-browser.js
by brizental (@brizental) 01 Sep '26
by brizental (@brizental) 01 Sep '26
01 Sep '26
brizental pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
825cf57f by Beatriz Rizental at 2026-09-01T11:26:22-03:00
fixup! TB 40562: Added Tor Browser preferences to 000-tor-browser.js
Bug 44155: Set local network access prefs to block it all.
This is defense-in-depth, since other mechanisms already block it.
- - - - -
7abdb578 by Beatriz Rizental at 2026-09-01T11:26:23-03:00
fixup! [android] Disable features and functionality
Bug 44155: Hide local network access toggles from settings.
These toggles are no-op already, since local network access is always
disabled throuh other mechanisms.
- - - - -
4 changed files:
- browser/app/profile/000-tor-browser.js
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SiteSettingsFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
Changes:
=====================================
browser/app/profile/000-tor-browser.js
=====================================
@@ -23,6 +23,10 @@ pref("network.http.max-persistent-connections-per-proxy", 256);
// Disable DNS over HTTPS. Set to explicitly off MODE_TRROFF = 5.
// See tor-browser#41906.
pref("network.trr.mode", 5, locked);
+// tor-browser#44155: Block Local Network Access (LNA)
+pref("network.lna.enabled", true);
+pref("network.lna.blocking", true);
+pref("network.lna.block_trackers", true);
// Treat .onions as secure
pref("dom.securecontext.allowlist_onions", true);
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt
=====================================
@@ -79,8 +79,6 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat(), Sys
@VisibleForTesting
internal fun bindCategoryPhoneFeatures() {
- val settings = provideSettings()
-
initPhoneFeature(CAMERA)
initPhoneFeature(LOCATION)
initPhoneFeature(MICROPHONE)
@@ -89,8 +87,10 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat(), Sys
initPhoneFeature(CROSS_ORIGIN_STORAGE_ACCESS)
initPhoneFeature(MEDIA_KEY_SYSTEM_ACCESS)
initAutoplayFeature()
- initPhoneFeature(LOCAL_DEVICE_ACCESS, visible = settings.isLnaFeatureEnabled)
- initPhoneFeature(LOCAL_NETWORK_ACCESS, visible = settings.isLnaFeatureEnabled)
+ // tor-browser#44155: Don't show local network / device toggles.
+ // We want it always in the same state: blocked.
+ initPhoneFeature(LOCAL_DEVICE_ACCESS, visible = false)
+ initPhoneFeature(LOCAL_NETWORK_ACCESS, visible = false)
bindClearPermissionsButton()
}
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SiteSettingsFragment.kt
=====================================
@@ -92,7 +92,9 @@ class SiteSettingsFragment : PreferenceFragmentCompat(), SystemInsetsPaddedFragm
// not need to be bound
.filter { it != PhoneFeature.AUTOPLAY_INAUDIBLE }
.excludeFeatures(
- condition = { !requireComponents.settings.isLnaFeatureEnabled },
+ // tor-browser#44155: Don't show local network / device toggles.
+ // We want it always in the same state: blocked.
+ condition = { true },
features = setOf(
PhoneFeature.LOCAL_DEVICE_ACCESS,
PhoneFeature.LOCAL_NETWORK_ACCESS,
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
=====================================
@@ -1009,18 +1009,12 @@ class Settings(
/**
* Indicates if the request blocking feature for Local Network / Local Device Access blocking is enabled.
*/
- var isLnaBlockingEnabled by booleanPreference(
- key = appContext.getPreferenceKey(R.string.pref_key_enable_lna_blocking_enabled),
- default = { FxNimbus.features.lnaBlocking.value().blocking || Config.channel.isNightlyOrDebug },
- )
+ var isLnaBlockingEnabled: Boolean = true
/**
* Indicates if the Local Network / Local Device Access tracker blocking feature is enabled.
*/
- var isLnaTrackerBlockingEnabled by booleanPreference(
- key = appContext.getPreferenceKey(R.string.pref_key_enable_lna_tracker_blocking_enabled),
- default = { FxNimbus.features.lnaBlocking.value().blockTrackers },
- )
+ var isLnaTrackerBlockingEnabled: Boolean = true
/**
* Indicates if the overall Local Network / Local Device Access feature is enabled.
@@ -1029,10 +1023,7 @@ class Settings(
* allowing requests that originate from remote origins targeting either localhost addresses or
* local network addresses.
*/
- var isLnaFeatureEnabled by booleanPreference(
- key = appContext.getPreferenceKey(R.string.pref_key_enable_lna_feature_enabled),
- default = { FxNimbus.features.lnaBlocking.value().enabled || Config.channel.isNightlyOrDebug },
- )
+ val isLnaFeatureEnabled: Boolean = true
/**
* Indicates whether isolated content processes are enabled or not.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/12016c…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/12016c…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-153.2.0esr-16.0-1] 2 commits: fixup! TB 44806: Implement the tor integration in Rust.
by Pier Angelo Vendrame (@pierov) 01 Sep '26
by Pier Angelo Vendrame (@pierov) 01 Sep '26
01 Sep '26
Pier Angelo Vendrame pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
0e74ee9b by Elena at 2026-08-31T17:50:01+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Implemented string unescape.
- - - - -
12016c7b by Elena at 2026-08-31T17:50:01+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Implemented the string escaping.
- - - - -
4 changed files:
- + toolkit/components/tor-integration/tor_provider/src/ctor/controller/escape.rs
- + toolkit/components/tor-integration/tor_provider/src/ctor/controller/mod.rs
- + toolkit/components/tor-integration/tor_provider/src/ctor/controller/unescape.rs
- toolkit/components/tor-integration/tor_provider/src/ctor/mod.rs
Changes:
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/controller/escape.rs
=====================================
@@ -0,0 +1,66 @@
+// Licensed under the Apache License, Version 2.0,
+// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
+// <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+use std::fmt::Write;
+
+/// Adaptation of tor's esc_for_log (src/lib/log/escape.c).
+///
+/// However, we prefer the hex representation to the octal one.
+pub fn tor_escape_into<T: AsRef<[u8]>>(buf: T, dest: &mut String) {
+ dest.reserve(buf.as_ref().len() + 2);
+ dest.push('"');
+ for b in buf.as_ref() {
+ match *b {
+ b'\'' | b'"' | b'\\' => {
+ dest.push('\\');
+ dest.push(*b as char);
+ }
+ b'\n' => {
+ dest.push_str("\\n");
+ }
+ b'\t' => {
+ dest.push_str("\\t");
+ }
+ b'\r' => {
+ dest.push_str("\\r");
+ }
+ 0x20..=0x7E => {
+ dest.push(*b as char);
+ }
+ _ => {
+ write!(dest, "\\x{:02X}", b).expect("String::write_str always returns Ok(()).");
+ }
+ }
+ }
+ dest.push('"');
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn tor_escape<T: AsRef<[u8]>>(buf: T) -> String {
+ let mut dest = String::new();
+ tor_escape_into(buf, &mut dest);
+ dest
+ }
+
+ #[test]
+ fn simple() {
+ assert_eq!(tor_escape("test"), "\"test\"");
+ }
+
+ #[test]
+ fn empty() {
+ assert_eq!(tor_escape(""), "\"\"");
+ }
+
+ #[test]
+ fn escaped() {
+ assert_eq!(tor_escape("'\"\\\r\n\t"), "\"\\'\\\"\\\\\\r\\n\\t\"");
+ assert_eq!(tor_escape("\0"), "\"\\x00\"");
+ assert_eq!(tor_escape("\u{1F9C5}"), "\"\\xF0\\x9F\\xA7\\x85\"");
+ }
+}
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/controller/mod.rs
=====================================
@@ -0,0 +1,10 @@
+// Licensed under the Apache License, Version 2.0,
+// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
+// <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+mod escape;
+mod unescape;
+
+use escape::*;
+use unescape::*;
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/controller/unescape.rs
=====================================
@@ -0,0 +1,290 @@
+// Licensed under the Apache License, Version 2.0,
+// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
+// <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+use std::borrow::Cow;
+use thiserror::Error;
+
+#[derive(Error, Debug, PartialEq, Eq)]
+pub enum UnescapeError {
+ #[error("space in an unquoted string")]
+ SpaceInUnquotedString,
+ #[error("unterminated escape sequence or string")]
+ Unterminated,
+ #[error("found an unescaped quote")]
+ UnescapedQuote,
+ #[error("found an invalid escape sequence")]
+ InvalidEscape,
+ #[error("an octal-escaped value exceeds 255")]
+ OctalOverflow,
+}
+
+/// Adaptation of tor's unescape_string (src/lib/encoding/cstring.c).
+/// The main difference is that we allow unquoted strings.
+pub fn tor_unescape<'a>(buf: &'a [u8]) -> Result<Cow<'a, [u8]>, UnescapeError> {
+ match buf.first() {
+ Some(b'"') => {
+ // Quoted string, consume the item and continue with the function.
+ }
+ _ => {
+ if buf.contains(&b' ') {
+ return Err(UnescapeError::SpaceInUnquotedString);
+ }
+ return Ok(Cow::Borrowed(buf));
+ }
+ }
+
+ if buf.len() == 1 || buf.last() != Some(&b'\"') {
+ return Err(UnescapeError::Unterminated);
+ }
+
+ // This is used only if we did an actual change.
+ let mut out = None;
+
+ // We already consumed the initial quote.
+ let mut i = 1;
+ while i < buf.len() {
+ let c = buf[i];
+ i += 1;
+ match c {
+ b'\"' => {
+ if i != buf.len() {
+ return Err(UnescapeError::UnescapedQuote);
+ }
+ return Ok(out
+ .map(|b| Cow::Owned(b))
+ .unwrap_or_else(|| Cow::Borrowed(&buf[1..buf.len() - 1])));
+ }
+ 0 | b'\n' => return Err(UnescapeError::Unterminated),
+ b'\\' => {
+ let esc = buf.get(i).ok_or(UnescapeError::Unterminated)?;
+ i += 1;
+ let out = out.get_or_insert_with(|| {
+ let mut b = Vec::with_capacity(buf.len() - 2);
+ b.extend(&buf[1..i - 2]);
+ b
+ });
+ out.push(match esc {
+ b'n' => Ok(b'\n'),
+ b'r' => Ok(b'\r'),
+ b't' => Ok(b'\t'),
+ b'x' | b'X' => unescape_hex(buf, &mut i),
+ b'0'..=b'7' => unescape_octal(buf, &mut i),
+ b'\'' | b'"' | b'\\' => Ok(*esc),
+ _ => Err(UnescapeError::InvalidEscape),
+ }?);
+ }
+ _ => {
+ if let Some(b) = out.as_mut() {
+ b.push(c);
+ }
+ }
+ }
+ }
+
+ // If we fall out of the loop we never saw a closing quote.
+ Err(UnescapeError::Unterminated)
+}
+
+fn unescape_hex(buf: &[u8], i: &mut usize) -> Result<u8, UnescapeError> {
+ // The C code expects exactly two hex digits.
+ *i += 2;
+ str::from_utf8(&buf[*i - 2..*i])
+ .ok()
+ .and_then(|s| u8::from_str_radix(s, 16).ok())
+ .ok_or(UnescapeError::InvalidEscape)
+}
+
+fn unescape_octal(buf: &[u8], i: &mut usize) -> Result<u8, UnescapeError> {
+ *i -= 1;
+ let mut len = 1;
+ while (*i + len) < buf.len() && len < 3 {
+ let b = buf[*i + len];
+ if b >= b'0' && b <= b'7' {
+ len += 1;
+ } else {
+ break;
+ }
+ }
+
+ *i += len;
+ // This should never fail with InvalidEscape, as we have just validated the
+ // octal values.
+ str::from_utf8(&buf[*i - len..*i])
+ .ok()
+ .and_then(|s| u16::from_str_radix(s, 8).ok())
+ .ok_or(UnescapeError::InvalidEscape)
+ .and_then(|v| v.try_into().map_err(|_| UnescapeError::OctalOverflow))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn simple() {
+ assert_eq!(&*tor_unescape(b"test").unwrap(), b"test");
+ assert_eq!(&*tor_unescape(b"\"test\"").unwrap(), b"test");
+ }
+
+ #[test]
+ fn quote_in_unquoted() {
+ assert_eq!(&*tor_unescape(b"te\"st").unwrap(), b"te\"st");
+ assert_eq!(&*tor_unescape(b"test\"").unwrap(), b"test\"");
+ }
+
+ #[test]
+ fn empty() {
+ assert_eq!(&*tor_unescape(b"").unwrap(), &[]);
+ assert_eq!(&*tor_unescape(b"\"\"").unwrap(), &[]);
+ }
+
+ #[test]
+ fn unescape_simple() {
+ assert_eq!(&*tor_unescape(b"\"\\n\"").unwrap(), b"\n");
+ assert_eq!(&*tor_unescape(b"\"\\r\"").unwrap(), b"\r");
+ assert_eq!(&*tor_unescape(b"\"\\t\"").unwrap(), b"\t");
+ assert_eq!(&*tor_unescape(b"\"\\r\\n\"").unwrap(), b"\r\n");
+ assert_eq!(&*tor_unescape(b"\"'\"").unwrap(), b"'");
+ assert_eq!(&*tor_unescape(b"\"\\'\"").unwrap(), b"'");
+ assert_eq!(&*tor_unescape(b"\"\\\"\"").unwrap(), b"\"");
+ assert_eq!(&*tor_unescape(b"\"\\\\\"").unwrap(), b"\\");
+ }
+
+ #[test]
+ fn unescape_hex() {
+ assert_eq!(&*tor_unescape(b"\"\\x20\"").unwrap(), b" ");
+ assert_eq!(&*tor_unescape(b"\"\\x20test\"").unwrap(), b" test");
+ assert_eq!(&*tor_unescape(b"\"test\\x20\"").unwrap(), b"test ");
+ assert_eq!(&*tor_unescape(b"\"te\\x20st\"").unwrap(), b"te st");
+
+ assert_eq!(&*tor_unescape(b"\"\\X20\"").unwrap(), b" ");
+ assert_eq!(&*tor_unescape(b"\"\\X20test\"").unwrap(), b" test");
+ assert_eq!(&*tor_unescape(b"\"test\\X20\"").unwrap(), b"test ");
+ assert_eq!(&*tor_unescape(b"\"te\\X20st\"").unwrap(), b"te st");
+
+ assert_eq!(&*tor_unescape(b"\"\\x00\"").unwrap(), b"\0");
+ assert_eq!(&*tor_unescape(b"\"test\\x00\"").unwrap(), b"test\0");
+ assert_eq!(&*tor_unescape(b"\"\\x00test\"").unwrap(), b"\0test");
+ assert_eq!(&*tor_unescape(b"\"\\X00\"").unwrap(), b"\0");
+ assert_eq!(&*tor_unescape(b"\"TEST\\X00\"").unwrap(), b"TEST\0");
+ assert_eq!(&*tor_unescape(b"\"\\X00TEST\"").unwrap(), b"\0TEST");
+ }
+
+ #[test]
+ fn unescape_octal() {
+ assert_eq!(&*tor_unescape(b"\"\\0\"").unwrap(), b"\0");
+ assert_eq!(&*tor_unescape(b"\"\\00\"").unwrap(), b"\0");
+ assert_eq!(&*tor_unescape(b"\"\\000\"").unwrap(), b"\0");
+
+ assert_eq!(&*tor_unescape(b"\"\\2\"").unwrap(), b"\x02");
+ assert_eq!(&*tor_unescape(b"\"\\02\"").unwrap(), b"\x02");
+ assert_eq!(&*tor_unescape(b"\"\\002\"").unwrap(), b"\x02");
+
+ assert_eq!(&*tor_unescape(b"\"\\40\"").unwrap(), b" ");
+ assert_eq!(&*tor_unescape(b"\"\\040\"").unwrap(), b" ");
+
+ assert_eq!(&*tor_unescape(b"\"\\40test\"").unwrap(), b" test");
+ assert_eq!(&*tor_unescape(b"\"\\040test\"").unwrap(), b" test");
+ assert_eq!(&*tor_unescape(b"\"\\40test\\0\"").unwrap(), b" test\0");
+ }
+
+ #[test]
+ fn invalid_unicode() {
+ // Raw invalid sequence, without quotes
+ assert_eq!(*tor_unescape(b"\xF5").unwrap(), [0xF5u8]);
+ // Raw invalid sequence, with quotes
+ assert_eq!(*tor_unescape(b"\"\xF5\"").unwrap(), [0xF5u8]);
+ // Escaped invalid values, we will unescape but keep them as they are.
+ assert_eq!(*tor_unescape(b"\"\\xF5\"").unwrap(), [0xF5u8]);
+ assert_eq!(*tor_unescape(b"\"\\365\"").unwrap(), [0xF5u8]);
+ }
+
+ #[test]
+ fn unquoted_space() {
+ assert_eq!(
+ tor_unescape(b"test test").unwrap_err(),
+ UnescapeError::SpaceInUnquotedString
+ );
+ }
+
+ #[test]
+ fn unterminated() {
+ assert_eq!(
+ tor_unescape(b"\"test").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ assert_eq!(
+ tor_unescape(b"\"test\\\"").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ assert_eq!(
+ tor_unescape(b"\"test\n\"").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ assert_eq!(
+ tor_unescape(b"\"test\n").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ assert_eq!(
+ tor_unescape(b"\"test\0\"").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ assert_eq!(
+ tor_unescape(b"\"test\0").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+
+ // Not having a final quote shortcircuits other errors.
+ assert_eq!(
+ tor_unescape(b"\"test \" test").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ }
+
+ #[test]
+ fn unescaped_quote() {
+ assert_eq!(
+ tor_unescape(b"\"test \" test\"").unwrap_err(),
+ UnescapeError::UnescapedQuote
+ );
+ }
+
+ #[test]
+ fn invalid_escape() {
+ assert_eq!(
+ tor_unescape(b"\"\\z\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+
+ assert_eq!(
+ tor_unescape(b"\"\\xy\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+ assert_eq!(
+ tor_unescape(b"\"\\XY\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+ assert_eq!(
+ tor_unescape(b"\"\\x1Y\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+ assert_eq!(
+ tor_unescape(b"\"\\x1\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+ assert_eq!(
+ tor_unescape(b"\"\\x\\xF5z\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+ }
+
+ #[test]
+ fn octal_overflow() {
+ assert_eq!(
+ tor_unescape(b"\"\\777\"").unwrap_err(),
+ UnescapeError::OctalOverflow,
+ );
+ }
+}
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/mod.rs
=====================================
@@ -4,6 +4,7 @@
// copied, modified, or distributed except according to those terms.
mod control_port;
+mod controller;
mod reply_parser;
pub use control_port::{ControlPort, ControlPortError, ControlSocket, ControlSocketError};
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/4610d7…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/4610d7…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0