Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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/. */
6
7
#include "OSPreferences.h"
8
#include "mozilla/intl/LocaleService.h"
9
#include <Carbon/Carbon.h>
10
11
using namespace mozilla::intl;
12
13
static void LocaleChangedNotificationCallback(CFNotificationCenterRef center,
14
void* observer, CFStringRef name,
15
const void* object,
16
CFDictionaryRef userInfo) {
17
if (!::CFEqual(name, kCFLocaleCurrentLocaleDidChangeNotification)) {
18
return;
19
}
20
static_cast<OSPreferences*>(observer)->Refresh();
21
}
22
23
OSPreferences::OSPreferences() {
24
::CFNotificationCenterAddObserver(
25
::CFNotificationCenterGetLocalCenter(), this,
26
LocaleChangedNotificationCallback,
27
kCFLocaleCurrentLocaleDidChangeNotification, 0,
28
CFNotificationSuspensionBehaviorDeliverImmediately);
29
}
30
31
OSPreferences::~OSPreferences() {
32
::CFNotificationCenterRemoveObserver(
33
::CFNotificationCenterGetLocalCenter(), this,
34
kCTFontManagerRegisteredFontsChangedNotification, 0);
35
}
36
37
bool OSPreferences::ReadSystemLocales(nsTArray<nsCString>& aLocaleList) {
38
MOZ_ASSERT(aLocaleList.IsEmpty());
39
40
CFArrayRef langs = ::CFLocaleCopyPreferredLanguages();
41
for (CFIndex i = 0; i < ::CFArrayGetCount(langs); i++) {
42
CFStringRef lang = (CFStringRef)::CFArrayGetValueAtIndex(langs, i);
43
44
AutoTArray<UniChar, 32> buffer;
45
int size = ::CFStringGetLength(lang);
46
buffer.SetLength(size);
47
48
CFRange range = ::CFRangeMake(0, size);
49
::CFStringGetCharacters(lang, range, buffer.Elements());
50
51
// Convert the locale string to the format that Mozilla expects
52
NS_LossyConvertUTF16toASCII locale(
53
reinterpret_cast<const char16_t*>(buffer.Elements()), buffer.Length());
54
55
if (CanonicalizeLanguageTag(locale)) {
56
aLocaleList.AppendElement(locale);
57
}
58
}
59
60
::CFRelease(langs);
61
62
return !aLocaleList.IsEmpty();
63
}
64
65
bool OSPreferences::ReadRegionalPrefsLocales(nsTArray<nsCString>& aLocaleList) {
66
// For now we're just taking System Locales since we don't know of any better
67
// API for regional prefs.
68
return ReadSystemLocales(aLocaleList);
69
}
70
71
static CFDateFormatterStyle ToCFDateFormatterStyle(
72
OSPreferences::DateTimeFormatStyle aFormatStyle) {
73
switch (aFormatStyle) {
74
case OSPreferences::DateTimeFormatStyle::None:
75
return kCFDateFormatterNoStyle;
76
case OSPreferences::DateTimeFormatStyle::Short:
77
return kCFDateFormatterShortStyle;
78
case OSPreferences::DateTimeFormatStyle::Medium:
79
return kCFDateFormatterMediumStyle;
80
case OSPreferences::DateTimeFormatStyle::Long:
81
return kCFDateFormatterLongStyle;
82
case OSPreferences::DateTimeFormatStyle::Full:
83
return kCFDateFormatterFullStyle;
84
case OSPreferences::DateTimeFormatStyle::Invalid:
85
MOZ_ASSERT_UNREACHABLE("invalid time format");
86
return kCFDateFormatterNoStyle;
87
}
88
}
89
90
// Given an 8-bit Gecko string, create a corresponding CFLocale;
91
// if aLocale is empty, returns a copy of the system's current locale.
92
// May return null on failure.
93
// Follows Core Foundation's Create rule, so the caller is responsible to
94
// release the returned reference.
95
static CFLocaleRef CreateCFLocaleFor(const nsACString& aLocale) {
96
nsAutoCString reqLocale;
97
nsAutoCString systemLocale;
98
99
OSPreferences::GetInstance()->GetSystemLocale(systemLocale);
100
101
if (aLocale.IsEmpty()) {
102
LocaleService::GetInstance()->GetAppLocaleAsBCP47(reqLocale);
103
} else {
104
reqLocale.Assign(aLocale);
105
}
106
107
bool match = LocaleService::LanguagesMatch(reqLocale, systemLocale);
108
if (match) {
109
return ::CFLocaleCopyCurrent();
110
}
111
112
CFStringRef identifier = CFStringCreateWithBytesNoCopy(
113
kCFAllocatorDefault, (const uint8_t*)reqLocale.BeginReading(),
114
reqLocale.Length(), kCFStringEncodingASCII, false, kCFAllocatorNull);
115
if (!identifier) {
116
return nullptr;
117
}
118
CFLocaleRef locale = CFLocaleCreate(kCFAllocatorDefault, identifier);
119
CFRelease(identifier);
120
return locale;
121
}
122
123
/**
124
* Cocoa API maps nicely to our four styles of date/time.
125
*
126
* The only caveat is that Cocoa takes regional preferences modifications
127
* into account only when we pass an empty string as a locale.
128
*
129
* In all other cases it will return the default pattern for a given locale.
130
*/
131
bool OSPreferences::ReadDateTimePattern(DateTimeFormatStyle aDateStyle,
132
DateTimeFormatStyle aTimeStyle,
133
const nsACString& aLocale,
134
nsAString& aRetVal) {
135
CFLocaleRef locale = CreateCFLocaleFor(aLocale);
136
if (!locale) {
137
return false;
138
}
139
140
CFDateFormatterRef formatter = CFDateFormatterCreate(
141
kCFAllocatorDefault, locale, ToCFDateFormatterStyle(aDateStyle),
142
ToCFDateFormatterStyle(aTimeStyle));
143
if (!formatter) {
144
return false;
145
}
146
CFStringRef format = CFDateFormatterGetFormat(formatter);
147
CFRelease(locale);
148
149
CFRange range = CFRangeMake(0, CFStringGetLength(format));
150
aRetVal.SetLength(range.length);
151
CFStringGetCharacters(format, range,
152
reinterpret_cast<UniChar*>(aRetVal.BeginWriting()));
153
CFRelease(formatter);
154
155
return true;
156
}