tor-commits
Threads by month
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 214510 discussions
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] 3 commits: fixup! BB 41803: Add some developer tools for working on tor-browser.
by Pier Angelo Vendrame (@pierov) 10 Sep '25
by Pier Angelo Vendrame (@pierov) 10 Sep '25
10 Sep '25
Pier Angelo Vendrame pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
7495e77b by Beatriz Rizental at 2025-09-10T10:34:11+02:00
fixup! BB 41803: Add some developer tools for working on tor-browser.
This reverts commit 36c2b42c9b849182fa60f648639b4ab11b53f537.
- - - - -
72d62388 by Beatriz Rizental at 2025-09-10T10:34:11+02:00
fixup! BB 41803: Add some developer tools for working on tor-browser.
- - - - -
6b49b72e by Beatriz Rizental at 2025-09-10T10:34:11+02:00
fixup! BB 42305: Add script to combine translation files across versions.
- - - - -
0 changed files:
Changes:
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/64132f…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/64132f…
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] 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 mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
6452d953 by Fatih Kilic at 2025-09-09T17:43:19+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/mullvad-browser/-/commit/645…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/645…
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] 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