1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "OSPreferences.h"
8 #include "mozilla/intl/LocaleService.h"
9 #include <CoreFoundation/CoreFoundation.h>
10 #include <CoreText/CoreText.h>
12 using namespace mozilla::intl
;
14 static void LocaleChangedNotificationCallback(CFNotificationCenterRef center
,
15 void* observer
, CFStringRef name
,
17 CFDictionaryRef userInfo
) {
18 if (!::CFEqual(name
, kCFLocaleCurrentLocaleDidChangeNotification
)) {
21 static_cast<OSPreferences
*>(observer
)->Refresh();
24 OSPreferences::OSPreferences() {
25 ::CFNotificationCenterAddObserver(
26 ::CFNotificationCenterGetLocalCenter(), this,
27 LocaleChangedNotificationCallback
,
28 kCFLocaleCurrentLocaleDidChangeNotification
, 0,
29 CFNotificationSuspensionBehaviorDeliverImmediately
);
32 bool OSPreferences::ReadSystemLocales(nsTArray
<nsCString
>& aLocaleList
) {
33 MOZ_ASSERT(aLocaleList
.IsEmpty());
35 CFArrayRef langs
= ::CFLocaleCopyPreferredLanguages();
36 for (CFIndex i
= 0; i
< ::CFArrayGetCount(langs
); i
++) {
37 CFStringRef lang
= (CFStringRef
)::CFArrayGetValueAtIndex(langs
, i
);
39 AutoTArray
<UniChar
, 32> buffer
;
40 int size
= ::CFStringGetLength(lang
);
41 buffer
.SetLength(size
);
43 CFRange range
= ::CFRangeMake(0, size
);
44 ::CFStringGetCharacters(lang
, range
, buffer
.Elements());
46 // Convert the locale string to the format that Mozilla expects
47 NS_LossyConvertUTF16toASCII
locale(
48 reinterpret_cast<const char16_t
*>(buffer
.Elements()), buffer
.Length());
50 if (CanonicalizeLanguageTag(locale
)) {
51 aLocaleList
.AppendElement(locale
);
57 return !aLocaleList
.IsEmpty();
60 bool OSPreferences::ReadRegionalPrefsLocales(nsTArray
<nsCString
>& aLocaleList
) {
61 // For now we're just taking System Locales since we don't know of any better
62 // API for regional prefs.
63 return ReadSystemLocales(aLocaleList
);
66 static CFDateFormatterStyle
ToCFDateFormatterStyle(
67 OSPreferences::DateTimeFormatStyle aFormatStyle
) {
68 switch (aFormatStyle
) {
69 case OSPreferences::DateTimeFormatStyle::None
:
70 return kCFDateFormatterNoStyle
;
71 case OSPreferences::DateTimeFormatStyle::Short
:
72 return kCFDateFormatterShortStyle
;
73 case OSPreferences::DateTimeFormatStyle::Medium
:
74 return kCFDateFormatterMediumStyle
;
75 case OSPreferences::DateTimeFormatStyle::Long
:
76 return kCFDateFormatterLongStyle
;
77 case OSPreferences::DateTimeFormatStyle::Full
:
78 return kCFDateFormatterFullStyle
;
79 case OSPreferences::DateTimeFormatStyle::Invalid
:
80 MOZ_ASSERT_UNREACHABLE("invalid time format");
81 return kCFDateFormatterNoStyle
;
85 // Given an 8-bit Gecko string, create a corresponding CFLocale;
86 // if aLocale is empty, returns a copy of the system's current locale.
87 // May return null on failure.
88 // Follows Core Foundation's Create rule, so the caller is responsible to
89 // release the returned reference.
90 static CFLocaleRef
CreateCFLocaleFor(const nsACString
& aLocale
) {
91 nsAutoCString reqLocale
;
92 nsAutoCString systemLocale
;
94 OSPreferences::GetInstance()->GetSystemLocale(systemLocale
);
96 if (aLocale
.IsEmpty()) {
97 LocaleService::GetInstance()->GetAppLocaleAsBCP47(reqLocale
);
99 reqLocale
.Assign(aLocale
);
102 bool match
= LocaleService::LanguagesMatch(reqLocale
, systemLocale
);
104 return ::CFLocaleCopyCurrent();
107 CFStringRef identifier
= CFStringCreateWithBytesNoCopy(
108 kCFAllocatorDefault
, (const uint8_t*)reqLocale
.BeginReading(),
109 reqLocale
.Length(), kCFStringEncodingASCII
, false, kCFAllocatorNull
);
113 CFLocaleRef locale
= CFLocaleCreate(kCFAllocatorDefault
, identifier
);
114 CFRelease(identifier
);
119 * Cocoa API maps nicely to our four styles of date/time.
121 * The only caveat is that Cocoa takes regional preferences modifications
122 * into account only when we pass an empty string as a locale.
124 * In all other cases it will return the default pattern for a given locale.
126 bool OSPreferences::ReadDateTimePattern(DateTimeFormatStyle aDateStyle
,
127 DateTimeFormatStyle aTimeStyle
,
128 const nsACString
& aLocale
,
129 nsACString
& aRetVal
) {
130 CFLocaleRef locale
= CreateCFLocaleFor(aLocale
);
135 CFDateFormatterRef formatter
= CFDateFormatterCreate(
136 kCFAllocatorDefault
, locale
, ToCFDateFormatterStyle(aDateStyle
),
137 ToCFDateFormatterStyle(aTimeStyle
));
141 CFStringRef format
= CFDateFormatterGetFormat(formatter
);
144 CFRange range
= CFRangeMake(0, CFStringGetLength(format
));
146 str
.SetLength(range
.length
);
147 CFStringGetCharacters(format
, range
,
148 reinterpret_cast<UniChar
*>(str
.BeginWriting()));
149 CFRelease(formatter
);
151 aRetVal
= NS_ConvertUTF16toUTF8(str
);
155 void OSPreferences::RemoveObservers() {
156 ::CFNotificationCenterRemoveObserver(
157 ::CFNotificationCenterGetLocalCenter(), this,
158 kCTFontManagerRegisteredFontsChangedNotification
, 0);