Pier Angelo Vendrame pushed to branch tor-browser-151.0a1-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
-
ac6d26b2
by Leo Tenenbaum at 2026-06-01T10:28:10+02:00
-
6f6690e1
by Pier Angelo Vendrame at 2026-06-01T10:34:21+02:00
7 changed files:
- browser/components/preferences/config/privacy.mjs
- dom/html/input/DateTimeInputTypes.cpp
- dom/html/input/DateTimeInputTypes.h
- dom/html/test/browser.toml
- + dom/html/test/browser_validationmessage_spoof_english.js
- intl/locale/AppDateTimeFormat.cpp
- intl/locale/AppDateTimeFormat.h
Changes:
| ... | ... | @@ -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",
|
| ... | ... | @@ -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(
|
| ... | ... | @@ -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.
|
| ... | ... | @@ -51,3 +51,5 @@ support-files = [ |
| 51 | 51 | "empty.html",
|
| 52 | 52 | "image_yellow.png",
|
| 53 | 53 | ]
|
| 54 | + |
|
| 55 | +["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 | +}); |
| ... | ... | @@ -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();
|
| ... | ... | @@ -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 | */
|