Bug 1426907 - Add OSPreferences::OverrideDateTimePattern method; r=zbraniecki

This new method allows for the user to override the long and short date and
time patterns by prefs. If no overrides are set or it fails while processing
the prefs, it will fallback to the existing methods for determining the patterns.
Since the user may have only overriden one of date or time, it is necessary to
be able to look up the other separately and combine the results.

This adds a prefix callback that watches the new prefs and flushes the cache if
they change. Otherwise, the user would have to restart the browser to see the
results of changing a pref, and would make testing more difficult. Unregistering
this callback required changes to the destructor, which was previously defined
separately on each operating system. A new RemoveObservers method has been added
to handle OS specific cleanup.

Differential Revision: https://phabricator.services.mozilla.com/D94433
This commit is contained in:
Dan Minor 2020-10-27 20:41:58 +00:00
Родитель 686b723d0d
Коммит 429ae74074
7 изменённых файлов: 240 добавлений и 15 удалений

Просмотреть файл

@ -26,6 +26,11 @@ mozilla::StaticRefPtr<OSPreferences> OSPreferences::sInstance;
OSPreferences* OSPreferences::GetInstance() {
if (!sInstance) {
sInstance = new OSPreferences();
DebugOnly<nsresult> rv = Preferences::RegisterPrefixCallback(
PreferenceChanged, "intl.date_time.pattern_override");
MOZ_ASSERT(NS_SUCCEEDED(rv), "Adding observers failed.");
ClearOnShutdown(&sInstance);
}
return sInstance;
@ -44,6 +49,20 @@ void OSPreferences::Refresh() {
}
}
OSPreferences::~OSPreferences() {
Preferences::UnregisterPrefixCallback(PreferenceChanged,
"intl.date_time.pattern_override");
RemoveObservers();
}
/*static*/
void OSPreferences::PreferenceChanged(const char* aPrefName,
void* /* aClosure */) {
if (sInstance) {
sInstance->mPatternCache.Clear();
}
}
/**
* This method should be called by every method of OSPreferences that
* retrieves a locale id from external source.
@ -171,6 +190,126 @@ bool OSPreferences::GetDateTimeSkeletonForStyle(DateTimeFormatStyle aDateStyle,
return true;
}
/**
* This method checks for preferences that override the defaults
*/
bool OSPreferences::OverrideDateTimePattern(DateTimeFormatStyle aDateStyle,
DateTimeFormatStyle aTimeStyle,
const nsACString& aLocale,
nsACString& aRetVal) {
const auto PrefToMaybeString = [](const char* pref) -> Maybe<nsAutoCString> {
nsAutoCString value;
nsresult nr = Preferences::GetCString(pref, value);
if (NS_FAILED(nr) || value.IsEmpty()) {
return Nothing();
}
return Some(std::move(value));
};
Maybe<nsAutoCString> timeSkeleton;
switch (aTimeStyle) {
case DateTimeFormatStyle::Short:
timeSkeleton =
PrefToMaybeString("intl.date_time.pattern_override.time_short");
break;
case DateTimeFormatStyle::Medium:
timeSkeleton =
PrefToMaybeString("intl.date_time.pattern_override.time_medium");
break;
case DateTimeFormatStyle::Long:
timeSkeleton =
PrefToMaybeString("intl.date_time.pattern_override.time_long");
break;
case DateTimeFormatStyle::Full:
timeSkeleton =
PrefToMaybeString("intl.date_time.pattern_override.time_full");
break;
default:
break;
}
Maybe<nsAutoCString> dateSkeleton;
switch (aDateStyle) {
case DateTimeFormatStyle::Short:
dateSkeleton =
PrefToMaybeString("intl.date_time.pattern_override.date_short");
break;
case DateTimeFormatStyle::Medium:
dateSkeleton =
PrefToMaybeString("intl.date_time.pattern_override.date_medium");
break;
case DateTimeFormatStyle::Long:
dateSkeleton =
PrefToMaybeString("intl.date_time.pattern_override.date_long");
break;
case DateTimeFormatStyle::Full:
dateSkeleton =
PrefToMaybeString("intl.date_time.pattern_override.date_full");
break;
default:
break;
}
nsAutoCString locale;
if (aLocale.IsEmpty()) {
AutoTArray<nsCString, 10> regionalPrefsLocales;
LocaleService::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales);
locale.Assign(regionalPrefsLocales[0]);
} else {
locale.Assign(aLocale);
}
const auto FillConnectorPattern = [&locale](
const nsAutoCString& datePattern,
const nsAutoCString& timePattern) {
nsAutoCString pattern;
GetDateTimeConnectorPattern(nsDependentCString(locale.get()), pattern);
int32_t index = pattern.Find("{1}");
if (index != kNotFound) {
pattern.Replace(index, 3, datePattern);
}
index = pattern.Find("{0}");
if (index != kNotFound) {
pattern.Replace(index, 3, timePattern);
}
return pattern;
};
if (timeSkeleton && dateSkeleton) {
aRetVal.Assign(FillConnectorPattern(*dateSkeleton, *timeSkeleton));
} else if (timeSkeleton) {
if (aDateStyle != DateTimeFormatStyle::None) {
nsAutoCString pattern;
if (!ReadDateTimePattern(aDateStyle, DateTimeFormatStyle::None, aLocale,
pattern) &&
!GetDateTimePatternForStyle(aDateStyle, DateTimeFormatStyle::None,
aLocale, pattern)) {
return false;
}
aRetVal.Assign(FillConnectorPattern(pattern, *timeSkeleton));
} else {
aRetVal.Assign(*timeSkeleton);
}
} else if (dateSkeleton) {
if (aTimeStyle != DateTimeFormatStyle::None) {
nsAutoCString pattern;
if (!ReadDateTimePattern(DateTimeFormatStyle::None, aTimeStyle, aLocale,
pattern) &&
!GetDateTimePatternForStyle(DateTimeFormatStyle::None, aTimeStyle,
aLocale, pattern)) {
return false;
}
aRetVal.Assign(FillConnectorPattern(*dateSkeleton, pattern));
} else {
aRetVal.Assign(*dateSkeleton);
}
} else {
return false;
}
return true;
}
/**
* This function is a counterpart to GetDateTimeSkeletonForStyle.
*
@ -347,9 +486,11 @@ OSPreferences::GetDateTimePattern(int32_t aDateFormatStyle,
return NS_OK;
}
if (!ReadDateTimePattern(dateStyle, timeStyle, aLocale, pattern)) {
if (!GetDateTimePatternForStyle(dateStyle, timeStyle, aLocale, pattern)) {
return NS_ERROR_FAILURE;
if (!OverrideDateTimePattern(dateStyle, timeStyle, aLocale, pattern)) {
if (!ReadDateTimePattern(dateStyle, timeStyle, aLocale, pattern)) {
if (!GetDateTimePatternForStyle(dateStyle, timeStyle, aLocale, pattern)) {
return NS_ERROR_FAILURE;
}
}
}

Просмотреть файл

@ -125,6 +125,10 @@ class OSPreferences : public mozIOSPreferences {
const nsACString& aLocale,
nsACString& aRetVal);
bool OverrideDateTimePattern(DateTimeFormatStyle aDateStyle,
DateTimeFormatStyle aTimeStyle,
const nsACString& aLocale, nsACString& aRetVal);
/**
* This is a host environment specific method that will be implemented
* separately for each platform.
@ -154,6 +158,18 @@ class OSPreferences : public mozIOSPreferences {
bool ReadDateTimePattern(DateTimeFormatStyle aDateFormatStyle,
DateTimeFormatStyle aTimeFormatStyle,
const nsACString& aLocale, nsACString& aRetVal);
/**
* This is called by the destructor to clean up any OS specific observers
* that are registered.
*/
void RemoveObservers();
/**
* This is called by the destructor to clean up any OS specific observers
* that are registered.
*/
static void PreferenceChanged(const char* aPrefName, void* /* aClosure */);
};
} // namespace intl

