tor-commits
Threads by month
- ----- 2025 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 213919 discussions

[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] Bug 1973265 - Put WebCodecs API behind RFP Target. r=tjr,webidl,smaug
by morgan (@morgan) 09 Sep '25
by morgan (@morgan) 09 Sep '25
09 Sep '25
morgan pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
9ae87bd3 by Fatih Kilic at 2025-09-09T17:41:43+00:00
Bug 1973265 - Put WebCodecs API behind RFP Target. r=tjr,webidl,smaug
Differential Revision: https://phabricator.services.mozilla.com/D254549
- - - - -
17 changed files:
- dom/base/nsContentUtils.cpp
- dom/media/webcodecs/VideoFrame.cpp
- dom/media/webcodecs/test/mochitest.toml
- + dom/media/webcodecs/test/test_rfp_api_disabling_disabled.html
- + dom/media/webcodecs/test/test_rfp_api_disabling_enabled.html
- + dom/media/webcodecs/test/test_rfp_api_disabling_exemption.html
- dom/webidl/AudioData.webidl
- dom/webidl/AudioDecoder.webidl
- dom/webidl/AudioEncoder.webidl
- dom/webidl/EncodedAudioChunk.webidl
- dom/webidl/EncodedVideoChunk.webidl
- dom/webidl/ImageDecoder.webidl
- dom/webidl/VideoDecoder.webidl
- dom/webidl/VideoEncoder.webidl
- toolkit/components/resistfingerprinting/RFPTargets.inc
- toolkit/components/resistfingerprinting/nsRFPService.cpp
- toolkit/components/resistfingerprinting/nsRFPService.h
Changes:
=====================================
dom/base/nsContentUtils.cpp
=====================================
@@ -2844,7 +2844,7 @@ bool nsContentUtils::ShouldResistFingerprinting_dangerous(
}
// Web extension principals are also excluded
- if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
+ if (NS_IsMainThread() && BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
("Inside ShouldResistFingerprinting(nsIPrincipal*)"
" and AddonPolicy said false"));
=====================================
dom/media/webcodecs/VideoFrame.cpp
=====================================
@@ -1423,7 +1423,7 @@ JSObject* VideoFrame::WrapObject(JSContext* aCx,
/* static */
bool VideoFrame::PrefEnabled(JSContext* aCx, JSObject* aObj) {
- return StaticPrefs::dom_media_webcodecs_enabled() ||
+ return nsRFPService::ExposeWebCodecsAPI(aCx, aObj) &&
StaticPrefs::dom_media_webcodecs_image_decoder_enabled();
}
=====================================
dom/media/webcodecs/test/mochitest.toml
=====================================
@@ -16,4 +16,13 @@ scheme = "https"
["test_imageDecoder_desiredSize.html"]
scheme = "https"
+["test_rfp_api_disabling_disabled.html"]
+scheme = "https"
+
+["test_rfp_api_disabling_enabled.html"]
+scheme = "https"
+
+["test_rfp_api_disabling_exemption.html"]
+scheme = "https"
+
["test_videoFrame_mismatched_codedSize.html"]
=====================================
dom/media/webcodecs/test/test_rfp_api_disabling_disabled.html
=====================================
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title></title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+const apis = [
+ "AudioData",
+ "AudioDecoder",
+ "AudioEncoder",
+ "EncodedAudioChunk",
+ "EncodedVideoChunk",
+ "ImageDecoder",
+ "ImageTrack",
+ "ImageTrackList",
+ "VideoColorSpace",
+ "VideoDecoder",
+ "VideoEncoder",
+ "VideoFrame",
+];
+
+function enabledAPIs() {
+ return apis.filter(api => typeof window[api] !== "undefined");
+}
+
+function enabledAPIsWorker() {
+ const code = `
+ onmessage = e => {
+ const apis = ${JSON.stringify(apis)};
+ postMessage(apis.filter(api => typeof self[api] !== "undefined"));
+ };`;
+ const blob = new Blob([code], { type: "application/javascript" });
+ const worker = new Worker(URL.createObjectURL(blob));
+
+ return new Promise((resolve) => {
+ worker.addEventListener("message", async (e) => {
+ worker.terminate();
+ resolve(e.data);
+ });
+
+ worker.postMessage({});
+ });
+}
+
+add_setup(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.media.webcodecs.enabled", true],
+ ["dom.media.webcodecs.image-decoder.enabled", true]
+ ],
+ });
+});
+
+add_task(async () => {
+ is(enabledAPIs().length, apis.length, true, "All WebCodecs APIs should be enabled");
+ is(
+ (await enabledAPIsWorker()).length,
+ apis.length,
+ "All WebCodecs APIs should be enabled in workers too"
+ );
+});
+</script>
+</body>
+</html>
=====================================
dom/media/webcodecs/test/test_rfp_api_disabling_enabled.html
=====================================
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title></title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+const apis = [
+ "AudioData",
+ "AudioDecoder",
+ "AudioEncoder",
+ "EncodedAudioChunk",
+ "EncodedVideoChunk",
+ "ImageDecoder",
+ "ImageTrack",
+ "ImageTrackList",
+ "VideoColorSpace",
+ "VideoDecoder",
+ "VideoEncoder",
+ "VideoFrame",
+];
+
+function enabledAPIs() {
+ return apis.filter(api => typeof window[api] !== "undefined");
+}
+
+function enabledAPIsWorker() {
+ const code = `
+ onmessage = e => {
+ const apis = ${JSON.stringify(apis)};
+ postMessage(apis.filter(api => typeof self[api] !== "undefined"));
+ };`;
+ const blob = new Blob([code], { type: "application/javascript" });
+ const worker = new Worker(URL.createObjectURL(blob));
+
+ return new Promise((resolve) => {
+ worker.addEventListener("message", async (e) => {
+ worker.terminate();
+ resolve(e.data);
+ });
+
+ worker.postMessage({});
+ });
+}
+
+add_setup(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.media.webcodecs.enabled", true],
+ ["dom.media.webcodecs.image-decoder.enabled", true],
+ ["privacy.fingerprintingProtection", true],
+ ["privacy.fingerprintingProtection.overrides", "-AllTargets,+WebCodecs"],
+ ],
+ });
+});
+
+add_task(async () => {
+ is(enabledAPIs().length, 0, true, "All WebCodecs APIs should be disabled");
+ is(
+ (await enabledAPIsWorker()).length,
+ 0,
+ "All WebCodecs APIs should be disabled in workers too"
+ );
+});
+</script>
+</body>
+</html>
=====================================
dom/media/webcodecs/test/test_rfp_api_disabling_exemption.html
=====================================
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title></title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+const apis = [
+ "AudioData",
+ "AudioDecoder",
+ "AudioEncoder",
+ "EncodedAudioChunk",
+ "EncodedVideoChunk",
+ "ImageDecoder",
+ "ImageTrack",
+ "ImageTrackList",
+ "VideoColorSpace",
+ "VideoDecoder",
+ "VideoEncoder",
+ "VideoFrame",
+];
+
+function enabledAPIs() {
+ return apis.filter(api => typeof window[api] !== "undefined");
+}
+
+function enabledAPIsWorker() {
+ const code = `
+ onmessage = e => {
+ const apis = ${JSON.stringify(apis)};
+ postMessage(apis.filter(api => typeof self[api] !== "undefined"));
+ };`;
+ const blob = new Blob([code], { type: "application/javascript" });
+ const worker = new Worker(URL.createObjectURL(blob));
+
+ return new Promise((resolve) => {
+ worker.addEventListener("message", async (e) => {
+ worker.terminate();
+ resolve(e.data);
+ });
+
+ worker.postMessage({});
+ });
+}
+
+add_setup(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.media.webcodecs.enabled", true],
+ ["dom.media.webcodecs.image-decoder.enabled", true],
+ ["privacy.fingerprintingProtection", true],
+ ["privacy.fingerprintingProtection.overrides", "-AllTargets,+WebCodecs"],
+ ["privacy.resistFingerprinting.exemptedDomains", location.hostname]
+ ],
+ });
+});
+
+add_task(async () => {
+ is(enabledAPIs().length, apis.length, true, "All WebCodecs APIs should be disabled");
+ is(
+ (await enabledAPIsWorker()).length,
+ apis.length,
+ "All WebCodecs APIs should be disabled in workers too"
+ );
+});
+</script>
+</body>
+</html>
=====================================
dom/webidl/AudioData.webidl
=====================================
@@ -9,7 +9,7 @@
// [Serializable, Transferable] are implemented without adding attributes here,
// but directly with {Read,Write}StructuredClone and Transfer/FromTransfered.
-[Exposed=(Window,DedicatedWorker), Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), Func="nsRFPService::ExposeWebCodecsAPI"]
interface AudioData {
[Throws]
constructor(AudioDataInit init);
=====================================
dom/webidl/AudioDecoder.webidl
=====================================
@@ -7,7 +7,7 @@
* https://w3c.github.io/webcodecs/#audiodecoder
*/
-[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
interface AudioDecoder : EventTarget {
[Throws]
constructor(AudioDecoderInit init);
=====================================
dom/webidl/AudioEncoder.webidl
=====================================
@@ -42,7 +42,7 @@ dictionary OpusEncoderConfig {
boolean usedtx = false;
};
-[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
interface AudioEncoder : EventTarget {
[Throws]
constructor(AudioEncoderInit init);
=====================================
dom/webidl/EncodedAudioChunk.webidl
=====================================
@@ -8,7 +8,7 @@
*/
// [Serializable] is implemented without adding attribute here.
-[Exposed=(Window,DedicatedWorker), Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), Func="nsRFPService::ExposeWebCodecsAPI"]
interface EncodedAudioChunk {
[Throws]
constructor(EncodedAudioChunkInit init);
=====================================
dom/webidl/EncodedVideoChunk.webidl
=====================================
@@ -8,7 +8,7 @@
*/
// [Serializable] is implemented without adding attribute here.
-[Exposed=(Window,DedicatedWorker), Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), Func="nsRFPService::ExposeWebCodecsAPI"]
interface EncodedVideoChunk {
[Throws]
constructor(EncodedVideoChunkInit init);
=====================================
dom/webidl/ImageDecoder.webidl
=====================================
@@ -30,7 +30,7 @@ dictionary ImageDecodeResult {
[Exposed=(Window,DedicatedWorker),
SecureContext,
- Pref="dom.media.webcodecs.image-decoder.enabled"]
+ Func="nsRFPService::ExposeWebCodecsAPIImageDecoder"]
interface ImageTrack {
readonly attribute boolean animated;
readonly attribute unsigned long frameCount;
@@ -40,7 +40,7 @@ interface ImageTrack {
[Exposed=(Window,DedicatedWorker),
SecureContext,
- Pref="dom.media.webcodecs.image-decoder.enabled"]
+ Func="nsRFPService::ExposeWebCodecsAPIImageDecoder"]
interface ImageTrackList {
getter ImageTrack (unsigned long index);
@@ -52,7 +52,7 @@ interface ImageTrackList {
[Exposed=(Window,DedicatedWorker),
SecureContext,
- Pref="dom.media.webcodecs.image-decoder.enabled"]
+ Func="nsRFPService::ExposeWebCodecsAPIImageDecoder"]
interface ImageDecoder {
[Throws]
constructor(ImageDecoderInit init);
=====================================
dom/webidl/VideoDecoder.webidl
=====================================
@@ -7,7 +7,7 @@
* https://w3c.github.io/webcodecs/#videodecoder
*/
-[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
interface VideoDecoder : EventTarget {
[Throws]
constructor(VideoDecoderInit init);
=====================================
dom/webidl/VideoEncoder.webidl
=====================================
@@ -12,7 +12,7 @@
* commented with a link of the document in which the member is listed.
*/
-[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
interface VideoEncoder : EventTarget {
[Throws]
constructor(VideoEncoderInit init);
=====================================
toolkit/components/resistfingerprinting/RFPTargets.inc
=====================================
@@ -101,6 +101,7 @@ ITEM_VALUE(JSLocalePrompt, 67)
ITEM_VALUE(ScreenAvailToResolution, 68)
ITEM_VALUE(UseHardcodedFontSubstitutes, 69)
ITEM_VALUE(DiskStorageLimit, 70)
+ITEM_VALUE(WebCodecs, 71)
// !!! Don't forget to update kDefaultFingerprintingProtections in nsRFPService.cpp
=====================================
toolkit/components/resistfingerprinting/nsRFPService.cpp
=====================================
@@ -2690,3 +2690,48 @@ uint64_t nsRFPService::GetSpoofedStorageLimit() {
return limit;
}
+
+/* static */
+bool nsRFPService::ExposeWebCodecsAPI(JSContext* aCx, JSObject* aObj) {
+ if (!StaticPrefs::dom_media_webcodecs_enabled()) {
+ return false;
+ }
+
+ return !IsWebCodecsRFPTargetEnabled(aCx);
+}
+
+/* static */
+bool nsRFPService::ExposeWebCodecsAPIImageDecoder(JSContext* aCx,
+ JSObject* aObj) {
+ if (!StaticPrefs::dom_media_webcodecs_image_decoder_enabled()) {
+ return false;
+ }
+
+ return !IsWebCodecsRFPTargetEnabled(aCx);
+}
+
+/* static */
+bool nsRFPService::IsWebCodecsRFPTargetEnabled(JSContext* aCx) {
+ if (!nsContentUtils::ShouldResistFingerprinting("Efficiency check",
+ RFPTarget::WebCodecs)) {
+ return false;
+ }
+
+ // We know that the RFPTarget::WebCodecs is enabled, check if principal
+ // is exempted.
+
+ // VideoFrame::PrefEnabled function can be called without a JSContext.
+ if (!aCx) {
+ return true;
+ }
+
+ // Once bug 1973966 is resolved, we can replace this section with just
+ // nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
+ JS::Realm* realm = js::GetContextRealm(aCx);
+ MOZ_ASSERT(realm);
+ JSPrincipals* principals = JS::GetRealmPrincipals(realm);
+ nsIPrincipal* principal = nsJSPrincipals::get(principals);
+
+ return nsContentUtils::ShouldResistFingerprinting_dangerous(
+ principal, "Principal is the best context we have", RFPTarget::WebCodecs);
+}
=====================================
toolkit/components/resistfingerprinting/nsRFPService.h
=====================================
@@ -416,6 +416,9 @@ class nsRFPService final : public nsIObserver, public nsIRFPService {
static uint64_t GetSpoofedStorageLimit();
+ static bool ExposeWebCodecsAPI(JSContext* aCx, JSObject* aObj);
+ static bool ExposeWebCodecsAPIImageDecoder(JSContext* aCx, JSObject* aObj);
+
private:
nsresult Init();
@@ -527,6 +530,8 @@ class nsRFPService final : public nsIObserver, public nsIRFPService {
static bool IsTargetActiveForMode(RFPTarget aTarget,
FingerprintingProtectionType aMode);
+ static bool IsWebCodecsRFPTargetEnabled(JSContext* aCx);
+
static nsCString* sExemptedDomainsLowercase;
};
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/9ae87bd…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/9ae87bd…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] Bug 1973265 - Put WebCodecs API behind RFP Target. r=tjr,webidl,smaug
by morgan (@morgan) 09 Sep '25
by morgan (@morgan) 09 Sep '25
09 Sep '25
morgan pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
64132f03 by Fatih Kilic at 2025-09-09T17:36:29+00:00
Bug 1973265 - Put WebCodecs API behind RFP Target. r=tjr,webidl,smaug
Differential Revision: https://phabricator.services.mozilla.com/D254549
- - - - -
17 changed files:
- dom/base/nsContentUtils.cpp
- dom/media/webcodecs/VideoFrame.cpp
- dom/media/webcodecs/test/mochitest.toml
- + dom/media/webcodecs/test/test_rfp_api_disabling_disabled.html
- + dom/media/webcodecs/test/test_rfp_api_disabling_enabled.html
- + dom/media/webcodecs/test/test_rfp_api_disabling_exemption.html
- dom/webidl/AudioData.webidl
- dom/webidl/AudioDecoder.webidl
- dom/webidl/AudioEncoder.webidl
- dom/webidl/EncodedAudioChunk.webidl
- dom/webidl/EncodedVideoChunk.webidl
- dom/webidl/ImageDecoder.webidl
- dom/webidl/VideoDecoder.webidl
- dom/webidl/VideoEncoder.webidl
- toolkit/components/resistfingerprinting/RFPTargets.inc
- toolkit/components/resistfingerprinting/nsRFPService.cpp
- toolkit/components/resistfingerprinting/nsRFPService.h
Changes:
=====================================
dom/base/nsContentUtils.cpp
=====================================
@@ -2844,7 +2844,7 @@ bool nsContentUtils::ShouldResistFingerprinting_dangerous(
}
// Web extension principals are also excluded
- if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
+ if (NS_IsMainThread() && BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
("Inside ShouldResistFingerprinting(nsIPrincipal*)"
" and AddonPolicy said false"));
=====================================
dom/media/webcodecs/VideoFrame.cpp
=====================================
@@ -1423,7 +1423,7 @@ JSObject* VideoFrame::WrapObject(JSContext* aCx,
/* static */
bool VideoFrame::PrefEnabled(JSContext* aCx, JSObject* aObj) {
- return StaticPrefs::dom_media_webcodecs_enabled() ||
+ return nsRFPService::ExposeWebCodecsAPI(aCx, aObj) &&
StaticPrefs::dom_media_webcodecs_image_decoder_enabled();
}
=====================================
dom/media/webcodecs/test/mochitest.toml
=====================================
@@ -16,4 +16,13 @@ scheme = "https"
["test_imageDecoder_desiredSize.html"]
scheme = "https"
+["test_rfp_api_disabling_disabled.html"]
+scheme = "https"
+
+["test_rfp_api_disabling_enabled.html"]
+scheme = "https"
+
+["test_rfp_api_disabling_exemption.html"]
+scheme = "https"
+
["test_videoFrame_mismatched_codedSize.html"]
=====================================
dom/media/webcodecs/test/test_rfp_api_disabling_disabled.html
=====================================
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title></title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+const apis = [
+ "AudioData",
+ "AudioDecoder",
+ "AudioEncoder",
+ "EncodedAudioChunk",
+ "EncodedVideoChunk",
+ "ImageDecoder",
+ "ImageTrack",
+ "ImageTrackList",
+ "VideoColorSpace",
+ "VideoDecoder",
+ "VideoEncoder",
+ "VideoFrame",
+];
+
+function enabledAPIs() {
+ return apis.filter(api => typeof window[api] !== "undefined");
+}
+
+function enabledAPIsWorker() {
+ const code = `
+ onmessage = e => {
+ const apis = ${JSON.stringify(apis)};
+ postMessage(apis.filter(api => typeof self[api] !== "undefined"));
+ };`;
+ const blob = new Blob([code], { type: "application/javascript" });
+ const worker = new Worker(URL.createObjectURL(blob));
+
+ return new Promise((resolve) => {
+ worker.addEventListener("message", async (e) => {
+ worker.terminate();
+ resolve(e.data);
+ });
+
+ worker.postMessage({});
+ });
+}
+
+add_setup(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.media.webcodecs.enabled", true],
+ ["dom.media.webcodecs.image-decoder.enabled", true]
+ ],
+ });
+});
+
+add_task(async () => {
+ is(enabledAPIs().length, apis.length, true, "All WebCodecs APIs should be enabled");
+ is(
+ (await enabledAPIsWorker()).length,
+ apis.length,
+ "All WebCodecs APIs should be enabled in workers too"
+ );
+});
+</script>
+</body>
+</html>
=====================================
dom/media/webcodecs/test/test_rfp_api_disabling_enabled.html
=====================================
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title></title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+const apis = [
+ "AudioData",
+ "AudioDecoder",
+ "AudioEncoder",
+ "EncodedAudioChunk",
+ "EncodedVideoChunk",
+ "ImageDecoder",
+ "ImageTrack",
+ "ImageTrackList",
+ "VideoColorSpace",
+ "VideoDecoder",
+ "VideoEncoder",
+ "VideoFrame",
+];
+
+function enabledAPIs() {
+ return apis.filter(api => typeof window[api] !== "undefined");
+}
+
+function enabledAPIsWorker() {
+ const code = `
+ onmessage = e => {
+ const apis = ${JSON.stringify(apis)};
+ postMessage(apis.filter(api => typeof self[api] !== "undefined"));
+ };`;
+ const blob = new Blob([code], { type: "application/javascript" });
+ const worker = new Worker(URL.createObjectURL(blob));
+
+ return new Promise((resolve) => {
+ worker.addEventListener("message", async (e) => {
+ worker.terminate();
+ resolve(e.data);
+ });
+
+ worker.postMessage({});
+ });
+}
+
+add_setup(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.media.webcodecs.enabled", true],
+ ["dom.media.webcodecs.image-decoder.enabled", true],
+ ["privacy.fingerprintingProtection", true],
+ ["privacy.fingerprintingProtection.overrides", "-AllTargets,+WebCodecs"],
+ ],
+ });
+});
+
+add_task(async () => {
+ is(enabledAPIs().length, 0, true, "All WebCodecs APIs should be disabled");
+ is(
+ (await enabledAPIsWorker()).length,
+ 0,
+ "All WebCodecs APIs should be disabled in workers too"
+ );
+});
+</script>
+</body>
+</html>
=====================================
dom/media/webcodecs/test/test_rfp_api_disabling_exemption.html
=====================================
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title></title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+const apis = [
+ "AudioData",
+ "AudioDecoder",
+ "AudioEncoder",
+ "EncodedAudioChunk",
+ "EncodedVideoChunk",
+ "ImageDecoder",
+ "ImageTrack",
+ "ImageTrackList",
+ "VideoColorSpace",
+ "VideoDecoder",
+ "VideoEncoder",
+ "VideoFrame",
+];
+
+function enabledAPIs() {
+ return apis.filter(api => typeof window[api] !== "undefined");
+}
+
+function enabledAPIsWorker() {
+ const code = `
+ onmessage = e => {
+ const apis = ${JSON.stringify(apis)};
+ postMessage(apis.filter(api => typeof self[api] !== "undefined"));
+ };`;
+ const blob = new Blob([code], { type: "application/javascript" });
+ const worker = new Worker(URL.createObjectURL(blob));
+
+ return new Promise((resolve) => {
+ worker.addEventListener("message", async (e) => {
+ worker.terminate();
+ resolve(e.data);
+ });
+
+ worker.postMessage({});
+ });
+}
+
+add_setup(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.media.webcodecs.enabled", true],
+ ["dom.media.webcodecs.image-decoder.enabled", true],
+ ["privacy.fingerprintingProtection", true],
+ ["privacy.fingerprintingProtection.overrides", "-AllTargets,+WebCodecs"],
+ ["privacy.resistFingerprinting.exemptedDomains", location.hostname]
+ ],
+ });
+});
+
+add_task(async () => {
+ is(enabledAPIs().length, apis.length, true, "All WebCodecs APIs should be disabled");
+ is(
+ (await enabledAPIsWorker()).length,
+ apis.length,
+ "All WebCodecs APIs should be disabled in workers too"
+ );
+});
+</script>
+</body>
+</html>
=====================================
dom/webidl/AudioData.webidl
=====================================
@@ -9,7 +9,7 @@
// [Serializable, Transferable] are implemented without adding attributes here,
// but directly with {Read,Write}StructuredClone and Transfer/FromTransfered.
-[Exposed=(Window,DedicatedWorker), Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), Func="nsRFPService::ExposeWebCodecsAPI"]
interface AudioData {
[Throws]
constructor(AudioDataInit init);
=====================================
dom/webidl/AudioDecoder.webidl
=====================================
@@ -7,7 +7,7 @@
* https://w3c.github.io/webcodecs/#audiodecoder
*/
-[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
interface AudioDecoder : EventTarget {
[Throws]
constructor(AudioDecoderInit init);
=====================================
dom/webidl/AudioEncoder.webidl
=====================================
@@ -42,7 +42,7 @@ dictionary OpusEncoderConfig {
boolean usedtx = false;
};
-[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
interface AudioEncoder : EventTarget {
[Throws]
constructor(AudioEncoderInit init);
=====================================
dom/webidl/EncodedAudioChunk.webidl
=====================================
@@ -8,7 +8,7 @@
*/
// [Serializable] is implemented without adding attribute here.
-[Exposed=(Window,DedicatedWorker), Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), Func="nsRFPService::ExposeWebCodecsAPI"]
interface EncodedAudioChunk {
[Throws]
constructor(EncodedAudioChunkInit init);
=====================================
dom/webidl/EncodedVideoChunk.webidl
=====================================
@@ -8,7 +8,7 @@
*/
// [Serializable] is implemented without adding attribute here.
-[Exposed=(Window,DedicatedWorker), Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), Func="nsRFPService::ExposeWebCodecsAPI"]
interface EncodedVideoChunk {
[Throws]
constructor(EncodedVideoChunkInit init);
=====================================
dom/webidl/ImageDecoder.webidl
=====================================
@@ -30,7 +30,7 @@ dictionary ImageDecodeResult {
[Exposed=(Window,DedicatedWorker),
SecureContext,
- Pref="dom.media.webcodecs.image-decoder.enabled"]
+ Func="nsRFPService::ExposeWebCodecsAPIImageDecoder"]
interface ImageTrack {
readonly attribute boolean animated;
readonly attribute unsigned long frameCount;
@@ -40,7 +40,7 @@ interface ImageTrack {
[Exposed=(Window,DedicatedWorker),
SecureContext,
- Pref="dom.media.webcodecs.image-decoder.enabled"]
+ Func="nsRFPService::ExposeWebCodecsAPIImageDecoder"]
interface ImageTrackList {
getter ImageTrack (unsigned long index);
@@ -52,7 +52,7 @@ interface ImageTrackList {
[Exposed=(Window,DedicatedWorker),
SecureContext,
- Pref="dom.media.webcodecs.image-decoder.enabled"]
+ Func="nsRFPService::ExposeWebCodecsAPIImageDecoder"]
interface ImageDecoder {
[Throws]
constructor(ImageDecoderInit init);
=====================================
dom/webidl/VideoDecoder.webidl
=====================================
@@ -7,7 +7,7 @@
* https://w3c.github.io/webcodecs/#videodecoder
*/
-[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
interface VideoDecoder : EventTarget {
[Throws]
constructor(VideoDecoderInit init);
=====================================
dom/webidl/VideoEncoder.webidl
=====================================
@@ -12,7 +12,7 @@
* commented with a link of the document in which the member is listed.
*/
-[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
+[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
interface VideoEncoder : EventTarget {
[Throws]
constructor(VideoEncoderInit init);
=====================================
toolkit/components/resistfingerprinting/RFPTargets.inc
=====================================
@@ -101,6 +101,7 @@ ITEM_VALUE(JSLocalePrompt, 67)
ITEM_VALUE(ScreenAvailToResolution, 68)
ITEM_VALUE(UseHardcodedFontSubstitutes, 69)
ITEM_VALUE(DiskStorageLimit, 70)
+ITEM_VALUE(WebCodecs, 71)
// !!! Don't forget to update kDefaultFingerprintingProtections in nsRFPService.cpp
=====================================
toolkit/components/resistfingerprinting/nsRFPService.cpp
=====================================
@@ -2697,3 +2697,48 @@ uint64_t nsRFPService::GetSpoofedStorageLimit() {
return limit;
}
+
+/* static */
+bool nsRFPService::ExposeWebCodecsAPI(JSContext* aCx, JSObject* aObj) {
+ if (!StaticPrefs::dom_media_webcodecs_enabled()) {
+ return false;
+ }
+
+ return !IsWebCodecsRFPTargetEnabled(aCx);
+}
+
+/* static */
+bool nsRFPService::ExposeWebCodecsAPIImageDecoder(JSContext* aCx,
+ JSObject* aObj) {
+ if (!StaticPrefs::dom_media_webcodecs_image_decoder_enabled()) {
+ return false;
+ }
+
+ return !IsWebCodecsRFPTargetEnabled(aCx);
+}
+
+/* static */
+bool nsRFPService::IsWebCodecsRFPTargetEnabled(JSContext* aCx) {
+ if (!nsContentUtils::ShouldResistFingerprinting("Efficiency check",
+ RFPTarget::WebCodecs)) {
+ return false;
+ }
+
+ // We know that the RFPTarget::WebCodecs is enabled, check if principal
+ // is exempted.
+
+ // VideoFrame::PrefEnabled function can be called without a JSContext.
+ if (!aCx) {
+ return true;
+ }
+
+ // Once bug 1973966 is resolved, we can replace this section with just
+ // nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
+ JS::Realm* realm = js::GetContextRealm(aCx);
+ MOZ_ASSERT(realm);
+ JSPrincipals* principals = JS::GetRealmPrincipals(realm);
+ nsIPrincipal* principal = nsJSPrincipals::get(principals);
+
+ return nsContentUtils::ShouldResistFingerprinting_dangerous(
+ principal, "Principal is the best context we have", RFPTarget::WebCodecs);
+}
=====================================
toolkit/components/resistfingerprinting/nsRFPService.h
=====================================
@@ -416,6 +416,9 @@ class nsRFPService final : public nsIObserver, public nsIRFPService {
static uint64_t GetSpoofedStorageLimit();
+ static bool ExposeWebCodecsAPI(JSContext* aCx, JSObject* aObj);
+ static bool ExposeWebCodecsAPIImageDecoder(JSContext* aCx, JSObject* aObj);
+
private:
nsresult Init();
@@ -527,6 +530,8 @@ class nsRFPService final : public nsIObserver, public nsIRFPService {
static bool IsTargetActiveForMode(RFPTarget aTarget,
FingerprintingProtectionType aMode);
+ static bool IsWebCodecsRFPTargetEnabled(JSContext* aCx);
+
static nsCString* sExemptedDomainsLowercase;
};
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/64132f0…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/64132f0…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] fixup! BB 40925: Implemented the Security Level component
by morgan (@morgan) 09 Sep '25
by morgan (@morgan) 09 Sep '25
09 Sep '25
morgan pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
cb4959c6 by Henry Wilkes at 2025-09-09T17:34:59+00:00
fixup! BB 40925: Implemented the Security Level component
TB 43966: Add a warning notification for users when they enter the
custom security level state.
- - - - -
4 changed files:
- browser/components/BrowserComponents.manifest
- browser/modules/SecurityLevelRestartNotification.sys.mjs → browser/modules/SecurityLevelNotification.sys.mjs
- browser/modules/moz.build
- toolkit/components/securitylevel/SecurityLevel.sys.mjs
Changes:
=====================================
browser/components/BrowserComponents.manifest
=====================================
@@ -51,7 +51,7 @@ category browser-first-window-ready resource://gre/modules/SandboxUtils.sys.mjs
#endif
#endif
category browser-first-window-ready moz-src:///browser/modules/ClipboardPrivacy.sys.mjs ClipboardPrivacy.init
-category browser-first-window-ready moz-src:///browser/modules/SecurityLevelRestartNotification.sys.mjs SecurityLevelRestartNotification.ready
+category browser-first-window-ready moz-src:///browser/modules/SecurityLevelNotification.sys.mjs SecurityLevelNotification.ready
category browser-idle-startup resource:///modules/PlacesUIUtils.sys.mjs PlacesUIUtils.unblockToolbars
category browser-idle-startup resource:///modules/BuiltInThemes.sys.mjs BuiltInThemes.ensureBuiltInThemes
=====================================
browser/modules/SecurityLevelRestartNotification.sys.mjs → browser/modules/SecurityLevelNotification.sys.mjs
=====================================
@@ -15,7 +15,7 @@ ChromeUtils.defineLazyGetter(lazy, "NotificationStrings", function () {
/**
* Interface for showing the security level restart notification on desktop.
*/
-export const SecurityLevelRestartNotification = {
+export const SecurityLevelNotification = {
/**
* Whether we have already been initialised.
*
@@ -31,11 +31,13 @@ export const SecurityLevelRestartNotification = {
return;
}
this._initialized = true;
- lazy.SecurityLevelPrefs.setRestartNotificationHandler(this);
+ lazy.SecurityLevelPrefs.setNotificationHandler(this);
},
/**
* Show the restart notification, and perform the restart if the user agrees.
+ *
+ * @returns {boolean} - Whether we are restarting the browser.
*/
async tryRestartBrowser() {
const [titleText, bodyText, primaryButtonText, secondaryButtonText] =
@@ -69,6 +71,49 @@ export const SecurityLevelRestartNotification = {
Services.startup.quit(
Services.startup.eAttemptQuit | Services.startup.eRestart
);
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Show or re-show the custom security notification.
+ *
+ * @param {Function} userDismissedCallback - The callback for when the user
+ * dismisses the notification.
+ */
+ async showCustomWarning(userDismissedCallback) {
+ const win = lazy.BrowserWindowTracker.getTopWindow();
+ if (!win) {
+ return;
+ }
+ const typeName = "security-level-custom";
+ const existing = win.gNotificationBox.getNotificationWithValue(typeName);
+ if (existing) {
+ win.gNotificationBox.removeNotification(existing);
}
+
+ const buttons = [
+ {
+ "l10n-id": "security-level-panel-open-settings-button",
+ callback() {
+ win.openPreferences("privacy-securitylevel");
+ },
+ },
+ ];
+
+ win.gNotificationBox.appendNotification(
+ typeName,
+ {
+ label: { "l10n-id": "security-level-summary-custom" },
+ priority: win.gNotificationBox.PRIORITY_WARNING_HIGH,
+ eventCallback: event => {
+ if (event === "dismissed") {
+ userDismissedCallback();
+ }
+ },
+ },
+ buttons
+ );
},
};
=====================================
browser/modules/moz.build
=====================================
@@ -149,7 +149,7 @@ EXTRA_JS_MODULES += [
MOZ_SRC_FILES += [
"ContextId.sys.mjs",
"ClipboardPrivacy.sys.mjs",
- "SecurityLevelRestartNotification.sys.mjs",
+ "SecurityLevelNotification.sys.mjs",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
=====================================
toolkit/components/securitylevel/SecurityLevel.sys.mjs
=====================================
@@ -238,7 +238,7 @@ var initializeNoScriptControl = () => {
} catch (e) {
logger.error("Could not apply NoScript settings", e);
// Treat as a custom security level for the rest of the session.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}
};
waitForExtensionMessage(noscriptID, a => a.__meta.name === "started").then(
@@ -248,7 +248,7 @@ var initializeNoScriptControl = () => {
} catch (e) {
logger.exception(e);
// Treat as a custom security level for the rest of the session.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}
};
@@ -401,7 +401,12 @@ var initializeSecurityPrefs = function () {
// particular, for the NoScript addon.
Services.prefs.setBoolPref(kCustomPref, false);
Services.prefs.setIntPref(kSliderPref, effectiveIndex);
- } else if (!wasCustom && effectiveIndex !== desiredIndex) {
+ }
+ // Warn the user if they have booted the browser in a custom state, and have
+ // not yet acknowledged it in a previous session.
+ SecurityLevelPrefs.maybeWarnCustom();
+
+ if (!wasCustom && effectiveIndex !== desiredIndex) {
// NOTE: We assume all our controlled preferences require a restart.
// In practice, only a subset of these preferences may actually require a
// restart, so we could switch their values. But we treat them all the same
@@ -448,7 +453,7 @@ var initializeSecurityPrefs = function () {
// properly applied. See tor-browser#43783.
// In the case where it does match a pre-set security level, the custom
// flag will be cleared at the next startup.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}),
});
}
@@ -517,12 +522,27 @@ export class SecurityLevel {
}
/**
- * @typedef {object} SecurityLevelRestartNotificationHandler
+ * @callback SecurityLevelTryRestartBrowserCallback
+ *
+ * @returns {Promise<boolean>} - A promise that resolves when the user has made
+ * a decision. Should return `true` when the browser is now restarting.
+ */
+/**
+ * @callback SecurityLevelShowCustomWarningCallback
*
- * An object that can serve the user a restart notification.
+ * @param {Function} userDismissedCallback - A callback that should be called
+ * if the user has acknowledged and dismissed the notification.
+ */
+/**
+ * @typedef {object} SecurityLevelNotificationHandler
*
- * @property {Function} tryRestartBrowser - The method that should be called to
- * ask the user to restart the browser.
+ * An object that can serve the user notifications.
+ *
+ * @property {SecurityLevelTryRestartBrowserCallback} tryRestartBrowser - The
+ * method that should be called to ask the user to restart the browser.
+ * @property {SecurityLevelShowCustomWarningCallback} showCustomWarning - The
+ * method that should be called to let the user know they have a custom
+ * security level.
*/
/*
@@ -538,6 +558,8 @@ export const SecurityLevelPrefs = {
}),
security_slider_pref: "browser.security_level.security_slider",
security_custom_pref: "browser.security_level.security_custom",
+ _customWarningDismissedPref:
+ "browser.security_level.custom_warning_dismissed",
/**
* The current security level preference.
@@ -593,18 +615,18 @@ export const SecurityLevelPrefs = {
},
/**
- * Whether the browser should be restarted to apply the security level.
+ * The external handler that can show a notification to the user, if any.
*
- * @type {boolean}
+ * @type {?SecurityLevelNotificationHandler}
*/
- _needRestart: false,
+ _notificationHandler: null,
/**
- * The external handler that can show a notification to the user, if any.
+ * The notifications we are waiting for a handler to show.
*
- * @type {?SecurityLevelRestartNotificationHandler}
+ * @type {Set}
*/
- _restartNotificationHandler: null,
+ _pendingNotifications: {},
/**
* Set the external handler for showing notifications to the user.
@@ -612,49 +634,73 @@ export const SecurityLevelPrefs = {
* This should only be called once per session once the handler is ready to
* show a notification, which may occur immediately during this call.
*
- * @param {SecurityLevelRestartNotificationHandler} handler - The new handler
- * to use.
+ * @param {SecurityLevelNotificationHandler} handler - The new handler to use.
*/
- setRestartNotificationHandler(handler) {
+ setNotificationHandler(handler) {
logger.info("Restart notification handler is set");
- this._restartNotificationHandler = handler;
- if (this._needRestart) {
- // Show now using the new handler.
- this._tryShowRestartNotification();
- }
+ this._notificationHandler = handler;
+ this._tryShowNotifications(this._pendingNotifications);
},
/**
* A promise for any ongoing notification prompt task.
*
- * @type {Promise}
+ * Resolves with whether the browser is restarting.
+ *
+ * @type {Promise<boolean>}
*/
_restartNotificationPromise: null,
/**
- * Try show a notification to the user.
+ * Try show notifications to the user.
+ *
+ * If no notification handler has been attached yet, this will queue the
+ * notification for when it is added, if ever.
*
- * If no notification handler has been attached yet, this will do nothing.
+ * @param {object} notifications - The notifications to try and show.
+ * @param {boolean} notifications.restart - Whether to show the restart
+ * notification.
+ * @param {boolean} notifications.custom - Whether to show the custom security
+ * level notification.
*/
- async _tryShowRestartNotification() {
- if (!this._restartNotificationHandler) {
- logger.info("Missing a restart notification handler");
+ async _tryShowNotifications(notifications) {
+ if (!this._notificationHandler) {
+ logger.info("Missing a notification handler", notifications);
// This may be added later in the session.
+ if (notifications.custom) {
+ this._pendingNotifications.custom = true;
+ }
+ if (notifications.restart) {
+ this._pendingNotifications.restart = true;
+ }
return;
}
- const prevPromise = this._restartNotificationPromise;
- let resolve;
- ({ promise: this._restartNotificationPromise, resolve } =
- Promise.withResolvers());
- await prevPromise;
-
- try {
- await this._restartNotificationHandler?.tryRestartBrowser();
- } finally {
- // Allow the notification to be shown again.
- resolve();
+ let isRestarting = false;
+ if (notifications.restart) {
+ const prevPromise = this._restartNotificationPromise;
+ let resolve;
+ ({ promise: this._restartNotificationPromise, resolve } =
+ Promise.withResolvers());
+ await prevPromise;
+
+ try {
+ isRestarting = await this._notificationHandler?.tryRestartBrowser();
+ } finally {
+ // Allow the notification to be shown again.
+ resolve();
+ }
+ }
+ // NOTE: We wait for the restart notification to resolve before showing the
+ // custom warning. We do not show the warning if we are already restarting.
+ if (!isRestarting && notifications.custom) {
+ this._notificationHandler?.showCustomWarning(() => {
+ // User has acknowledged and dismissed the notification.
+ Services.prefs.setBoolPref(this._customWarningDismissedPref, true);
+ });
}
+
+ this._pendingNotifications = {};
},
/**
@@ -670,7 +716,6 @@ export const SecurityLevelPrefs = {
// At the next startup, the custom flag may be cleared if the settings are
// as expected.
Services.prefs.setBoolPref(kCustomPref, true);
- this._needRestart = true;
// NOTE: We need to change the controlled security level preferences in
// response to the desired change in security level. We could either:
@@ -696,6 +741,39 @@ export const SecurityLevelPrefs = {
// still be marked as "custom" because:
// 1. Some preferences require a browser restart to be applied.
// 2. NoScript has not been updated with the new settings.
- this._tryShowRestartNotification();
+
+ this._tryShowNotifications({ restart: true, custom: true });
+ },
+
+ /**
+ * Put the user in the custom security level state and show them a warning
+ * about this state.
+ */
+ setCustomAndWarn() {
+ Services.prefs.setBoolPref(kCustomPref, true);
+ // NOTE: We clear _customWarningDismissedPref because the entry points
+ // for this method imply we should re-warn the user each time.
+ Services.prefs.clearUserPref(this._customWarningDismissedPref);
+ this._tryShowNotifications({ custom: true });
+ },
+
+ /**
+ * If the user is in a custom state, try and notify them of this state.
+ */
+ maybeWarnCustom() {
+ const isCustom = Services.prefs.getBoolPref(kCustomPref, false);
+ if (!isCustom) {
+ // Clear the dismissed preference so the user will be re-shown the
+ // notification when they re-enter the custom state.
+ Services.prefs.clearUserPref(this._customWarningDismissedPref);
+ return;
+ }
+ if (Services.prefs.getBoolPref(this._customWarningDismissedPref, false)) {
+ // Do not warn the user of the custom state if they have already
+ // acknowledged and dismissed this in a previous session.
+ return;
+ }
+
+ this._tryShowNotifications({ custom: true });
},
}; /* Security Level Prefs */
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/cb4…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/cb4…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] fixup! BB 40925: Implemented the Security Level component
by morgan (@morgan) 09 Sep '25
by morgan (@morgan) 09 Sep '25
09 Sep '25
morgan pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
f8f4dbce by Henry Wilkes at 2025-09-09T17:33:53+00:00
fixup! BB 40925: Implemented the Security Level component
TB 43966: Add a warning notification for users when they enter the
custom security level state.
- - - - -
4 changed files:
- browser/components/BrowserComponents.manifest
- browser/modules/SecurityLevelRestartNotification.sys.mjs → browser/modules/SecurityLevelNotification.sys.mjs
- browser/modules/moz.build
- toolkit/components/securitylevel/SecurityLevel.sys.mjs
Changes:
=====================================
browser/components/BrowserComponents.manifest
=====================================
@@ -51,7 +51,7 @@ category browser-first-window-ready resource://gre/modules/SandboxUtils.sys.mjs
#endif
#endif
category browser-first-window-ready moz-src:///browser/modules/ClipboardPrivacy.sys.mjs ClipboardPrivacy.init
-category browser-first-window-ready moz-src:///browser/modules/SecurityLevelRestartNotification.sys.mjs SecurityLevelRestartNotification.ready
+category browser-first-window-ready moz-src:///browser/modules/SecurityLevelNotification.sys.mjs SecurityLevelNotification.ready
category browser-idle-startup resource:///modules/PlacesUIUtils.sys.mjs PlacesUIUtils.unblockToolbars
category browser-idle-startup resource:///modules/BuiltInThemes.sys.mjs BuiltInThemes.ensureBuiltInThemes
=====================================
browser/modules/SecurityLevelRestartNotification.sys.mjs → browser/modules/SecurityLevelNotification.sys.mjs
=====================================
@@ -15,7 +15,7 @@ ChromeUtils.defineLazyGetter(lazy, "NotificationStrings", function () {
/**
* Interface for showing the security level restart notification on desktop.
*/
-export const SecurityLevelRestartNotification = {
+export const SecurityLevelNotification = {
/**
* Whether we have already been initialised.
*
@@ -31,11 +31,13 @@ export const SecurityLevelRestartNotification = {
return;
}
this._initialized = true;
- lazy.SecurityLevelPrefs.setRestartNotificationHandler(this);
+ lazy.SecurityLevelPrefs.setNotificationHandler(this);
},
/**
* Show the restart notification, and perform the restart if the user agrees.
+ *
+ * @returns {boolean} - Whether we are restarting the browser.
*/
async tryRestartBrowser() {
const [titleText, bodyText, primaryButtonText, secondaryButtonText] =
@@ -69,6 +71,49 @@ export const SecurityLevelRestartNotification = {
Services.startup.quit(
Services.startup.eAttemptQuit | Services.startup.eRestart
);
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Show or re-show the custom security notification.
+ *
+ * @param {Function} userDismissedCallback - The callback for when the user
+ * dismisses the notification.
+ */
+ async showCustomWarning(userDismissedCallback) {
+ const win = lazy.BrowserWindowTracker.getTopWindow();
+ if (!win) {
+ return;
+ }
+ const typeName = "security-level-custom";
+ const existing = win.gNotificationBox.getNotificationWithValue(typeName);
+ if (existing) {
+ win.gNotificationBox.removeNotification(existing);
}
+
+ const buttons = [
+ {
+ "l10n-id": "security-level-panel-open-settings-button",
+ callback() {
+ win.openPreferences("privacy-securitylevel");
+ },
+ },
+ ];
+
+ win.gNotificationBox.appendNotification(
+ typeName,
+ {
+ label: { "l10n-id": "security-level-summary-custom" },
+ priority: win.gNotificationBox.PRIORITY_WARNING_HIGH,
+ eventCallback: event => {
+ if (event === "dismissed") {
+ userDismissedCallback();
+ }
+ },
+ },
+ buttons
+ );
},
};
=====================================
browser/modules/moz.build
=====================================
@@ -149,7 +149,7 @@ EXTRA_JS_MODULES += [
MOZ_SRC_FILES += [
"ContextId.sys.mjs",
"ClipboardPrivacy.sys.mjs",
- "SecurityLevelRestartNotification.sys.mjs",
+ "SecurityLevelNotification.sys.mjs",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
=====================================
toolkit/components/securitylevel/SecurityLevel.sys.mjs
=====================================
@@ -226,7 +226,7 @@ var initializeNoScriptControl = () => {
} catch (e) {
logger.error("Could not apply NoScript settings", e);
// Treat as a custom security level for the rest of the session.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}
};
waitForExtensionMessage(noscriptID, a => a.__meta.name === "started").then(
@@ -236,7 +236,7 @@ var initializeNoScriptControl = () => {
} catch (e) {
logger.exception(e);
// Treat as a custom security level for the rest of the session.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}
};
@@ -389,7 +389,12 @@ var initializeSecurityPrefs = function () {
// particular, for the NoScript addon.
Services.prefs.setBoolPref(kCustomPref, false);
Services.prefs.setIntPref(kSliderPref, effectiveIndex);
- } else if (!wasCustom && effectiveIndex !== desiredIndex) {
+ }
+ // Warn the user if they have booted the browser in a custom state, and have
+ // not yet acknowledged it in a previous session.
+ SecurityLevelPrefs.maybeWarnCustom();
+
+ if (!wasCustom && effectiveIndex !== desiredIndex) {
// NOTE: We assume all our controlled preferences require a restart.
// In practice, only a subset of these preferences may actually require a
// restart, so we could switch their values. But we treat them all the same
@@ -436,7 +441,7 @@ var initializeSecurityPrefs = function () {
// properly applied. See tor-browser#43783.
// In the case where it does match a pre-set security level, the custom
// flag will be cleared at the next startup.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}),
});
}
@@ -505,12 +510,27 @@ export class SecurityLevel {
}
/**
- * @typedef {object} SecurityLevelRestartNotificationHandler
+ * @callback SecurityLevelTryRestartBrowserCallback
+ *
+ * @returns {Promise<boolean>} - A promise that resolves when the user has made
+ * a decision. Should return `true` when the browser is now restarting.
+ */
+/**
+ * @callback SecurityLevelShowCustomWarningCallback
*
- * An object that can serve the user a restart notification.
+ * @param {Function} userDismissedCallback - A callback that should be called
+ * if the user has acknowledged and dismissed the notification.
+ */
+/**
+ * @typedef {object} SecurityLevelNotificationHandler
*
- * @property {Function} tryRestartBrowser - The method that should be called to
- * ask the user to restart the browser.
+ * An object that can serve the user notifications.
+ *
+ * @property {SecurityLevelTryRestartBrowserCallback} tryRestartBrowser - The
+ * method that should be called to ask the user to restart the browser.
+ * @property {SecurityLevelShowCustomWarningCallback} showCustomWarning - The
+ * method that should be called to let the user know they have a custom
+ * security level.
*/
/*
@@ -526,6 +546,8 @@ export const SecurityLevelPrefs = {
}),
security_slider_pref: "browser.security_level.security_slider",
security_custom_pref: "browser.security_level.security_custom",
+ _customWarningDismissedPref:
+ "browser.security_level.custom_warning_dismissed",
/**
* The current security level preference.
@@ -581,18 +603,18 @@ export const SecurityLevelPrefs = {
},
/**
- * Whether the browser should be restarted to apply the security level.
+ * The external handler that can show a notification to the user, if any.
*
- * @type {boolean}
+ * @type {?SecurityLevelNotificationHandler}
*/
- _needRestart: false,
+ _notificationHandler: null,
/**
- * The external handler that can show a notification to the user, if any.
+ * The notifications we are waiting for a handler to show.
*
- * @type {?SecurityLevelRestartNotificationHandler}
+ * @type {Set}
*/
- _restartNotificationHandler: null,
+ _pendingNotifications: {},
/**
* Set the external handler for showing notifications to the user.
@@ -600,49 +622,73 @@ export const SecurityLevelPrefs = {
* This should only be called once per session once the handler is ready to
* show a notification, which may occur immediately during this call.
*
- * @param {SecurityLevelRestartNotificationHandler} handler - The new handler
- * to use.
+ * @param {SecurityLevelNotificationHandler} handler - The new handler to use.
*/
- setRestartNotificationHandler(handler) {
+ setNotificationHandler(handler) {
logger.info("Restart notification handler is set");
- this._restartNotificationHandler = handler;
- if (this._needRestart) {
- // Show now using the new handler.
- this._tryShowRestartNotification();
- }
+ this._notificationHandler = handler;
+ this._tryShowNotifications(this._pendingNotifications);
},
/**
* A promise for any ongoing notification prompt task.
*
- * @type {Promise}
+ * Resolves with whether the browser is restarting.
+ *
+ * @type {Promise<boolean>}
*/
_restartNotificationPromise: null,
/**
- * Try show a notification to the user.
+ * Try show notifications to the user.
+ *
+ * If no notification handler has been attached yet, this will queue the
+ * notification for when it is added, if ever.
*
- * If no notification handler has been attached yet, this will do nothing.
+ * @param {object} notifications - The notifications to try and show.
+ * @param {boolean} notifications.restart - Whether to show the restart
+ * notification.
+ * @param {boolean} notifications.custom - Whether to show the custom security
+ * level notification.
*/
- async _tryShowRestartNotification() {
- if (!this._restartNotificationHandler) {
- logger.info("Missing a restart notification handler");
+ async _tryShowNotifications(notifications) {
+ if (!this._notificationHandler) {
+ logger.info("Missing a notification handler", notifications);
// This may be added later in the session.
+ if (notifications.custom) {
+ this._pendingNotifications.custom = true;
+ }
+ if (notifications.restart) {
+ this._pendingNotifications.restart = true;
+ }
return;
}
- const prevPromise = this._restartNotificationPromise;
- let resolve;
- ({ promise: this._restartNotificationPromise, resolve } =
- Promise.withResolvers());
- await prevPromise;
-
- try {
- await this._restartNotificationHandler?.tryRestartBrowser();
- } finally {
- // Allow the notification to be shown again.
- resolve();
+ let isRestarting = false;
+ if (notifications.restart) {
+ const prevPromise = this._restartNotificationPromise;
+ let resolve;
+ ({ promise: this._restartNotificationPromise, resolve } =
+ Promise.withResolvers());
+ await prevPromise;
+
+ try {
+ isRestarting = await this._notificationHandler?.tryRestartBrowser();
+ } finally {
+ // Allow the notification to be shown again.
+ resolve();
+ }
+ }
+ // NOTE: We wait for the restart notification to resolve before showing the
+ // custom warning. We do not show the warning if we are already restarting.
+ if (!isRestarting && notifications.custom) {
+ this._notificationHandler?.showCustomWarning(() => {
+ // User has acknowledged and dismissed the notification.
+ Services.prefs.setBoolPref(this._customWarningDismissedPref, true);
+ });
}
+
+ this._pendingNotifications = {};
},
/**
@@ -658,7 +704,6 @@ export const SecurityLevelPrefs = {
// At the next startup, the custom flag may be cleared if the settings are
// as expected.
Services.prefs.setBoolPref(kCustomPref, true);
- this._needRestart = true;
// NOTE: We need to change the controlled security level preferences in
// response to the desired change in security level. We could either:
@@ -684,6 +729,39 @@ export const SecurityLevelPrefs = {
// still be marked as "custom" because:
// 1. Some preferences require a browser restart to be applied.
// 2. NoScript has not been updated with the new settings.
- this._tryShowRestartNotification();
+
+ this._tryShowNotifications({ restart: true, custom: true });
+ },
+
+ /**
+ * Put the user in the custom security level state and show them a warning
+ * about this state.
+ */
+ setCustomAndWarn() {
+ Services.prefs.setBoolPref(kCustomPref, true);
+ // NOTE: We clear _customWarningDismissedPref because the entry points
+ // for this method imply we should re-warn the user each time.
+ Services.prefs.clearUserPref(this._customWarningDismissedPref);
+ this._tryShowNotifications({ custom: true });
+ },
+
+ /**
+ * If the user is in a custom state, try and notify them of this state.
+ */
+ maybeWarnCustom() {
+ const isCustom = Services.prefs.getBoolPref(kCustomPref, false);
+ if (!isCustom) {
+ // Clear the dismissed preference so the user will be re-shown the
+ // notification when they re-enter the custom state.
+ Services.prefs.clearUserPref(this._customWarningDismissedPref);
+ return;
+ }
+ if (Services.prefs.getBoolPref(this._customWarningDismissedPref, false)) {
+ // Do not warn the user of the custom state if they have already
+ // acknowledged and dismissed this in a previous session.
+ return;
+ }
+
+ this._tryShowNotifications({ custom: true });
},
}; /* Security Level Prefs */
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/f8f4dbc…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/f8f4dbc…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! BB 40925: Implemented the Security Level component
by morgan (@morgan) 09 Sep '25
by morgan (@morgan) 09 Sep '25
09 Sep '25
morgan pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
c1b85777 by Henry Wilkes at 2025-09-09T17:27:56+00:00
fixup! BB 40925: Implemented the Security Level component
TB 43966: Add a warning notification for users when they enter the
custom security level state.
- - - - -
4 changed files:
- browser/components/BrowserComponents.manifest
- browser/modules/SecurityLevelRestartNotification.sys.mjs → browser/modules/SecurityLevelNotification.sys.mjs
- browser/modules/moz.build
- toolkit/components/securitylevel/SecurityLevel.sys.mjs
Changes:
=====================================
browser/components/BrowserComponents.manifest
=====================================
@@ -52,7 +52,7 @@ category browser-first-window-ready resource://gre/modules/SandboxUtils.sys.mjs
#endif
#endif
category browser-first-window-ready moz-src:///browser/modules/ClipboardPrivacy.sys.mjs ClipboardPrivacy.init
-category browser-first-window-ready moz-src:///browser/modules/SecurityLevelRestartNotification.sys.mjs SecurityLevelRestartNotification.ready
+category browser-first-window-ready moz-src:///browser/modules/SecurityLevelNotification.sys.mjs SecurityLevelNotification.ready
category browser-first-window-ready moz-src:///toolkit/modules/DragDropFilter.sys.mjs DragDropFilter.init
category browser-first-window-ready moz-src:///browser/modules/TorSettingsNotification.sys.mjs TorSettingsNotification.ready
category browser-first-window-ready moz-src:///browser/components/onionservices/OnionAliasStore.sys.mjs OnionAliasStore.init
=====================================
browser/modules/SecurityLevelRestartNotification.sys.mjs → browser/modules/SecurityLevelNotification.sys.mjs
=====================================
@@ -15,7 +15,7 @@ ChromeUtils.defineLazyGetter(lazy, "NotificationStrings", function () {
/**
* Interface for showing the security level restart notification on desktop.
*/
-export const SecurityLevelRestartNotification = {
+export const SecurityLevelNotification = {
/**
* Whether we have already been initialised.
*
@@ -31,11 +31,13 @@ export const SecurityLevelRestartNotification = {
return;
}
this._initialized = true;
- lazy.SecurityLevelPrefs.setRestartNotificationHandler(this);
+ lazy.SecurityLevelPrefs.setNotificationHandler(this);
},
/**
* Show the restart notification, and perform the restart if the user agrees.
+ *
+ * @returns {boolean} - Whether we are restarting the browser.
*/
async tryRestartBrowser() {
const [titleText, bodyText, primaryButtonText, secondaryButtonText] =
@@ -69,6 +71,49 @@ export const SecurityLevelRestartNotification = {
Services.startup.quit(
Services.startup.eAttemptQuit | Services.startup.eRestart
);
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Show or re-show the custom security notification.
+ *
+ * @param {Function} userDismissedCallback - The callback for when the user
+ * dismisses the notification.
+ */
+ async showCustomWarning(userDismissedCallback) {
+ const win = lazy.BrowserWindowTracker.getTopWindow();
+ if (!win) {
+ return;
+ }
+ const typeName = "security-level-custom";
+ const existing = win.gNotificationBox.getNotificationWithValue(typeName);
+ if (existing) {
+ win.gNotificationBox.removeNotification(existing);
}
+
+ const buttons = [
+ {
+ "l10n-id": "security-level-panel-open-settings-button",
+ callback() {
+ win.openPreferences("privacy-securitylevel");
+ },
+ },
+ ];
+
+ win.gNotificationBox.appendNotification(
+ typeName,
+ {
+ label: { "l10n-id": "security-level-summary-custom" },
+ priority: win.gNotificationBox.PRIORITY_WARNING_HIGH,
+ eventCallback: event => {
+ if (event === "dismissed") {
+ userDismissedCallback();
+ }
+ },
+ },
+ buttons
+ );
},
};
=====================================
browser/modules/moz.build
=====================================
@@ -150,7 +150,7 @@ EXTRA_JS_MODULES += [
MOZ_SRC_FILES += [
"ContextId.sys.mjs",
"ClipboardPrivacy.sys.mjs",
- "SecurityLevelRestartNotification.sys.mjs",
+ "SecurityLevelNotification.sys.mjs",
"TorSettingsNotification.sys.mjs",
]
=====================================
toolkit/components/securitylevel/SecurityLevel.sys.mjs
=====================================
@@ -226,7 +226,7 @@ var initializeNoScriptControl = () => {
} catch (e) {
logger.error("Could not apply NoScript settings", e);
// Treat as a custom security level for the rest of the session.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}
};
waitForExtensionMessage(noscriptID, a => a.__meta.name === "started").then(
@@ -236,7 +236,7 @@ var initializeNoScriptControl = () => {
} catch (e) {
logger.exception(e);
// Treat as a custom security level for the rest of the session.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}
};
@@ -389,7 +389,12 @@ var initializeSecurityPrefs = function () {
// particular, for the NoScript addon.
Services.prefs.setBoolPref(kCustomPref, false);
Services.prefs.setIntPref(kSliderPref, effectiveIndex);
- } else if (!wasCustom && effectiveIndex !== desiredIndex) {
+ }
+ // Warn the user if they have booted the browser in a custom state, and have
+ // not yet acknowledged it in a previous session.
+ SecurityLevelPrefs.maybeWarnCustom();
+
+ if (!wasCustom && effectiveIndex !== desiredIndex) {
// NOTE: We assume all our controlled preferences require a restart.
// In practice, only a subset of these preferences may actually require a
// restart, so we could switch their values. But we treat them all the same
@@ -436,7 +441,7 @@ var initializeSecurityPrefs = function () {
// properly applied. See tor-browser#43783.
// In the case where it does match a pre-set security level, the custom
// flag will be cleared at the next startup.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}),
});
}
@@ -505,12 +510,27 @@ export class SecurityLevel {
}
/**
- * @typedef {object} SecurityLevelRestartNotificationHandler
+ * @callback SecurityLevelTryRestartBrowserCallback
+ *
+ * @returns {Promise<boolean>} - A promise that resolves when the user has made
+ * a decision. Should return `true` when the browser is now restarting.
+ */
+/**
+ * @callback SecurityLevelShowCustomWarningCallback
*
- * An object that can serve the user a restart notification.
+ * @param {Function} userDismissedCallback - A callback that should be called
+ * if the user has acknowledged and dismissed the notification.
+ */
+/**
+ * @typedef {object} SecurityLevelNotificationHandler
*
- * @property {Function} tryRestartBrowser - The method that should be called to
- * ask the user to restart the browser.
+ * An object that can serve the user notifications.
+ *
+ * @property {SecurityLevelTryRestartBrowserCallback} tryRestartBrowser - The
+ * method that should be called to ask the user to restart the browser.
+ * @property {SecurityLevelShowCustomWarningCallback} showCustomWarning - The
+ * method that should be called to let the user know they have a custom
+ * security level.
*/
/*
@@ -526,6 +546,8 @@ export const SecurityLevelPrefs = {
}),
security_slider_pref: "browser.security_level.security_slider",
security_custom_pref: "browser.security_level.security_custom",
+ _customWarningDismissedPref:
+ "browser.security_level.custom_warning_dismissed",
/**
* The current security level preference.
@@ -581,18 +603,18 @@ export const SecurityLevelPrefs = {
},
/**
- * Whether the browser should be restarted to apply the security level.
+ * The external handler that can show a notification to the user, if any.
*
- * @type {boolean}
+ * @type {?SecurityLevelNotificationHandler}
*/
- _needRestart: false,
+ _notificationHandler: null,
/**
- * The external handler that can show a notification to the user, if any.
+ * The notifications we are waiting for a handler to show.
*
- * @type {?SecurityLevelRestartNotificationHandler}
+ * @type {Set}
*/
- _restartNotificationHandler: null,
+ _pendingNotifications: {},
/**
* Set the external handler for showing notifications to the user.
@@ -600,49 +622,73 @@ export const SecurityLevelPrefs = {
* This should only be called once per session once the handler is ready to
* show a notification, which may occur immediately during this call.
*
- * @param {SecurityLevelRestartNotificationHandler} handler - The new handler
- * to use.
+ * @param {SecurityLevelNotificationHandler} handler - The new handler to use.
*/
- setRestartNotificationHandler(handler) {
+ setNotificationHandler(handler) {
logger.info("Restart notification handler is set");
- this._restartNotificationHandler = handler;
- if (this._needRestart) {
- // Show now using the new handler.
- this._tryShowRestartNotification();
- }
+ this._notificationHandler = handler;
+ this._tryShowNotifications(this._pendingNotifications);
},
/**
* A promise for any ongoing notification prompt task.
*
- * @type {Promise}
+ * Resolves with whether the browser is restarting.
+ *
+ * @type {Promise<boolean>}
*/
_restartNotificationPromise: null,
/**
- * Try show a notification to the user.
+ * Try show notifications to the user.
+ *
+ * If no notification handler has been attached yet, this will queue the
+ * notification for when it is added, if ever.
*
- * If no notification handler has been attached yet, this will do nothing.
+ * @param {object} notifications - The notifications to try and show.
+ * @param {boolean} notifications.restart - Whether to show the restart
+ * notification.
+ * @param {boolean} notifications.custom - Whether to show the custom security
+ * level notification.
*/
- async _tryShowRestartNotification() {
- if (!this._restartNotificationHandler) {
- logger.info("Missing a restart notification handler");
+ async _tryShowNotifications(notifications) {
+ if (!this._notificationHandler) {
+ logger.info("Missing a notification handler", notifications);
// This may be added later in the session.
+ if (notifications.custom) {
+ this._pendingNotifications.custom = true;
+ }
+ if (notifications.restart) {
+ this._pendingNotifications.restart = true;
+ }
return;
}
- const prevPromise = this._restartNotificationPromise;
- let resolve;
- ({ promise: this._restartNotificationPromise, resolve } =
- Promise.withResolvers());
- await prevPromise;
-
- try {
- await this._restartNotificationHandler?.tryRestartBrowser();
- } finally {
- // Allow the notification to be shown again.
- resolve();
+ let isRestarting = false;
+ if (notifications.restart) {
+ const prevPromise = this._restartNotificationPromise;
+ let resolve;
+ ({ promise: this._restartNotificationPromise, resolve } =
+ Promise.withResolvers());
+ await prevPromise;
+
+ try {
+ isRestarting = await this._notificationHandler?.tryRestartBrowser();
+ } finally {
+ // Allow the notification to be shown again.
+ resolve();
+ }
+ }
+ // NOTE: We wait for the restart notification to resolve before showing the
+ // custom warning. We do not show the warning if we are already restarting.
+ if (!isRestarting && notifications.custom) {
+ this._notificationHandler?.showCustomWarning(() => {
+ // User has acknowledged and dismissed the notification.
+ Services.prefs.setBoolPref(this._customWarningDismissedPref, true);
+ });
}
+
+ this._pendingNotifications = {};
},
/**
@@ -658,7 +704,6 @@ export const SecurityLevelPrefs = {
// At the next startup, the custom flag may be cleared if the settings are
// as expected.
Services.prefs.setBoolPref(kCustomPref, true);
- this._needRestart = true;
// NOTE: We need to change the controlled security level preferences in
// response to the desired change in security level. We could either:
@@ -684,6 +729,39 @@ export const SecurityLevelPrefs = {
// still be marked as "custom" because:
// 1. Some preferences require a browser restart to be applied.
// 2. NoScript has not been updated with the new settings.
- this._tryShowRestartNotification();
+
+ this._tryShowNotifications({ restart: true, custom: true });
+ },
+
+ /**
+ * Put the user in the custom security level state and show them a warning
+ * about this state.
+ */
+ setCustomAndWarn() {
+ Services.prefs.setBoolPref(kCustomPref, true);
+ // NOTE: We clear _customWarningDismissedPref because the entry points
+ // for this method imply we should re-warn the user each time.
+ Services.prefs.clearUserPref(this._customWarningDismissedPref);
+ this._tryShowNotifications({ custom: true });
+ },
+
+ /**
+ * If the user is in a custom state, try and notify them of this state.
+ */
+ maybeWarnCustom() {
+ const isCustom = Services.prefs.getBoolPref(kCustomPref, false);
+ if (!isCustom) {
+ // Clear the dismissed preference so the user will be re-shown the
+ // notification when they re-enter the custom state.
+ Services.prefs.clearUserPref(this._customWarningDismissedPref);
+ return;
+ }
+ if (Services.prefs.getBoolPref(this._customWarningDismissedPref, false)) {
+ // Do not warn the user of the custom state if they have already
+ // acknowledged and dismissed this in a previous session.
+ return;
+ }
+
+ this._tryShowNotifications({ custom: true });
},
}; /* Security Level Prefs */
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/c1b8577…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/c1b8577…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

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

[Git][tpo/applications/tor-browser-build][maint-14.5] MB 463: Fix typo in MimeType entry in .desktop file included in deb/rpm package
by Pier Angelo Vendrame (@pierov) 09 Sep '25
by Pier Angelo Vendrame (@pierov) 09 Sep '25
09 Sep '25
Pier Angelo Vendrame pushed to branch maint-14.5 at The Tor Project / Applications / tor-browser-build
Commits:
207612f3 by Nicolas Vigier at 2025-09-09T17:44:21+02:00
MB 463: Fix typo in MimeType entry in .desktop file included in deb/rpm package
- - - - -
1 changed file:
- projects/linux-packages/browser.desktop.in
Changes:
=====================================
projects/linux-packages/browser.desktop.in
=====================================
@@ -10,6 +10,6 @@ Icon=[% c("var/system_pkg/pkg_name") %]
StartupNotify=true
StartupWMClass=[% c("var/display_name") %]
[% IF ! c("var/tor-browser") -%]
-MimeType=text/html;text/xml;application/xhtml_xml;x-scheme-handler/http;x-scheme-handler/https;
+MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;
[% END -%]
Terminal=false
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/2…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/2…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! TB 41435: Add a Tor Browser migration function
by henry (@henry) 09 Sep '25
by henry (@henry) 09 Sep '25
09 Sep '25
henry pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
86b25200 by Henry Wilkes at 2025-09-09T15:34:21+00:00
fixup! TB 41435: Add a Tor Browser migration function
TB 44180: Clear YEC 2024 preference.
- - - - -
1 changed file:
- browser/components/ProfileDataUpgrader.sys.mjs
Changes:
=====================================
browser/components/ProfileDataUpgrader.sys.mjs
=====================================
@@ -993,7 +993,9 @@ export let ProfileDataUpgrader = {
// (tor-browser#43567).
// Version 8: Tor Browser 15.0a2: Remove legacy search addons
// (tor-browser#43111).
- const TBB_MIGRATION_VERSION = 8;
+ // Version 9: Tor Browser 15.0a3: Remove YEC 2024 preference.
+ // (tor-browser#44180)
+ const TBB_MIGRATION_VERSION = 9;
const MIGRATION_PREF = "torbrowser.migration.version";
// If we decide to force updating users to pass through any version
@@ -1093,6 +1095,9 @@ export let ProfileDataUpgrader = {
"wikipedia(a)search.mozilla.org",
]);
}
+ if (currentVersion < 9) {
+ Services.prefs.clearUserPref("torbrowser.homepage.yec2024.message");
+ }
Services.prefs.setIntPref(MIGRATION_PREF, TBB_MIGRATION_VERSION);
},
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/86b2520…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/86b2520…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 09 Sep '25
by Pier Angelo Vendrame (@pierov) 09 Sep '25
09 Sep '25
Pier Angelo Vendrame pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
8998794a by Pier Angelo Vendrame at 2025-09-09T16:00:06+02:00
fixup! Firefox preference overrides.
BB 43959: Switch to Noto Color Emoji on Linux and Windows.
It has better compatibility than Twemoji Mozilla and upstream also did
it in Bug 1939359 (for Linux).
We don't use Segoe UI Emoji on Windows, so we enable Noto Color Emoji
also on Windows.
- - - - -
1 changed file:
- browser/app/profile/001-base-profile.js
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -883,8 +883,9 @@ pref("font.name-list.monospace.x-unicode", "Menlo, Courier New, Noto Sans Baline
#endif
#ifdef XP_WIN
-pref("font.system.whitelist", "Arial, Cambria Math, Consolas, Courier New, Georgia, Lucida Console, MS Gothic, MS ゴシック, MS PGothic, MS Pゴシック, MV Boli, Malgun Gothic, Microsoft Himalaya, Microsoft JhengHei, Microsoft YaHei, 微软雅黑, Segoe UI, SimSun, 宋体, Sylfaen, Tahoma, Times New Roman, Verdana, Twemoji Mozilla, Noto Sans Adlam, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Kannada, Noto Sans Khmer, Noto Sans Javanese, Noto Sans Kayah Li, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans Newa, Noto Sans New Tai Lue, Noto Sans NKo, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols 2, Noto Sans Symbols, Noto Sans Syriac, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh APT, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Tibetan, Noto Serif Yezidi, Noto Naskh Arabic, Noto Sans, Noto Serif, Pyidaungsu");
+pref("font.system.whitelist", "Arial, Cambria Math, Consolas, Courier New, Georgia, Lucida Console, MS Gothic, MS ゴシック, MS PGothic, MS Pゴシック, MV Boli, Malgun Gothic, Microsoft Himalaya, Microsoft JhengHei, Microsoft YaHei, 微软雅黑, Segoe UI, SimSun, 宋体, Sylfaen, Tahoma, Times New Roman, Verdana, Noto Color Emoji, Noto Sans Adlam, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Kannada, Noto Sans Khmer, Noto Sans Javanese, Noto Sans Kayah Li, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans Newa, Noto Sans New Tai Lue, Noto Sans NKo, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols 2, Noto Sans Symbols, Noto Sans Syriac, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh APT, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Tibetan, Noto Serif Yezidi, Noto Naskh Arabic, Noto Sans, Noto Serif, Pyidaungsu, Twemoji Mozilla");
+pref("font.name-list.emoji", "Noto Color Emoji, Twemoji Mozilla");
// Arabic
pref("font.name-list.serif.ar", "Times New Roman, Noto Naskh Arabic");
pref("font.name-list.sans-serif.ar", "Segoe UI, Tahoma, Arial, Noto Naskh Arabic");
@@ -960,8 +961,9 @@ pref("font.name-list.monospace.x-unicode", "Consolas, Noto Sans Balinese, Noto S
#endif
#ifdef XP_LINUX
-pref("font.system.whitelist", "Arimo, Cousine, Noto Naskh Arabic, Noto Sans Adlam, Noto Sans Armenian, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Hebrew, Noto Sans JP, Noto Sans Javanese, Noto Sans KR, Noto Sans Kannada, Noto Sans Kayah Li, Noto Sans Khmer, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans NKo, Noto Sans New Tai Lue, Noto Sans Newa, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans SC, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols, Noto Sans Symbols 2, Noto Sans Syriac, Noto Sans TC, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Thai, Noto Sans Tifinagh, Noto Sans Tifinagh APT, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Armenian, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Hebrew, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Thai, Noto Serif Tibetan, Noto Serif Yezidi, Pyidaungsu, STIX Two Math, Tinos, Twemoji Mozilla");
+pref("font.system.whitelist", "Arimo, Cousine, Noto Color Emoji, Noto Naskh Arabic, Noto Sans Adlam, Noto Sans Armenian, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Hebrew, Noto Sans JP, Noto Sans Javanese, Noto Sans KR, Noto Sans Kannada, Noto Sans Kayah Li, Noto Sans Khmer, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans NKo, Noto Sans New Tai Lue, Noto Sans Newa, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans SC, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols, Noto Sans Symbols 2, Noto Sans Syriac, Noto Sans TC, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Thai, Noto Sans Tifinagh, Noto Sans Tifinagh APT, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Armenian, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Hebrew, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Thai, Noto Serif Tibetan, Noto Serif Yezidi, Pyidaungsu, STIX Two Math, Tinos, Twemoji Mozilla");
+pref("font.name-list.emoji", "Noto Color Emoji, Twemoji Mozilla");
// Arabic
pref("font.name-list.serif.ar", "Noto Naskh Arabic, Tinos");
pref("font.name-list.sans-serif.ar", "Noto Naskh Arabic, Arimo");
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/899…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/899…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 09 Sep '25
by Pier Angelo Vendrame (@pierov) 09 Sep '25
09 Sep '25
Pier Angelo Vendrame pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
8dcb50e7 by Pier Angelo Vendrame at 2025-09-09T15:59:25+02:00
fixup! Firefox preference overrides.
BB 43959: Switch to Noto Color Emoji on Linux and Windows.
It has better compatibility than Twemoji Mozilla and upstream also did
it in Bug 1939359 (for Linux).
We don't use Segoe UI Emoji on Windows, so we enable Noto Color Emoji
also on Windows.
- - - - -
1 changed file:
- browser/app/profile/001-base-profile.js
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -887,8 +887,9 @@ pref("font.name-list.monospace.x-unicode", "Menlo, Courier New, Noto Sans Baline
#endif
#ifdef XP_WIN
-pref("font.system.whitelist", "Arial, Cambria Math, Consolas, Courier New, Georgia, Lucida Console, MS Gothic, MS ゴシック, MS PGothic, MS Pゴシック, MV Boli, Malgun Gothic, Microsoft Himalaya, Microsoft JhengHei, Microsoft YaHei, 微软雅黑, Segoe UI, SimSun, 宋体, Sylfaen, Tahoma, Times New Roman, Verdana, Twemoji Mozilla, Noto Sans Adlam, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Kannada, Noto Sans Khmer, Noto Sans Javanese, Noto Sans Kayah Li, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans Newa, Noto Sans New Tai Lue, Noto Sans NKo, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols 2, Noto Sans Symbols, Noto Sans Syriac, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh APT, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Tibetan, Noto Serif Yezidi, Noto Naskh Arabic, Noto Sans, Noto Serif, Pyidaungsu");
+pref("font.system.whitelist", "Arial, Cambria Math, Consolas, Courier New, Georgia, Lucida Console, MS Gothic, MS ゴシック, MS PGothic, MS Pゴシック, MV Boli, Malgun Gothic, Microsoft Himalaya, Microsoft JhengHei, Microsoft YaHei, 微软雅黑, Segoe UI, SimSun, 宋体, Sylfaen, Tahoma, Times New Roman, Verdana, Noto Color Emoji, Noto Sans Adlam, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Kannada, Noto Sans Khmer, Noto Sans Javanese, Noto Sans Kayah Li, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans Newa, Noto Sans New Tai Lue, Noto Sans NKo, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols 2, Noto Sans Symbols, Noto Sans Syriac, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh APT, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Tibetan, Noto Serif Yezidi, Noto Naskh Arabic, Noto Sans, Noto Serif, Pyidaungsu, Twemoji Mozilla");
+pref("font.name-list.emoji", "Noto Color Emoji, Twemoji Mozilla");
// Arabic
pref("font.name-list.serif.ar", "Times New Roman, Noto Naskh Arabic");
pref("font.name-list.sans-serif.ar", "Segoe UI, Tahoma, Arial, Noto Naskh Arabic");
@@ -964,8 +965,9 @@ pref("font.name-list.monospace.x-unicode", "Consolas, Noto Sans Balinese, Noto S
#endif
#ifdef XP_LINUX
-pref("font.system.whitelist", "Arimo, Cousine, Noto Naskh Arabic, Noto Sans Adlam, Noto Sans Armenian, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Hebrew, Noto Sans JP, Noto Sans Javanese, Noto Sans KR, Noto Sans Kannada, Noto Sans Kayah Li, Noto Sans Khmer, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans NKo, Noto Sans New Tai Lue, Noto Sans Newa, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans SC, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols, Noto Sans Symbols 2, Noto Sans Syriac, Noto Sans TC, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Thai, Noto Sans Tifinagh, Noto Sans Tifinagh APT, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Armenian, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Hebrew, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Thai, Noto Serif Tibetan, Noto Serif Yezidi, Pyidaungsu, STIX Two Math, Tinos, Twemoji Mozilla");
+pref("font.system.whitelist", "Arimo, Cousine, Noto Color Emoji, Noto Naskh Arabic, Noto Sans Adlam, Noto Sans Armenian, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Hebrew, Noto Sans JP, Noto Sans Javanese, Noto Sans KR, Noto Sans Kannada, Noto Sans Kayah Li, Noto Sans Khmer, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans NKo, Noto Sans New Tai Lue, Noto Sans Newa, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans SC, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols, Noto Sans Symbols 2, Noto Sans Syriac, Noto Sans TC, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Thai, Noto Sans Tifinagh, Noto Sans Tifinagh APT, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Armenian, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Hebrew, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Thai, Noto Serif Tibetan, Noto Serif Yezidi, Pyidaungsu, STIX Two Math, Tinos, Twemoji Mozilla");
+pref("font.name-list.emoji", "Noto Color Emoji, Twemoji Mozilla");
// Arabic
pref("font.name-list.serif.ar", "Noto Naskh Arabic, Tinos");
pref("font.name-list.sans-serif.ar", "Noto Naskh Arabic, Arimo");
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/8dcb50e…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/8dcb50e…
You're receiving this email because of your account on gitlab.torproject.org.
1
0