Loading dom/html/input/DateTimeInputTypes.cpp +6 −8 Original line number Diff line number Diff line Loading @@ -109,21 +109,19 @@ bool DateTimeInputTypeBase::HasBadInput() const { return !allEmpty && IsValueEmpty(); } // Format PRExplodedTime according to current locale static bool FormatDateTime( bool DateTimeInputTypeBase::FormatDateTime( const PRExplodedTime& aTime, const intl::DateTimeFormat::ComponentsBag& aComponents, nsAString& aFormatted) { nsAString& aFormatted) const { // AppDateTimeFormat is not thread-safe. MOZ_ASSERT(NS_IsMainThread(), "Should only be called from main thread"); return NS_SUCCEEDED( intl::AppDateTimeFormat::Format(aComponents, &aTime, aFormatted)); return NS_SUCCEEDED(intl::AppDateTimeFormat::FormatForDocument( aComponents, &aTime, mInputElement->OwnerDoc(), aFormatted)); } // Format timestamp according to current locale static bool FormatDateTime( bool DateTimeInputTypeBase::FormatDateTime( double aValue, const intl::DateTimeFormat::ComponentsBag& aComponents, nsAString& aFormatted) { nsAString& aFormatted) const { PRExplodedTime exploded; PRTime time = static_cast<PRTime>(aValue * PR_USEC_PER_MSEC); PR_ExplodeTime( Loading dom/html/input/DateTimeInputTypes.h +16 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,9 @@ #define mozilla_dom_DateTimeInputTypes_h_ #include "mozilla/dom/InputType.h" #include "mozilla/intl/DateTimeFormat.h" struct PRExplodedTime; namespace mozilla::dom { Loading Loading @@ -39,6 +42,19 @@ class DateTimeInputTypeBase : public InputType { bool GetTimeFromMs(double aValue, uint16_t* aHours, uint16_t* aMinutes, uint16_t* aSeconds, uint16_t* aMilliseconds) const; /** * Format PRExplodedTime according to current locale */ bool FormatDateTime(const PRExplodedTime& aTime, const intl::DateTimeFormat::ComponentsBag& aComponents, nsAString& aFormatted) const; /** * Format timestamp according to current locale */ bool FormatDateTime(double aValue, const intl::DateTimeFormat::ComponentsBag& aComponents, nsAString& aFormatted) const; // Minimum year limited by HTML standard, year >= 1. static const double kMinimumYear; // Maximum year limited by ECMAScript date object range, year <= 275760. Loading dom/html/test/browser.toml +2 −0 Original line number Diff line number Diff line Loading @@ -51,3 +51,5 @@ support-files = [ "empty.html", "image_yellow.png", ] ["browser_validationmessage_spoof_english.js"] dom/html/test/browser_validationmessage_spoof_english.js 0 → 100644 +86 −0 Original line number Diff line number Diff line /* Any copyright is dedicated to the Public Domain. https://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /* Test for bug 2040704 - datetime input validation messages should use en_US localization when English spoofing is enabled. */ const originalAvailableLocales = Services.locale.availableLocales; const originalRequestedLocales = Services.locale.requestedLocales; async function runTest(test) { for (let spoof of [false, true]) { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.spoof_english", spoof ? 2 : 0], ["privacy.resistFingerprinting", spoof], ], }); let source = `<!DOCTYPE html> <input type="${test.type}" min="${test.min}" value="${test.value}">`; let result = await BrowserTestUtils.withNewTab( "data:text/html," + source, browser => { return SpecialPowers.spawn(browser, [], () => { return content.eval( 'document.querySelector("input").validationMessage' ); }); } ); let expectIncludes = test[spoof ? "en" : "de"]; let expectDoesNotInclude = test[spoof ? "de" : "en"]; ok( result.includes(expectIncludes), `With spoofing ${spoof ? "enabled" : "disabled"}: expect validationMessage ` + `to include "${expectIncludes}": "${result}"` ); ok( !result.includes(expectDoesNotInclude), `With spoofing ${spoof ? "enabled" : "disabled"}: expect validationMessage ` + `to not include "${expectDoesNotInclude}": "${result}"` ); } } const tests = [ { type: "date", min: "2000-01-01", value: "1999-01-01", en: "01/01/2000", de: "01.01.2000", }, { type: "time", min: "16:00", value: "15:00", en: "4:00 PM", de: "16:00", }, { type: "datetime-local", min: "2000-01-01T00:00", value: "1999-01-01T00:00", en: "01/01/2000", de: "01.01.2000", }, ]; add_task(() => { Services.locale.availableLocales = ["de-DE"]; Services.locale.requestedLocales = ["de-DE"]; }); for (let test of tests) { add_task(() => runTest(test)); } add_task(() => { // restore previous locales Services.locale.availableLocales = originalAvailableLocales; Services.locale.requestedLocales = originalRequestedLocales; }); intl/locale/AppDateTimeFormat.cpp +17 −2 Original line number Diff line number Diff line Loading @@ -10,6 +10,8 @@ #include "mozilla/intl/LocaleService.h" #include "OSPreferences.h" #include "mozIOSPreferences.h" #include "nsContentUtils.h" #include "nsRFPService.h" #ifdef DEBUG # include "nsThreadManager.h" #endif Loading Loading @@ -64,6 +66,14 @@ nsresult AppDateTimeFormat::Format(const DateTimeFormat::StyleBag& aStyle, nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag, const PRExplodedTime* aExplodedTime, nsAString& aStringOut) { return FormatForDocument(aBag, aExplodedTime, nullptr, aStringOut); } /*static*/ nsresult AppDateTimeFormat::FormatForDocument( const DateTimeFormat::ComponentsBag& aBag, const PRExplodedTime* aExplodedTime, const dom::Document* aForDocument, nsAString& aStringOut) { // set up locale data nsresult rv = Initialize(); if (NS_FAILED(rv)) { Loading @@ -75,12 +85,17 @@ nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag, nsAutoString timeZoneID; BuildTimeZoneString(aExplodedTime->tm_params, timeZoneID); auto genResult = DateTimePatternGenerator::TryCreate(sLocale->get()); const bool spoofEnglish = aForDocument && nsContentUtils::ShouldResistFingerprinting( aForDocument, mozilla::RFPTarget::JSLocale); const nsCString& locale = spoofEnglish ? nsRFPService::GetSpoofedJSLocale() : *sLocale; auto genResult = DateTimePatternGenerator::TryCreate(locale.get()); NS_ENSURE_TRUE(genResult.isOk(), NS_ERROR_FAILURE); auto dateTimePatternGenerator = genResult.unwrap(); auto result = DateTimeFormat::TryCreateFromComponents( *sLocale, aBag, dateTimePatternGenerator.get(), locale, aBag, dateTimePatternGenerator.get(), Some(Span<const char16_t>(timeZoneID.Data(), timeZoneID.Length()))); NS_ENSURE_TRUE(result.isOk(), NS_ERROR_FAILURE); auto dateTimeFormat = result.unwrap(); Loading Loading
dom/html/input/DateTimeInputTypes.cpp +6 −8 Original line number Diff line number Diff line Loading @@ -109,21 +109,19 @@ bool DateTimeInputTypeBase::HasBadInput() const { return !allEmpty && IsValueEmpty(); } // Format PRExplodedTime according to current locale static bool FormatDateTime( bool DateTimeInputTypeBase::FormatDateTime( const PRExplodedTime& aTime, const intl::DateTimeFormat::ComponentsBag& aComponents, nsAString& aFormatted) { nsAString& aFormatted) const { // AppDateTimeFormat is not thread-safe. MOZ_ASSERT(NS_IsMainThread(), "Should only be called from main thread"); return NS_SUCCEEDED( intl::AppDateTimeFormat::Format(aComponents, &aTime, aFormatted)); return NS_SUCCEEDED(intl::AppDateTimeFormat::FormatForDocument( aComponents, &aTime, mInputElement->OwnerDoc(), aFormatted)); } // Format timestamp according to current locale static bool FormatDateTime( bool DateTimeInputTypeBase::FormatDateTime( double aValue, const intl::DateTimeFormat::ComponentsBag& aComponents, nsAString& aFormatted) { nsAString& aFormatted) const { PRExplodedTime exploded; PRTime time = static_cast<PRTime>(aValue * PR_USEC_PER_MSEC); PR_ExplodeTime( Loading
dom/html/input/DateTimeInputTypes.h +16 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,9 @@ #define mozilla_dom_DateTimeInputTypes_h_ #include "mozilla/dom/InputType.h" #include "mozilla/intl/DateTimeFormat.h" struct PRExplodedTime; namespace mozilla::dom { Loading Loading @@ -39,6 +42,19 @@ class DateTimeInputTypeBase : public InputType { bool GetTimeFromMs(double aValue, uint16_t* aHours, uint16_t* aMinutes, uint16_t* aSeconds, uint16_t* aMilliseconds) const; /** * Format PRExplodedTime according to current locale */ bool FormatDateTime(const PRExplodedTime& aTime, const intl::DateTimeFormat::ComponentsBag& aComponents, nsAString& aFormatted) const; /** * Format timestamp according to current locale */ bool FormatDateTime(double aValue, const intl::DateTimeFormat::ComponentsBag& aComponents, nsAString& aFormatted) const; // Minimum year limited by HTML standard, year >= 1. static const double kMinimumYear; // Maximum year limited by ECMAScript date object range, year <= 275760. Loading
dom/html/test/browser.toml +2 −0 Original line number Diff line number Diff line Loading @@ -51,3 +51,5 @@ support-files = [ "empty.html", "image_yellow.png", ] ["browser_validationmessage_spoof_english.js"]
dom/html/test/browser_validationmessage_spoof_english.js 0 → 100644 +86 −0 Original line number Diff line number Diff line /* Any copyright is dedicated to the Public Domain. https://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /* Test for bug 2040704 - datetime input validation messages should use en_US localization when English spoofing is enabled. */ const originalAvailableLocales = Services.locale.availableLocales; const originalRequestedLocales = Services.locale.requestedLocales; async function runTest(test) { for (let spoof of [false, true]) { await SpecialPowers.pushPrefEnv({ set: [ ["privacy.spoof_english", spoof ? 2 : 0], ["privacy.resistFingerprinting", spoof], ], }); let source = `<!DOCTYPE html> <input type="${test.type}" min="${test.min}" value="${test.value}">`; let result = await BrowserTestUtils.withNewTab( "data:text/html," + source, browser => { return SpecialPowers.spawn(browser, [], () => { return content.eval( 'document.querySelector("input").validationMessage' ); }); } ); let expectIncludes = test[spoof ? "en" : "de"]; let expectDoesNotInclude = test[spoof ? "de" : "en"]; ok( result.includes(expectIncludes), `With spoofing ${spoof ? "enabled" : "disabled"}: expect validationMessage ` + `to include "${expectIncludes}": "${result}"` ); ok( !result.includes(expectDoesNotInclude), `With spoofing ${spoof ? "enabled" : "disabled"}: expect validationMessage ` + `to not include "${expectDoesNotInclude}": "${result}"` ); } } const tests = [ { type: "date", min: "2000-01-01", value: "1999-01-01", en: "01/01/2000", de: "01.01.2000", }, { type: "time", min: "16:00", value: "15:00", en: "4:00 PM", de: "16:00", }, { type: "datetime-local", min: "2000-01-01T00:00", value: "1999-01-01T00:00", en: "01/01/2000", de: "01.01.2000", }, ]; add_task(() => { Services.locale.availableLocales = ["de-DE"]; Services.locale.requestedLocales = ["de-DE"]; }); for (let test of tests) { add_task(() => runTest(test)); } add_task(() => { // restore previous locales Services.locale.availableLocales = originalAvailableLocales; Services.locale.requestedLocales = originalRequestedLocales; });
intl/locale/AppDateTimeFormat.cpp +17 −2 Original line number Diff line number Diff line Loading @@ -10,6 +10,8 @@ #include "mozilla/intl/LocaleService.h" #include "OSPreferences.h" #include "mozIOSPreferences.h" #include "nsContentUtils.h" #include "nsRFPService.h" #ifdef DEBUG # include "nsThreadManager.h" #endif Loading Loading @@ -64,6 +66,14 @@ nsresult AppDateTimeFormat::Format(const DateTimeFormat::StyleBag& aStyle, nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag, const PRExplodedTime* aExplodedTime, nsAString& aStringOut) { return FormatForDocument(aBag, aExplodedTime, nullptr, aStringOut); } /*static*/ nsresult AppDateTimeFormat::FormatForDocument( const DateTimeFormat::ComponentsBag& aBag, const PRExplodedTime* aExplodedTime, const dom::Document* aForDocument, nsAString& aStringOut) { // set up locale data nsresult rv = Initialize(); if (NS_FAILED(rv)) { Loading @@ -75,12 +85,17 @@ nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag, nsAutoString timeZoneID; BuildTimeZoneString(aExplodedTime->tm_params, timeZoneID); auto genResult = DateTimePatternGenerator::TryCreate(sLocale->get()); const bool spoofEnglish = aForDocument && nsContentUtils::ShouldResistFingerprinting( aForDocument, mozilla::RFPTarget::JSLocale); const nsCString& locale = spoofEnglish ? nsRFPService::GetSpoofedJSLocale() : *sLocale; auto genResult = DateTimePatternGenerator::TryCreate(locale.get()); NS_ENSURE_TRUE(genResult.isOk(), NS_ERROR_FAILURE); auto dateTimePatternGenerator = genResult.unwrap(); auto result = DateTimeFormat::TryCreateFromComponents( *sLocale, aBag, dateTimePatternGenerator.get(), locale, aBag, dateTimePatternGenerator.get(), Some(Span<const char16_t>(timeZoneID.Data(), timeZoneID.Length()))); NS_ENSURE_TRUE(result.isOk(), NS_ERROR_FAILURE); auto dateTimeFormat = result.unwrap(); Loading