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
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:
... | ... | @@ -2844,7 +2844,7 @@ bool nsContentUtils::ShouldResistFingerprinting_dangerous( |
2844 | 2844 | }
|
2845 | 2845 | |
2846 | 2846 | // Web extension principals are also excluded
|
2847 | - if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
|
|
2847 | + if (NS_IsMainThread() && BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
|
|
2848 | 2848 | MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
|
2849 | 2849 | ("Inside ShouldResistFingerprinting(nsIPrincipal*)"
|
2850 | 2850 | " and AddonPolicy said false"));
|
... | ... | @@ -1423,7 +1423,7 @@ JSObject* VideoFrame::WrapObject(JSContext* aCx, |
1423 | 1423 | |
1424 | 1424 | /* static */
|
1425 | 1425 | bool VideoFrame::PrefEnabled(JSContext* aCx, JSObject* aObj) {
|
1426 | - return StaticPrefs::dom_media_webcodecs_enabled() ||
|
|
1426 | + return nsRFPService::ExposeWebCodecsAPI(aCx, aObj) &&
|
|
1427 | 1427 | StaticPrefs::dom_media_webcodecs_image_decoder_enabled();
|
1428 | 1428 | }
|
1429 | 1429 |
... | ... | @@ -16,4 +16,13 @@ scheme = "https" |
16 | 16 | ["test_imageDecoder_desiredSize.html"]
|
17 | 17 | scheme = "https"
|
18 | 18 | |
19 | +["test_rfp_api_disabling_disabled.html"]
|
|
20 | +scheme = "https"
|
|
21 | + |
|
22 | +["test_rfp_api_disabling_enabled.html"]
|
|
23 | +scheme = "https"
|
|
24 | + |
|
25 | +["test_rfp_api_disabling_exemption.html"]
|
|
26 | +scheme = "https"
|
|
27 | + |
|
19 | 28 | ["test_videoFrame_mismatched_codedSize.html"] |
1 | +<!DOCTYPE html>
|
|
2 | +<html>
|
|
3 | +<head>
|
|
4 | +<title></title>
|
|
5 | +<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
6 | +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
|
7 | +</head>
|
|
8 | +<body>
|
|
9 | +<script>
|
|
10 | +const apis = [
|
|
11 | + "AudioData",
|
|
12 | + "AudioDecoder",
|
|
13 | + "AudioEncoder",
|
|
14 | + "EncodedAudioChunk",
|
|
15 | + "EncodedVideoChunk",
|
|
16 | + "ImageDecoder",
|
|
17 | + "ImageTrack",
|
|
18 | + "ImageTrackList",
|
|
19 | + "VideoColorSpace",
|
|
20 | + "VideoDecoder",
|
|
21 | + "VideoEncoder",
|
|
22 | + "VideoFrame",
|
|
23 | +];
|
|
24 | + |
|
25 | +function enabledAPIs() {
|
|
26 | + return apis.filter(api => typeof window[api] !== "undefined");
|
|
27 | +}
|
|
28 | + |
|
29 | +function enabledAPIsWorker() {
|
|
30 | + const code = `
|
|
31 | + onmessage = e => {
|
|
32 | + const apis = ${JSON.stringify(apis)};
|
|
33 | + postMessage(apis.filter(api => typeof self[api] !== "undefined"));
|
|
34 | + };`;
|
|
35 | + const blob = new Blob([code], { type: "application/javascript" });
|
|
36 | + const worker = new Worker(URL.createObjectURL(blob));
|
|
37 | + |
|
38 | + return new Promise((resolve) => {
|
|
39 | + worker.addEventListener("message", async (e) => {
|
|
40 | + worker.terminate();
|
|
41 | + resolve(e.data);
|
|
42 | + });
|
|
43 | + |
|
44 | + worker.postMessage({});
|
|
45 | + });
|
|
46 | +}
|
|
47 | + |
|
48 | +add_setup(async () => {
|
|
49 | + await SpecialPowers.pushPrefEnv({
|
|
50 | + set: [
|
|
51 | + ["dom.media.webcodecs.enabled", true],
|
|
52 | + ["dom.media.webcodecs.image-decoder.enabled", true]
|
|
53 | + ],
|
|
54 | + });
|
|
55 | +});
|
|
56 | + |
|
57 | +add_task(async () => {
|
|
58 | + is(enabledAPIs().length, apis.length, true, "All WebCodecs APIs should be enabled");
|
|
59 | + is(
|
|
60 | + (await enabledAPIsWorker()).length,
|
|
61 | + apis.length,
|
|
62 | + "All WebCodecs APIs should be enabled in workers too"
|
|
63 | + );
|
|
64 | +});
|
|
65 | +</script>
|
|
66 | +</body>
|
|
67 | +</html> |
1 | +<!DOCTYPE html>
|
|
2 | +<html>
|
|
3 | +<head>
|
|
4 | +<title></title>
|
|
5 | +<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
6 | +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
|
7 | +</head>
|
|
8 | +<body>
|
|
9 | +<script>
|
|
10 | +const apis = [
|
|
11 | + "AudioData",
|
|
12 | + "AudioDecoder",
|
|
13 | + "AudioEncoder",
|
|
14 | + "EncodedAudioChunk",
|
|
15 | + "EncodedVideoChunk",
|
|
16 | + "ImageDecoder",
|
|
17 | + "ImageTrack",
|
|
18 | + "ImageTrackList",
|
|
19 | + "VideoColorSpace",
|
|
20 | + "VideoDecoder",
|
|
21 | + "VideoEncoder",
|
|
22 | + "VideoFrame",
|
|
23 | +];
|
|
24 | + |
|
25 | +function enabledAPIs() {
|
|
26 | + return apis.filter(api => typeof window[api] !== "undefined");
|
|
27 | +}
|
|
28 | + |
|
29 | +function enabledAPIsWorker() {
|
|
30 | + const code = `
|
|
31 | + onmessage = e => {
|
|
32 | + const apis = ${JSON.stringify(apis)};
|
|
33 | + postMessage(apis.filter(api => typeof self[api] !== "undefined"));
|
|
34 | + };`;
|
|
35 | + const blob = new Blob([code], { type: "application/javascript" });
|
|
36 | + const worker = new Worker(URL.createObjectURL(blob));
|
|
37 | + |
|
38 | + return new Promise((resolve) => {
|
|
39 | + worker.addEventListener("message", async (e) => {
|
|
40 | + worker.terminate();
|
|
41 | + resolve(e.data);
|
|
42 | + });
|
|
43 | + |
|
44 | + worker.postMessage({});
|
|
45 | + });
|
|
46 | +}
|
|
47 | + |
|
48 | +add_setup(async () => {
|
|
49 | + await SpecialPowers.pushPrefEnv({
|
|
50 | + set: [
|
|
51 | + ["dom.media.webcodecs.enabled", true],
|
|
52 | + ["dom.media.webcodecs.image-decoder.enabled", true],
|
|
53 | + ["privacy.fingerprintingProtection", true],
|
|
54 | + ["privacy.fingerprintingProtection.overrides", "-AllTargets,+WebCodecs"],
|
|
55 | + ],
|
|
56 | + });
|
|
57 | +});
|
|
58 | + |
|
59 | +add_task(async () => {
|
|
60 | + is(enabledAPIs().length, 0, true, "All WebCodecs APIs should be disabled");
|
|
61 | + is(
|
|
62 | + (await enabledAPIsWorker()).length,
|
|
63 | + 0,
|
|
64 | + "All WebCodecs APIs should be disabled in workers too"
|
|
65 | + );
|
|
66 | +});
|
|
67 | +</script>
|
|
68 | +</body>
|
|
69 | +</html> |
1 | +<!DOCTYPE html>
|
|
2 | +<html>
|
|
3 | +<head>
|
|
4 | +<title></title>
|
|
5 | +<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
6 | +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
|
7 | +</head>
|
|
8 | +<body>
|
|
9 | +<script>
|
|
10 | +const apis = [
|
|
11 | + "AudioData",
|
|
12 | + "AudioDecoder",
|
|
13 | + "AudioEncoder",
|
|
14 | + "EncodedAudioChunk",
|
|
15 | + "EncodedVideoChunk",
|
|
16 | + "ImageDecoder",
|
|
17 | + "ImageTrack",
|
|
18 | + "ImageTrackList",
|
|
19 | + "VideoColorSpace",
|
|
20 | + "VideoDecoder",
|
|
21 | + "VideoEncoder",
|
|
22 | + "VideoFrame",
|
|
23 | +];
|
|
24 | + |
|
25 | +function enabledAPIs() {
|
|
26 | + return apis.filter(api => typeof window[api] !== "undefined");
|
|
27 | +}
|
|
28 | + |
|
29 | +function enabledAPIsWorker() {
|
|
30 | + const code = `
|
|
31 | + onmessage = e => {
|
|
32 | + const apis = ${JSON.stringify(apis)};
|
|
33 | + postMessage(apis.filter(api => typeof self[api] !== "undefined"));
|
|
34 | + };`;
|
|
35 | + const blob = new Blob([code], { type: "application/javascript" });
|
|
36 | + const worker = new Worker(URL.createObjectURL(blob));
|
|
37 | + |
|
38 | + return new Promise((resolve) => {
|
|
39 | + worker.addEventListener("message", async (e) => {
|
|
40 | + worker.terminate();
|
|
41 | + resolve(e.data);
|
|
42 | + });
|
|
43 | + |
|
44 | + worker.postMessage({});
|
|
45 | + });
|
|
46 | +}
|
|
47 | + |
|
48 | +add_setup(async () => {
|
|
49 | + await SpecialPowers.pushPrefEnv({
|
|
50 | + set: [
|
|
51 | + ["dom.media.webcodecs.enabled", true],
|
|
52 | + ["dom.media.webcodecs.image-decoder.enabled", true],
|
|
53 | + ["privacy.fingerprintingProtection", true],
|
|
54 | + ["privacy.fingerprintingProtection.overrides", "-AllTargets,+WebCodecs"],
|
|
55 | + ["privacy.resistFingerprinting.exemptedDomains", location.hostname]
|
|
56 | + ],
|
|
57 | + });
|
|
58 | +});
|
|
59 | + |
|
60 | +add_task(async () => {
|
|
61 | + is(enabledAPIs().length, apis.length, true, "All WebCodecs APIs should be disabled");
|
|
62 | + is(
|
|
63 | + (await enabledAPIsWorker()).length,
|
|
64 | + apis.length,
|
|
65 | + "All WebCodecs APIs should be disabled in workers too"
|
|
66 | + );
|
|
67 | +});
|
|
68 | +</script>
|
|
69 | +</body>
|
|
70 | +</html> |
... | ... | @@ -9,7 +9,7 @@ |
9 | 9 | |
10 | 10 | // [Serializable, Transferable] are implemented without adding attributes here,
|
11 | 11 | // but directly with {Read,Write}StructuredClone and Transfer/FromTransfered.
|
12 | -[Exposed=(Window,DedicatedWorker), Pref="dom.media.webcodecs.enabled"]
|
|
12 | +[Exposed=(Window,DedicatedWorker), Func="nsRFPService::ExposeWebCodecsAPI"]
|
|
13 | 13 | interface AudioData {
|
14 | 14 | [Throws]
|
15 | 15 | constructor(AudioDataInit init);
|
... | ... | @@ -7,7 +7,7 @@ |
7 | 7 | * https://w3c.github.io/webcodecs/#audiodecoder
|
8 | 8 | */
|
9 | 9 | |
10 | -[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
|
|
10 | +[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
|
|
11 | 11 | interface AudioDecoder : EventTarget {
|
12 | 12 | [Throws]
|
13 | 13 | constructor(AudioDecoderInit init);
|
... | ... | @@ -42,7 +42,7 @@ dictionary OpusEncoderConfig { |
42 | 42 | boolean usedtx = false;
|
43 | 43 | };
|
44 | 44 | |
45 | -[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
|
|
45 | +[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
|
|
46 | 46 | interface AudioEncoder : EventTarget {
|
47 | 47 | [Throws]
|
48 | 48 | constructor(AudioEncoderInit init);
|
... | ... | @@ -8,7 +8,7 @@ |
8 | 8 | */
|
9 | 9 | |
10 | 10 | // [Serializable] is implemented without adding attribute here.
|
11 | -[Exposed=(Window,DedicatedWorker), Pref="dom.media.webcodecs.enabled"]
|
|
11 | +[Exposed=(Window,DedicatedWorker), Func="nsRFPService::ExposeWebCodecsAPI"]
|
|
12 | 12 | interface EncodedAudioChunk {
|
13 | 13 | [Throws]
|
14 | 14 | constructor(EncodedAudioChunkInit init);
|
... | ... | @@ -8,7 +8,7 @@ |
8 | 8 | */
|
9 | 9 | |
10 | 10 | // [Serializable] is implemented without adding attribute here.
|
11 | -[Exposed=(Window,DedicatedWorker), Pref="dom.media.webcodecs.enabled"]
|
|
11 | +[Exposed=(Window,DedicatedWorker), Func="nsRFPService::ExposeWebCodecsAPI"]
|
|
12 | 12 | interface EncodedVideoChunk {
|
13 | 13 | [Throws]
|
14 | 14 | constructor(EncodedVideoChunkInit init);
|
... | ... | @@ -30,7 +30,7 @@ dictionary ImageDecodeResult { |
30 | 30 | |
31 | 31 | [Exposed=(Window,DedicatedWorker),
|
32 | 32 | SecureContext,
|
33 | - Pref="dom.media.webcodecs.image-decoder.enabled"]
|
|
33 | + Func="nsRFPService::ExposeWebCodecsAPIImageDecoder"]
|
|
34 | 34 | interface ImageTrack {
|
35 | 35 | readonly attribute boolean animated;
|
36 | 36 | readonly attribute unsigned long frameCount;
|
... | ... | @@ -40,7 +40,7 @@ interface ImageTrack { |
40 | 40 | |
41 | 41 | [Exposed=(Window,DedicatedWorker),
|
42 | 42 | SecureContext,
|
43 | - Pref="dom.media.webcodecs.image-decoder.enabled"]
|
|
43 | + Func="nsRFPService::ExposeWebCodecsAPIImageDecoder"]
|
|
44 | 44 | interface ImageTrackList {
|
45 | 45 | getter ImageTrack (unsigned long index);
|
46 | 46 | |
... | ... | @@ -52,7 +52,7 @@ interface ImageTrackList { |
52 | 52 | |
53 | 53 | [Exposed=(Window,DedicatedWorker),
|
54 | 54 | SecureContext,
|
55 | - Pref="dom.media.webcodecs.image-decoder.enabled"]
|
|
55 | + Func="nsRFPService::ExposeWebCodecsAPIImageDecoder"]
|
|
56 | 56 | interface ImageDecoder {
|
57 | 57 | [Throws]
|
58 | 58 | constructor(ImageDecoderInit init);
|
... | ... | @@ -7,7 +7,7 @@ |
7 | 7 | * https://w3c.github.io/webcodecs/#videodecoder
|
8 | 8 | */
|
9 | 9 | |
10 | -[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
|
|
10 | +[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
|
|
11 | 11 | interface VideoDecoder : EventTarget {
|
12 | 12 | [Throws]
|
13 | 13 | constructor(VideoDecoderInit init);
|
... | ... | @@ -12,7 +12,7 @@ |
12 | 12 | * commented with a link of the document in which the member is listed.
|
13 | 13 | */
|
14 | 14 | |
15 | -[Exposed=(Window,DedicatedWorker), SecureContext, Pref="dom.media.webcodecs.enabled"]
|
|
15 | +[Exposed=(Window,DedicatedWorker), SecureContext, Func="nsRFPService::ExposeWebCodecsAPI"]
|
|
16 | 16 | interface VideoEncoder : EventTarget {
|
17 | 17 | [Throws]
|
18 | 18 | constructor(VideoEncoderInit init);
|
... | ... | @@ -101,6 +101,7 @@ ITEM_VALUE(JSLocalePrompt, 67) |
101 | 101 | ITEM_VALUE(ScreenAvailToResolution, 68)
|
102 | 102 | ITEM_VALUE(UseHardcodedFontSubstitutes, 69)
|
103 | 103 | ITEM_VALUE(DiskStorageLimit, 70)
|
104 | +ITEM_VALUE(WebCodecs, 71)
|
|
104 | 105 | |
105 | 106 | |
106 | 107 | // !!! Don't forget to update kDefaultFingerprintingProtections in nsRFPService.cpp
|
... | ... | @@ -2697,3 +2697,48 @@ uint64_t nsRFPService::GetSpoofedStorageLimit() { |
2697 | 2697 | |
2698 | 2698 | return limit;
|
2699 | 2699 | }
|
2700 | + |
|
2701 | +/* static */
|
|
2702 | +bool nsRFPService::ExposeWebCodecsAPI(JSContext* aCx, JSObject* aObj) {
|
|
2703 | + if (!StaticPrefs::dom_media_webcodecs_enabled()) {
|
|
2704 | + return false;
|
|
2705 | + }
|
|
2706 | + |
|
2707 | + return !IsWebCodecsRFPTargetEnabled(aCx);
|
|
2708 | +}
|
|
2709 | + |
|
2710 | +/* static */
|
|
2711 | +bool nsRFPService::ExposeWebCodecsAPIImageDecoder(JSContext* aCx,
|
|
2712 | + JSObject* aObj) {
|
|
2713 | + if (!StaticPrefs::dom_media_webcodecs_image_decoder_enabled()) {
|
|
2714 | + return false;
|
|
2715 | + }
|
|
2716 | + |
|
2717 | + return !IsWebCodecsRFPTargetEnabled(aCx);
|
|
2718 | +}
|
|
2719 | + |
|
2720 | +/* static */
|
|
2721 | +bool nsRFPService::IsWebCodecsRFPTargetEnabled(JSContext* aCx) {
|
|
2722 | + if (!nsContentUtils::ShouldResistFingerprinting("Efficiency check",
|
|
2723 | + RFPTarget::WebCodecs)) {
|
|
2724 | + return false;
|
|
2725 | + }
|
|
2726 | + |
|
2727 | + // We know that the RFPTarget::WebCodecs is enabled, check if principal
|
|
2728 | + // is exempted.
|
|
2729 | + |
|
2730 | + // VideoFrame::PrefEnabled function can be called without a JSContext.
|
|
2731 | + if (!aCx) {
|
|
2732 | + return true;
|
|
2733 | + }
|
|
2734 | + |
|
2735 | + // Once bug 1973966 is resolved, we can replace this section with just
|
|
2736 | + // nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
|
|
2737 | + JS::Realm* realm = js::GetContextRealm(aCx);
|
|
2738 | + MOZ_ASSERT(realm);
|
|
2739 | + JSPrincipals* principals = JS::GetRealmPrincipals(realm);
|
|
2740 | + nsIPrincipal* principal = nsJSPrincipals::get(principals);
|
|
2741 | + |
|
2742 | + return nsContentUtils::ShouldResistFingerprinting_dangerous(
|
|
2743 | + principal, "Principal is the best context we have", RFPTarget::WebCodecs);
|
|
2744 | +} |
... | ... | @@ -416,6 +416,9 @@ class nsRFPService final : public nsIObserver, public nsIRFPService { |
416 | 416 | |
417 | 417 | static uint64_t GetSpoofedStorageLimit();
|
418 | 418 | |
419 | + static bool ExposeWebCodecsAPI(JSContext* aCx, JSObject* aObj);
|
|
420 | + static bool ExposeWebCodecsAPIImageDecoder(JSContext* aCx, JSObject* aObj);
|
|
421 | + |
|
419 | 422 | private:
|
420 | 423 | nsresult Init();
|
421 | 424 | |
... | ... | @@ -527,6 +530,8 @@ class nsRFPService final : public nsIObserver, public nsIRFPService { |
527 | 530 | static bool IsTargetActiveForMode(RFPTarget aTarget,
|
528 | 531 | FingerprintingProtectionType aMode);
|
529 | 532 | |
533 | + static bool IsWebCodecsRFPTargetEnabled(JSContext* aCx);
|
|
534 | + |
|
530 | 535 | static nsCString* sExemptedDomainsLowercase;
|
531 | 536 | };
|
532 | 537 |