Bug 1908670 - Home and newtab topic personalization needs region controls r=home...
[gecko.git] / intl / locale / mac / OSPreferences_mac.cpp
blobddd0a41e0374eb5caeb8614e058e4c60ead0ac56
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,
16 const void* object,
17 CFDictionaryRef userInfo) {
18 if (!::CFEqual(name, kCFLocaleCurrentLocaleDidChangeNotification)) {
19 return;
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);
55 ::CFRelease(langs);
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);
98 } else {
99 reqLocale.Assign(aLocale);
102 bool match = LocaleService::LanguagesMatch(reqLocale, systemLocale);
103 if (match) {
104 return ::CFLocaleCopyCurrent();
107 CFStringRef identifier = CFStringCreateWithBytesNoCopy(
108 kCFAllocatorDefault, (const uint8_t*)reqLocale.BeginReading(),
109 reqLocale.Length(), kCFStringEncodingASCII, false, kCFAllocatorNull);
110 if (!identifier) {
111 return nullptr;
113 CFLocaleRef locale = CFLocaleCreate(kCFAllocatorDefault, identifier);
114 CFRelease(identifier);
115 return locale;
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);
131 if (!locale) {
132 return false;
135 CFDateFormatterRef formatter = CFDateFormatterCreate(
136 kCFAllocatorDefault, locale, ToCFDateFormatterStyle(aDateStyle),
137 ToCFDateFormatterStyle(aTimeStyle));
138 if (!formatter) {
139 return false;
141 CFStringRef format = CFDateFormatterGetFormat(formatter);
142 CFRelease(locale);
144 CFRange range = CFRangeMake(0, CFStringGetLength(format));
145 nsAutoString str;
146 str.SetLength(range.length);
147 CFStringGetCharacters(format, range,
148 reinterpret_cast<UniChar*>(str.BeginWriting()));
149 CFRelease(formatter);
151 aRetVal = NS_ConvertUTF16toUTF8(str);
152 return true;
155 void OSPreferences::RemoveObservers() {
156 ::CFNotificationCenterRemoveObserver(
157 ::CFNotificationCenterGetLocalCenter(), this,
158 kCTFontManagerRegisteredFontsChangedNotification, 0);