Просмотреть файл

@ -13,8 +13,6 @@ using namespace mozilla::intl;
OSPreferences::OSPreferences() {}
OSPreferences::~OSPreferences() {}
bool OSPreferences::ReadSystemLocales(nsTArray<nsCString>& aLocaleList) {
if (!mozilla::jni::IsAvailable()) {
return false;
@ -46,3 +44,5 @@ bool OSPreferences::ReadDateTimePattern(DateTimeFormatStyle aDateStyle,
nsACString& aRetVal) {
return false;
}
void OSPreferences::RemoveObservers() {}

Просмотреть файл

@ -16,8 +16,6 @@ using namespace mozilla::intl;
OSPreferences::OSPreferences() = default;
OSPreferences::~OSPreferences() = default;
bool OSPreferences::ReadSystemLocales(nsTArray<nsCString>& aLocaleList) {
MOZ_ASSERT(aLocaleList.IsEmpty());
@ -198,3 +196,5 @@ bool OSPreferences::ReadDateTimePattern(DateTimeFormatStyle aDateStyle,
return true;
}
void OSPreferences::RemoveObservers() {}

Просмотреть файл

@ -28,12 +28,6 @@ OSPreferences::OSPreferences() {
CFNotificationSuspensionBehaviorDeliverImmediately);
}
OSPreferences::~OSPreferences() {
::CFNotificationCenterRemoveObserver(
::CFNotificationCenterGetLocalCenter(), this,
kCTFontManagerRegisteredFontsChangedNotification, 0);
}
bool OSPreferences::ReadSystemLocales(nsTArray<nsCString>& aLocaleList) {
MOZ_ASSERT(aLocaleList.IsEmpty());
@ -156,3 +150,9 @@ bool OSPreferences::ReadDateTimePattern(DateTimeFormatStyle aDateStyle,
aRetVal = NS_ConvertUTF16toUTF8(str);
return true;
}
void OSPreferences::RemoveObservers() {
::CFNotificationCenterRemoveObserver(
::CFNotificationCenterGetLocalCenter(), this,
kCTFontManagerRegisteredFontsChangedNotification, 0);
}

Просмотреть файл

@ -5,6 +5,7 @@
#include "gtest/gtest.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/intl/OSPreferences.h"
using namespace mozilla::intl;
@ -78,3 +79,70 @@ TEST(Intl_Locale_OSPreferences, GetDateTimePattern)
ASSERT_TRUE(1);
}
/**
* Test that is possible to override the OS defaults through a pref.
*/
TEST(Intl_Locale_OSPreferences, GetDateTimePatternPrefOverrides)
{
nsresult nr;
nsAutoCString default_pattern, pattern;
OSPreferences* osprefs = OSPreferences::GetInstance();
struct {
const char* DatePref;
const char* TimePref;
int32_t DateTimeFormatStyle;
} configs[] = {{"intl.date_time.pattern_override.date_short",
"intl.date_time.pattern_override.time_short",
mozIOSPreferences::dateTimeFormatStyleShort},
{"intl.date_time.pattern_override.date_medium",
"intl.date_time.pattern_override.time_medium",
mozIOSPreferences::dateTimeFormatStyleMedium},
{"intl.date_time.pattern_override.date_long",
"intl.date_time.pattern_override.time_long",
mozIOSPreferences::dateTimeFormatStyleLong},
{"intl.date_time.pattern_override.date_full",
"intl.date_time.pattern_override.time_full",
mozIOSPreferences::dateTimeFormatStyleFull}};
for (const auto& config : configs) {
// Get default value for the OS
nr = osprefs->GetDateTimePattern(config.DateTimeFormatStyle,
mozIOSPreferences::dateTimeFormatStyleNone,
nsDependentCString(""), default_pattern);
ASSERT_TRUE(NS_SUCCEEDED(nr));
// Override date format
mozilla::Preferences::SetCString(config.DatePref, "yy-MM");
nr = osprefs->GetDateTimePattern(config.DateTimeFormatStyle,
mozIOSPreferences::dateTimeFormatStyleNone,
nsDependentCString(""), pattern);
ASSERT_TRUE(NS_SUCCEEDED(nr));
ASSERT_TRUE(pattern.EqualsASCII("yy-MM"));
// Override time format
mozilla::Preferences::SetCString(config.TimePref, "HH:mm");
nr = osprefs->GetDateTimePattern(mozIOSPreferences::dateTimeFormatStyleNone,
config.DateTimeFormatStyle,
nsDependentCString(""), pattern);
ASSERT_TRUE(NS_SUCCEEDED(nr));
ASSERT_TRUE(pattern.EqualsASCII("HH:mm"));
// Override both
nr = osprefs->GetDateTimePattern(config.DateTimeFormatStyle,
config.DateTimeFormatStyle,
nsDependentCString(""), pattern);
ASSERT_TRUE(NS_SUCCEEDED(nr));
ASSERT_TRUE(pattern.EqualsASCII("yy-MM, HH:mm"));
// Clear overrides, we should get the default value back.
mozilla::Preferences::SetCString(config.DatePref, "");
mozilla::Preferences::SetCString(config.TimePref, "");
nr = osprefs->GetDateTimePattern(config.DateTimeFormatStyle,
mozIOSPreferences::dateTimeFormatStyleNone,
nsDependentCString(""), pattern);
ASSERT_TRUE(NS_SUCCEEDED(nr));
ASSERT_EQ(default_pattern, pattern);
}
}

Просмотреть файл

@ -27,8 +27,6 @@ using namespace mozilla::intl;
OSPreferences::OSPreferences() {}
OSPreferences::~OSPreferences() {}
bool OSPreferences::ReadSystemLocales(nsTArray<nsCString>& aLocaleList) {
MOZ_ASSERT(aLocaleList.IsEmpty());
@ -330,3 +328,5 @@ bool OSPreferences::ReadDateTimePattern(DateTimeFormatStyle aDateStyle,
return true;
}
void OSPreferences::RemoveObservers() {}