OSPreferences_mac.cpp (5556B)
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 <CoreFoundation/CoreFoundation.h> 10 #include <CoreText/CoreText.h> 11 12 using namespace mozilla::intl; 13 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; 20 } 21 static_cast<OSPreferences*>(observer)->Refresh(); 22 } 23 24 OSPreferences::OSPreferences() { 25 ::CFNotificationCenterAddObserver( 26 ::CFNotificationCenterGetLocalCenter(), this, 27 LocaleChangedNotificationCallback, 28 kCFLocaleCurrentLocaleDidChangeNotification, 0, 29 CFNotificationSuspensionBehaviorDeliverImmediately); 30 } 31 32 bool OSPreferences::ReadSystemLocales(nsTArray<nsCString>& aLocaleList) { 33 MOZ_ASSERT(aLocaleList.IsEmpty()); 34 35 CFArrayRef langs = ::CFLocaleCopyPreferredLanguages(); 36 for (CFIndex i = 0; i < ::CFArrayGetCount(langs); i++) { 37 CFStringRef lang = (CFStringRef)::CFArrayGetValueAtIndex(langs, i); 38 39 AutoTArray<UniChar, 32> buffer; 40 int size = ::CFStringGetLength(lang); 41 buffer.SetLength(size); 42 43 CFRange range = ::CFRangeMake(0, size); 44 ::CFStringGetCharacters(lang, range, buffer.Elements()); 45 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()); 49 50 if (CanonicalizeLanguageTag(locale)) { 51 aLocaleList.AppendElement(locale); 52 } 53 } 54 55 ::CFRelease(langs); 56 57 return !aLocaleList.IsEmpty(); 58 } 59 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); 64 } 65 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; 82 } 83 } 84 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; 93 94 OSPreferences::GetInstance()->GetSystemLocale(systemLocale); 95 96 if (aLocale.IsEmpty()) { 97 LocaleService::GetInstance()->GetAppLocaleAsBCP47(reqLocale); 98 } else { 99 reqLocale.Assign(aLocale); 100 } 101 102 bool match = LocaleService::LanguagesMatch(reqLocale, systemLocale); 103 if (match) { 104 return ::CFLocaleCopyCurrent(); 105 } 106 107 CFStringRef identifier = CFStringCreateWithBytesNoCopy( 108 kCFAllocatorDefault, (const uint8_t*)reqLocale.BeginReading(), 109 reqLocale.Length(), kCFStringEncodingASCII, false, kCFAllocatorNull); 110 if (!identifier) { 111 return nullptr; 112 } 113 CFLocaleRef locale = CFLocaleCreate(kCFAllocatorDefault, identifier); 114 CFRelease(identifier); 115 return locale; 116 } 117 118 /** 119 * Cocoa API maps nicely to our four styles of date/time. 120 * 121 * The only caveat is that Cocoa takes regional preferences modifications 122 * into account only when we pass an empty string as a locale. 123 * 124 * In all other cases it will return the default pattern for a given locale. 125 */ 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; 133 } 134 135 CFDateFormatterRef formatter = CFDateFormatterCreate( 136 kCFAllocatorDefault, locale, ToCFDateFormatterStyle(aDateStyle), 137 ToCFDateFormatterStyle(aTimeStyle)); 138 if (!formatter) { 139 return false; 140 } 141 CFStringRef format = CFDateFormatterGetFormat(formatter); 142 CFRelease(locale); 143 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); 150 151 aRetVal = NS_ConvertUTF16toUTF8(str); 152 return true; 153 } 154 155 void OSPreferences::RemoveObservers() { 156 ::CFNotificationCenterRemoveObserver( 157 ::CFNotificationCenterGetLocalCenter(), this, 158 kCTFontManagerRegisteredFontsChangedNotification, 0); 159 }