Commits:
-
ac6d26b2
by Leo Tenenbaum at 2026-06-01T10:28:10+02:00
Bug 2040704 - Respect privacy.spoof_english in datetime input validation messages. r=timhuang,dom-core,hsivonen
Differential Revision: https://phabricator.services.mozilla.com/D301531
-
6f6690e1
by Pier Angelo Vendrame at 2026-06-01T10:34:21+02:00
BB 44999: Fix the privacy panel.
This commit might be dropped in 153.
For 152, it might need adjustments, or we might be able to cherry-pick
upstream's commit (Bug 2042309).
7 changed files:
Changes:
browser/components/preferences/config/privacy.mjs
| ... |
... |
@@ -742,6 +742,13 @@ Preferences.addAll([ |
|
742
|
742
|
},
|
|
743
|
743
|
{ id: "app.normandy.enabled", type: "bool" },
|
|
744
|
744
|
{ id: "browser.privacySegmentation.preferences.show", type: "bool" },
|
|
|
745
|
+
|
|
|
746
|
+ // Preference instances for prefs that we need to monitor while the page is open.
|
|
|
747
|
+ { id: "app.shield.optoutstudies.enabled", type: "bool" },
|
|
|
748
|
+ { id: "browser.discovery.enabled", type: "bool" },
|
|
|
749
|
+ { id: "datareporting.healthreport.uploadEnabled", type: "bool" },
|
|
|
750
|
+ { id: "datareporting.usage.uploadEnabled", type: "bool" },
|
|
|
751
|
+ { id: "dom.private-attribution.submission.enabled", type: "bool" },
|
|
745
|
752
|
]);
|
|
746
|
753
|
|
|
747
|
754
|
if (SECURITY_PRIVACY_STATUS_CARD_ENABLED) {
|
| ... |
... |
@@ -2983,17 +2990,6 @@ Preferences.addSetting({ |
|
2983
|
2990
|
!ipProtectionSubscribedToVpn.value,
|
|
2984
|
2991
|
});
|
|
2985
|
2992
|
|
|
2986
|
|
-// Study opt out
|
|
2987
|
|
-if (lazy.AppConstants.MOZ_DATA_REPORTING) {
|
|
2988
|
|
- Preferences.addAll([
|
|
2989
|
|
- // Preference instances for prefs that we need to monitor while the page is open.
|
|
2990
|
|
- { id: "app.shield.optoutstudies.enabled", type: "bool" },
|
|
2991
|
|
- { id: "browser.discovery.enabled", type: "bool" },
|
|
2992
|
|
- { id: "datareporting.healthreport.uploadEnabled", type: "bool" },
|
|
2993
|
|
- { id: "datareporting.usage.uploadEnabled", type: "bool" },
|
|
2994
|
|
- { id: "dom.private-attribution.submission.enabled", type: "bool" },
|
|
2995
|
|
- ]);
|
|
2996
|
|
-}
|
|
2997
|
2993
|
// Privacy segmentation section
|
|
2998
|
2994
|
Preferences.add({
|
|
2999
|
2995
|
id: "browser.dataFeatureRecommendations.enabled",
|
dom/html/input/DateTimeInputTypes.cpp
| ... |
... |
@@ -109,21 +109,19 @@ bool DateTimeInputTypeBase::HasBadInput() const { |
|
109
|
109
|
return !allEmpty && IsValueEmpty();
|
|
110
|
110
|
}
|
|
111
|
111
|
|
|
112
|
|
-// Format PRExplodedTime according to current locale
|
|
113
|
|
-static bool FormatDateTime(
|
|
|
112
|
+bool DateTimeInputTypeBase::FormatDateTime(
|
|
114
|
113
|
const PRExplodedTime& aTime,
|
|
115
|
114
|
const intl::DateTimeFormat::ComponentsBag& aComponents,
|
|
116
|
|
- nsAString& aFormatted) {
|
|
|
115
|
+ nsAString& aFormatted) const {
|
|
117
|
116
|
// AppDateTimeFormat is not thread-safe.
|
|
118
|
117
|
MOZ_ASSERT(NS_IsMainThread(), "Should only be called from main thread");
|
|
119
|
|
- return NS_SUCCEEDED(
|
|
120
|
|
- intl::AppDateTimeFormat::Format(aComponents, &aTime, aFormatted));
|
|
|
118
|
+ return NS_SUCCEEDED(intl::AppDateTimeFormat::FormatForDocument(
|
|
|
119
|
+ aComponents, &aTime, mInputElement->OwnerDoc(), aFormatted));
|
|
121
|
120
|
}
|
|
122
|
121
|
|
|
123
|
|
-// Format timestamp according to current locale
|
|
124
|
|
-static bool FormatDateTime(
|
|
|
122
|
+bool DateTimeInputTypeBase::FormatDateTime(
|
|
125
|
123
|
double aValue, const intl::DateTimeFormat::ComponentsBag& aComponents,
|
|
126
|
|
- nsAString& aFormatted) {
|
|
|
124
|
+ nsAString& aFormatted) const {
|
|
127
|
125
|
PRExplodedTime exploded;
|
|
128
|
126
|
PRTime time = static_cast<PRTime>(aValue * PR_USEC_PER_MSEC);
|
|
129
|
127
|
PR_ExplodeTime(
|
dom/html/input/DateTimeInputTypes.h
| ... |
... |
@@ -6,6 +6,9 @@ |
|
6
|
6
|
#define mozilla_dom_DateTimeInputTypes_h_
|
|
7
|
7
|
|
|
8
|
8
|
#include "mozilla/dom/InputType.h"
|
|
|
9
|
+#include "mozilla/intl/DateTimeFormat.h"
|
|
|
10
|
+
|
|
|
11
|
+struct PRExplodedTime;
|
|
9
|
12
|
|
|
10
|
13
|
namespace mozilla::dom {
|
|
11
|
14
|
|
| ... |
... |
@@ -39,6 +42,19 @@ class DateTimeInputTypeBase : public InputType { |
|
39
|
42
|
bool GetTimeFromMs(double aValue, uint16_t* aHours, uint16_t* aMinutes,
|
|
40
|
43
|
uint16_t* aSeconds, uint16_t* aMilliseconds) const;
|
|
41
|
44
|
|
|
|
45
|
+ /**
|
|
|
46
|
+ * Format PRExplodedTime according to current locale
|
|
|
47
|
+ */
|
|
|
48
|
+ bool FormatDateTime(const PRExplodedTime& aTime,
|
|
|
49
|
+ const intl::DateTimeFormat::ComponentsBag& aComponents,
|
|
|
50
|
+ nsAString& aFormatted) const;
|
|
|
51
|
+ /**
|
|
|
52
|
+ * Format timestamp according to current locale
|
|
|
53
|
+ */
|
|
|
54
|
+ bool FormatDateTime(double aValue,
|
|
|
55
|
+ const intl::DateTimeFormat::ComponentsBag& aComponents,
|
|
|
56
|
+ nsAString& aFormatted) const;
|
|
|
57
|
+
|
|
42
|
58
|
// Minimum year limited by HTML standard, year >= 1.
|
|
43
|
59
|
static const double kMinimumYear;
|
|
44
|
60
|
// Maximum year limited by ECMAScript date object range, year <= 275760.
|
dom/html/test/browser.toml
| ... |
... |
@@ -51,3 +51,5 @@ support-files = [ |
|
51
|
51
|
"empty.html",
|
|
52
|
52
|
"image_yellow.png",
|
|
53
|
53
|
]
|
|
|
54
|
+
|
|
|
55
|
+["browser_validationmessage_spoof_english.js"] |
dom/html/test/browser_validationmessage_spoof_english.js
|
|
1
|
+/* Any copyright is dedicated to the Public Domain.
|
|
|
2
|
+ https://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
3
|
+
|
|
|
4
|
+"use strict";
|
|
|
5
|
+
|
|
|
6
|
+/*
|
|
|
7
|
+Test for bug 2040704 - datetime input validation messages should use
|
|
|
8
|
+ en_US localization when English spoofing is enabled.
|
|
|
9
|
+*/
|
|
|
10
|
+
|
|
|
11
|
+const originalAvailableLocales = Services.locale.availableLocales;
|
|
|
12
|
+const originalRequestedLocales = Services.locale.requestedLocales;
|
|
|
13
|
+
|
|
|
14
|
+async function runTest(test) {
|
|
|
15
|
+ for (let spoof of [false, true]) {
|
|
|
16
|
+ await SpecialPowers.pushPrefEnv({
|
|
|
17
|
+ set: [
|
|
|
18
|
+ ["privacy.spoof_english", spoof ? 2 : 0],
|
|
|
19
|
+ ["privacy.resistFingerprinting", spoof],
|
|
|
20
|
+ ],
|
|
|
21
|
+ });
|
|
|
22
|
+ let source = `<!DOCTYPE html>
|
|
|
23
|
+<input type="${test.type}" min="${test.min}" value="${test.value}">`;
|
|
|
24
|
+ let result = await BrowserTestUtils.withNewTab(
|
|
|
25
|
+ "data:text/html," + source,
|
|
|
26
|
+ browser => {
|
|
|
27
|
+ return SpecialPowers.spawn(browser, [], () => {
|
|
|
28
|
+ return content.eval(
|
|
|
29
|
+ 'document.querySelector("input").validationMessage'
|
|
|
30
|
+ );
|
|
|
31
|
+ });
|
|
|
32
|
+ }
|
|
|
33
|
+ );
|
|
|
34
|
+ let expectIncludes = test[spoof ? "en" : "de"];
|
|
|
35
|
+ let expectDoesNotInclude = test[spoof ? "de" : "en"];
|
|
|
36
|
+ ok(
|
|
|
37
|
+ result.includes(expectIncludes),
|
|
|
38
|
+ `With spoofing ${spoof ? "enabled" : "disabled"}: expect validationMessage ` +
|
|
|
39
|
+ `to include "${expectIncludes}": "${result}"`
|
|
|
40
|
+ );
|
|
|
41
|
+ ok(
|
|
|
42
|
+ !result.includes(expectDoesNotInclude),
|
|
|
43
|
+ `With spoofing ${spoof ? "enabled" : "disabled"}: expect validationMessage ` +
|
|
|
44
|
+ `to not include "${expectDoesNotInclude}": "${result}"`
|
|
|
45
|
+ );
|
|
|
46
|
+ }
|
|
|
47
|
+}
|
|
|
48
|
+
|
|
|
49
|
+const tests = [
|
|
|
50
|
+ {
|
|
|
51
|
+ type: "date",
|
|
|
52
|
+ min: "2000-01-01",
|
|
|
53
|
+ value: "1999-01-01",
|
|
|
54
|
+ en: "01/01/2000",
|
|
|
55
|
+ de: "01.01.2000",
|
|
|
56
|
+ },
|
|
|
57
|
+ {
|
|
|
58
|
+ type: "time",
|
|
|
59
|
+ min: "16:00",
|
|
|
60
|
+ value: "15:00",
|
|
|
61
|
+ en: "4:00 PM",
|
|
|
62
|
+ de: "16:00",
|
|
|
63
|
+ },
|
|
|
64
|
+ {
|
|
|
65
|
+ type: "datetime-local",
|
|
|
66
|
+ min: "2000-01-01T00:00",
|
|
|
67
|
+ value: "1999-01-01T00:00",
|
|
|
68
|
+ en: "01/01/2000",
|
|
|
69
|
+ de: "01.01.2000",
|
|
|
70
|
+ },
|
|
|
71
|
+];
|
|
|
72
|
+
|
|
|
73
|
+add_task(() => {
|
|
|
74
|
+ Services.locale.availableLocales = ["de-DE"];
|
|
|
75
|
+ Services.locale.requestedLocales = ["de-DE"];
|
|
|
76
|
+});
|
|
|
77
|
+
|
|
|
78
|
+for (let test of tests) {
|
|
|
79
|
+ add_task(() => runTest(test));
|
|
|
80
|
+}
|
|
|
81
|
+
|
|
|
82
|
+add_task(() => {
|
|
|
83
|
+ // restore previous locales
|
|
|
84
|
+ Services.locale.availableLocales = originalAvailableLocales;
|
|
|
85
|
+ Services.locale.requestedLocales = originalRequestedLocales;
|
|
|
86
|
+}); |
intl/locale/AppDateTimeFormat.cpp
| ... |
... |
@@ -10,6 +10,8 @@ |
|
10
|
10
|
#include "mozilla/intl/LocaleService.h"
|
|
11
|
11
|
#include "OSPreferences.h"
|
|
12
|
12
|
#include "mozIOSPreferences.h"
|
|
|
13
|
+#include "nsContentUtils.h"
|
|
|
14
|
+#include "nsRFPService.h"
|
|
13
|
15
|
#ifdef DEBUG
|
|
14
|
16
|
# include "nsThreadManager.h"
|
|
15
|
17
|
#endif
|
| ... |
... |
@@ -64,6 +66,14 @@ nsresult AppDateTimeFormat::Format(const DateTimeFormat::StyleBag& aStyle, |
|
64
|
66
|
nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag,
|
|
65
|
67
|
const PRExplodedTime* aExplodedTime,
|
|
66
|
68
|
nsAString& aStringOut) {
|
|
|
69
|
+ return FormatForDocument(aBag, aExplodedTime, nullptr, aStringOut);
|
|
|
70
|
+}
|
|
|
71
|
+
|
|
|
72
|
+/*static*/
|
|
|
73
|
+nsresult AppDateTimeFormat::FormatForDocument(
|
|
|
74
|
+ const DateTimeFormat::ComponentsBag& aBag,
|
|
|
75
|
+ const PRExplodedTime* aExplodedTime, const dom::Document* aForDocument,
|
|
|
76
|
+ nsAString& aStringOut) {
|
|
67
|
77
|
// set up locale data
|
|
68
|
78
|
nsresult rv = Initialize();
|
|
69
|
79
|
if (NS_FAILED(rv)) {
|
| ... |
... |
@@ -75,12 +85,17 @@ nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag, |
|
75
|
85
|
nsAutoString timeZoneID;
|
|
76
|
86
|
BuildTimeZoneString(aExplodedTime->tm_params, timeZoneID);
|
|
77
|
87
|
|
|
78
|
|
- auto genResult = DateTimePatternGenerator::TryCreate(sLocale->get());
|
|
|
88
|
+ const bool spoofEnglish =
|
|
|
89
|
+ aForDocument && nsContentUtils::ShouldResistFingerprinting(
|
|
|
90
|
+ aForDocument, mozilla::RFPTarget::JSLocale);
|
|
|
91
|
+ const nsCString& locale =
|
|
|
92
|
+ spoofEnglish ? nsRFPService::GetSpoofedJSLocale() : *sLocale;
|
|
|
93
|
+ auto genResult = DateTimePatternGenerator::TryCreate(locale.get());
|
|
79
|
94
|
NS_ENSURE_TRUE(genResult.isOk(), NS_ERROR_FAILURE);
|
|
80
|
95
|
auto dateTimePatternGenerator = genResult.unwrap();
|
|
81
|
96
|
|
|
82
|
97
|
auto result = DateTimeFormat::TryCreateFromComponents(
|
|
83
|
|
- *sLocale, aBag, dateTimePatternGenerator.get(),
|
|
|
98
|
+ locale, aBag, dateTimePatternGenerator.get(),
|
|
84
|
99
|
Some(Span<const char16_t>(timeZoneID.Data(), timeZoneID.Length())));
|
|
85
|
100
|
NS_ENSURE_TRUE(result.isOk(), NS_ERROR_FAILURE);
|
|
86
|
101
|
auto dateTimeFormat = result.unwrap();
|
intl/locale/AppDateTimeFormat.h
| ... |
... |
@@ -13,6 +13,10 @@ |
|
13
|
13
|
#include "prtime.h"
|
|
14
|
14
|
#include "mozilla/intl/DateTimeFormat.h"
|
|
15
|
15
|
|
|
|
16
|
+namespace mozilla::dom {
|
|
|
17
|
+class Document;
|
|
|
18
|
+}
|
|
|
19
|
+
|
|
16
|
20
|
namespace mozilla::intl {
|
|
17
|
21
|
|
|
18
|
22
|
/**
|
| ... |
... |
@@ -48,6 +52,15 @@ class AppDateTimeFormat { |
|
48
|
52
|
const PRExplodedTime* aExplodedTime,
|
|
49
|
53
|
nsAString& aStringOut);
|
|
50
|
54
|
|
|
|
55
|
+ /**
|
|
|
56
|
+ * Format a DateTime for a document, respecting the privacy.spoof_english
|
|
|
57
|
+ * preference.
|
|
|
58
|
+ */
|
|
|
59
|
+ static nsresult FormatForDocument(const DateTimeFormat::ComponentsBag& aStyle,
|
|
|
60
|
+ const PRExplodedTime* aExplodedTime,
|
|
|
61
|
+ const dom::Document* aForDocument,
|
|
|
62
|
+ nsAString& aStringOut);
|
|
|
63
|
+
|
|
51
|
64
|
/**
|
|
52
|
65
|
* If the app locale changes, the cached locale needs to be reset.
|
|
53
|
66
|
*/
|
|